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

时间:2013-10-22 21:14:09

标签: c# c interop function-pointers

我需要在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代码或使用GetFunctionAddress动态加载的情况下使C库指针指向C#代码?我认为这可能是不可能的,但似乎可能是这样。

3 个答案:

答案 0 :(得分:3)

我不能100%确定这是否是您正在寻找的。

在我们的例子中,我们有一个第三方C Sdk,它为回调消息提供了几个钩子。回调调用在C代码中启动,并期望调用C函数,但我们可以使用以下步骤将其挂钩到C#方法。 (这段代码有点陈旧,可能有更多的succint和现代方法在C#的新迭代中完成相同的操作。)

首先,我们在C#类中声明一个 Callback委托,与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有一个C PlayM4_SetDecCallBack()函数,它希望函数指针在调用回调时调用 - 我们可以安全地传递我们类的委托成员。

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

我希望这有用。

答案 1 :(得分:1)

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

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

当前代码尝试执行的操作称为“hack”,如果您想要实现类似的功能而不需要任何黑客攻击:

  1. 在C代码中提供将接受执行函数指针的函数
  2. 在调用ptr_R_ShowMessage的C代码中,使用来自p1
  3. 的指针
  4. 在P1的C#代码调用函数中,提供调用参数的方法
  5. 以下是我发现的从外部库执行函数并提供C#函数作为回调的示例:http://tutorials.csharp-online.net/CSharp_Delegates_and_Events%E2%80%94Win32_callbacks

答案 2 :(得分:-2)

我确认这在C#中是不可能的,因此需要与libary加载功能连接。感谢两者的答案,绝对有用。