将 C 函数指针设置为 C# 函数

本文关键字:函数 设置 指针 | 更新日期: 2023-09-27 18:36:38

我需要在 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# 代码? 我认为这可能是不可能的,但似乎有可能。

将 C 函数指针设置为 C# 函数

我不是 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# 类中的某个地方,我们需要初始化委托成员并挂接我们的回调处理程序(在本例中它是静态方法HandleDecData()

_decodeCallBack += new DecCallBack(HandleDecData);

最后,我们将委托成员传递给 C,就好像它是指向 C 的函数指针一样。在我们的例子中,第三方 SDK 有一个 C PlayM4_SetDecCallBack()函数,它期望函数指针在调用回调时调用 - 我们可以安全地传递我们类的委托成员。

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

我希望这是有帮助的。

C

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

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

当前代码试图做的事情被称为"hack",如果你想在没有hacks的情况下实现类似的功能,你需要:

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

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

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