将C功能指针设置为C#功能

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)

我希望这是有帮助的。


evo*_*obe -3

我确认这在 C# 中是不可能直接实现的,因此需要与库加载函数进行交互。不过,感谢两位的回答,绝对有用。