如何从 C# 调用具有 void* 回调和对象参数的 C++ Dll 中的函数
How do I call a function in a C++ Dll from C# that has void* callback and object parameter
我正在尝试创建一个 C dll 的包装器,我正在尝试调用一个函数,该函数具有回调函数,接收一个对象作为传回的指针。
.h 文件德拉雷斯
extern int SetErrorHandler(void (*handler) (int, const char*, void*),
void *data_ptr);
处理程序是一个回调函数,在发生错误时调用,data_ptr是传回给你的任何对象(状态),在我的应用程序中,它只是这个(当前对象)。
我能够在使用编组常量类型(如简单类型字符串、整数等)的 dll 中调用函数。但是我不知道如何只封送指向作为状态的 C# 对象的指针。
为了将对象引用从我通过搜索找到的内容传递给 C 函数,否则似乎我需要一个结构类型才能编组到函数,所以我创建了一个结构来保存我的对象:
[StructLayout(LayoutKind.Sequential)]
struct MyObjectState
{
public object state;
}
编辑:我试图在public object state
属性上放置一个属性:[MarshalAs(UnmanagedType.Struct, SizeConst = 4)]
,但这会产生相同的错误,所以我删除了它,似乎无论如何它都不起作用。
该结构包含一个对象属性,用于保存回调函数的任何对象。
我在 C# 中声明了委托,如下所示:
delegate void ErrorHandler(int errorCode, IntPtr name, IntPtr data);
然后我在 C# 中声明了导入函数,如下所示:
[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler handler, IntPtr data);
然后我在 C# 代码中创建了一个回调函数:
void MyErrorHandler(int errorCode, IntPtr name, IntPtr data)
{
var strName = Marshal.PtrToStringAnsi(name);
var state = new MyObjectState();
Marshal.PtrToStructure(data, state);
Console.WriteLine(strName);
}
我可以按如下方式调用库函数:
var state = new MyObjectState()
{
state = this
};
IntPtr pStruct = Marshal.AllocHGlobal(Marshal.SizeOf(state));
Marshal.StructureToPtr(state, pStruct, true);
int ret = SetErrorHandler(MyErrorHandler, pStruct);
调用有效,调用回调函数,但我无法访问回调函数中的数据,当我尝试Marshal.PtrToStructure
时出现错误:
结构不能是值类。
我在这里做了很多搜索,在马歇尔和 void* 上找到了各种东西,但没有什么能帮助我让它发挥作用
谢谢。
你正在使它比它需要的更复杂。C# 客户端不需要使用 data_ptr
参数,因为 C# 委托已具有用于维护this
指针的内置机制。
因此,您只需将IntPtr.Zero
传递给代理即可。在错误处理程序委托中,您只需忽略data_ptr
的值,因为this
将可用。
如果您不遵循此描述,这里有一个简短的程序来说明我的意思。请注意,MyErrorHandler
是充当错误处理程序并且可以修改实例数据的实例方法。
class Wrapper
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void ErrorHandler(int errorCode, string name, IntPtr data);
[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler(ErrorHandler handler, IntPtr data);
void MyErrorHandler(int errorCode, string name, IntPtr data)
{
lastError = errorCode;
lastErrorName = name;
}
public Wrapper()
{
SetErrorHandler(MyErrorHandler, IntPtr.Zero);
}
public int lastError { get; set; }
public string lastErrorName { get; set; }
}
class Program
{
static void Main(string[] args)
{
Wrapper wrapper = new Wrapper();
}
}
很可能有一种方法可以做到这一点,但我很久以前就放弃了。我想出的解决方案有点黑客化,但它非常有效,并且可以与我抛出的所有内容一起使用:
C# -> 托管C++ -> 本机调用
这样做,你最终会在托管C++中编写一个小包装器,这有点痛苦,但我发现它比所有这些封送代码更强大,更不痛苦。
老实说,虽然我有点希望有人给出一个不回避的答案,但我自己已经为此挣扎了很长一段时间。
- 如何使用Visual Studio 2017在C++中为参数化对象数组使用唯一指针
- Arduino C++在构造函数中用参数声明对象数组
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- C++ 类对象作为函数参数
- 如何将对象数组作为参数传递给模板
- 使用向量初始化参数化构造函数的对象数组
- 如何将成员函数作为参数传递并在派生对象上执行方法列表
- 如何编写将展开以定义具有模板参数的对象的宏
- C++类对象 - 遍历基于参数的对象
- 将指针传递到成员的指针,从模板参数包到函数对象
- 如何将子类作为函数的参数传递给期望基类,然后将该对象传递到指向这些抽象类对象的指针向量中?
- 是否可以获取成员函数模板参数的拥有对象?
- C++多参数对象创建
- 在不创建参数对象的情况下解析constexpr函数
- 推断模板参数对象成员的类型
- 如何解析c++ v8中的参数对象
- 当默认参数对象被销毁时
- 为什么函数不能更改其参数对象的地址/引用值?