5

我需要在 C 库中设置一个与 C# 函数相等的回调函数,并且在没有 dlopen 或 kernel32(这似乎是 windows/unix 特定的)的情况下无法找到一种方法。有谁知道这样做的方法?

问题:C 共享库暴露了函数指针,其值应该通过覆盖它们来设置。例如

//C Code
extern void (*ptr_R_ShowMessage) (const char *)

当前的 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);

对 libdl 和 kernel32.dll 的要求会导致各种问题......理想情况下会避免。

有谁知道如何使 C 库指针指向 C# 代码,而无需修改 C 代码或使用 GetFunctionAddress 动态加载?我认为这可能是不可能的,但它似乎是可能的。

4

3 回答 3

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);

这是匹配的 C# 委托定义。请注意,“魔术”是由于以下UnmanagedFunctionPointer属性而发生的:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void DecCallBack(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2);

一旦我们有了这个,我们声明一个类型的类成员DecCallBack ,它将在我们的类中保存一个委托实例。

static DecCallBack _decodeCallBack = null;

接下来我们编写一个与委托签名兼容的处理程序方法。在这种情况下,我们将其命名为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

}       

接下来,在 C# 类的某个地方,我们需要初始化委托成员并挂钩我们的回调 Handler(在这种情况下它是静态方法HandleDecData()

_decodeCallBack += new DecCallBack(HandleDecData);

最后,我们将委托成员作为指向 C 的函数指针传递。在我们的例子中,第三方 SDK 有一个 CPlayM4_SetDecCallBack()函数,该函数期望在调用回调时调用函数指针 - 我们可以安全地传递而是我们的班级。

 if( !PlayM4_SetDecCallBack(channel, _decodeCallBack) ) 
  throw new InvalidOperationException("Error setting DecCallBack");

我希望这是有帮助的。

于 2013-10-22T21:46:28.717 回答
1

C 共享库不以这种方式公开函数指针,extern 意味着该函数是在另一个 C 文件中定义的,并且在创建二进制对象期间链接器将在最终可执行文件中使用函数中的实际代码

要解决此问题,您需要以某种方式在 C 项目中包含 C# 文件,以便 C 编译器将两者链接到二进制对象中,但这是不可能的

当前代码尝试执行的操作称为“hack”,如果您想在没有 hack 的情况下实现类似的功能,则需要:

  1. 在 C 代码中提供函数,该函数将接受指向要执行的函数的指针
  2. 在调用 ptr_R_ShowMessage 的 C 代码中,使用来自 p1 的指针
  3. 在 C# 代码中从 P1 调用函数并提供作为参数调用的方法

这是我发现的示例,它从外部库执行函数并将其提供为 C# 函数作为回调:http ://tutorials.csharp-online.net/CSharp_Delegates_and_Events%E2%80%94Win32_callbacks

于 2013-10-22T21:17:26.577 回答
-2

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

于 2013-10-30T17:09:29.240 回答