void funct(void*(*handler)(void*));
是一个接受指针并返回指针的函数。
在 C# 中是:
IntPtr MyFunc(IntPtr ptr);
所以你需要一个像这样的代表:
public IntPtr delegate MessageHandlerDelegate(IntPtr ptr);
[DllImport("mydll.dll")]
public static extern void register_message_handler(MessageHandlerDelegate del);
请注意,P/Invoke(调用本机方法)是 Action<...> 和 Func<...> 委托不起作用的罕见情况之一,您必须构建“特定”委托。
BUT 来调用它,它有点复杂,因为你必须保存委托的“副本”,这样如果 C 函数在任何时候调用这个方法,这个副本仍然是“活动的” " 并且还没有被 GC(例如参见 https://stackoverflow.com/a/5465074/613130)。最常见的做法是将所有内容封装在一个类中,并将副本放在该类的属性/字段中:
class MyClass
{
public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);
[DllImport("mydll.dll")]
public static extern void register_message_handler(MessageHandlerDelegate del);
[DllImport("mydll.dll")]
public static extern IntPtr message_handler(IntPtr message);
public MessageHandlerDelegate Del { get; set; }
public void Register()
{
// Make a copy of the delegate
Del = Handler;
register_message_handler(Del);
}
public IntPtr Handler(IntPtr ptr)
{
// I don't know what ptr is
Console.WriteLine("Handled");
return IntPtr.Zero; // Return something sensible
}
}
请注意,如果您使用IntPtr,则不需要unsafe。
如果你想将message_handler传递给register_message_handler,最安全的方法是
// Make a copy of the delegate
Del = message_handler;
register_message_handler(Del);
有可能你可以直接做 不,没有,检查
罢工>
register_message_handler(message_handler);
CLR 会解决这个问题,但我不确定……我不能轻易测试它,我也不会这样做。 (如果你想测试它,在register_message_handler 之后添加一个GC.Collect()。如果一段时间后你收到CallbackOnCollectedDelegate 错误,那么你知道你不能这样做:-))
嗯...检查。 你不能做原始的register_message_handler(message_handler),你必须使用MyClass。
非常意识到一些事情:最好总是在函数指针中指定调用约定 C 端 和 C# 端 even。 C# 使用 stdcall,而 C 使用 cdecl。在 x86 模式下,您可能会遇到非常可怕的静默崩溃(在 x64 中只有一个调用约定)
void __stdcall register_message_handler(void* (__stdcall *handler)(void*));
void * __stdcall message_handler(void *raw_message);
和C#方面
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void register_message_handler(MessageHandlerDelegate del);
[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr message_handler(IntPtr message);
(或无处不在cdecl)