evo*_*obe 5 c c# interop function-pointers
我需要在C库中设置一个等于C#函数的回调函数,并且如果没有dlopen或kernel32(这似乎是windows/unix特定的),就无法找到一种方法.有谁知道这样做的方法?
问题:C共享库暴露了函数指针,其值应该通过覆盖它们来设置.例如
//C Code
extern void (*ptr_R_ShowMessage) (const char *)
Run Code Online (Sandbox Code Playgroud)
当前的c#代码创建一个与该签名匹配的函数的委托,使用marshal类获取指向该委托的指针,然后用该值覆盖C指针.
//call to libdl or kernel32.dll
IntPtr showPointer = GetFunctionAddress(hndl,"ptr_R_ShowMessage");
IntPtr newShowPointer = Marshal.GetFunctionPointerForDelegate(matchingDelegate);
Marshal.WriteIntPtr(showPointer, newShowPointer);
Run Code Online (Sandbox Code Playgroud)
对libdl和kernel32.dll的要求会导致各种问题...理想情况下可以避免.
有没有人知道如何使C库指针指向C#代码,而无需修改C代码或使用GetFunctionAddress动态加载?我认为这可能是不可能的,但似乎可能是这样.
小智 5
我不是 100% 确定这是否是您要找的。
在我们的例子中,我们有一个第三方 C Sdk,它为回调消息提供了几个钩子。回调调用在 C 代码中启动并期望调用 C 函数,但我们可以使用以下步骤将其挂接到 C# 方法。(这段代码有点旧,可能有更简洁和现代的方法可以在 C# 的较新迭代中完成相同的操作)。
首先,我们在 C# 类中声明一个回调委托,与 SDK 期望的 C 函数指针的签名兼容。
例如,如果 C 代码期望回调具有以下签名的函数:
void DecCallBack(int nPort, void* pBuf, int nSize, FRAME_INFO *pInfo, int nReserved1, int reserved 2);
Run Code Online (Sandbox Code Playgroud)
这是匹配的 C# 委托定义。请注意,“魔术”的发生归功于以下UnmanagedFunctionPointer属性:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void DecCallBack(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2);
Run Code Online (Sandbox Code Playgroud)
一旦我们有了这个,我们就声明了一个类型的类成员DecCallBack ,它将在我们的类中保存一个委托实例。
static DecCallBack _decodeCallBack = null;
Run Code Online (Sandbox Code Playgroud)
接下来我们编写一个与委托签名兼容的处理程序方法。在这种情况下,我们将其命名为HandleDecData()
private static void HandleDecData(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2) {
// Here we handle the callback, this code is being called from an external C library
}
Run Code Online (Sandbox Code Playgroud)
接下来,在 C# 类的某个地方,我们需要初始化委托成员并挂钩我们的回调处理程序(在这种情况下它是静态方法 HandleDecData()
_decodeCallBack += new DecCallBack(HandleDecData);
Run Code Online (Sandbox Code Playgroud)
最后,我们将委托成员作为 C 的函数指针传递。在我们的例子中,第三方 SDK 有一个 CPlayM4_SetDecCallBack()函数,它期望在调用回调时调用函数指针——我们可以安全地传递我们班代替。
if( !PlayM4_SetDecCallBack(channel, _decodeCallBack) )
throw new InvalidOperationException("Error setting DecCallBack");
Run Code Online (Sandbox Code Playgroud)
我希望这是有帮助的。