如何将委托或函数指针从 C# 传递到 C++ 并使用 InternalCall 调用它

Posted

技术标签:

【中文标题】如何将委托或函数指针从 C# 传递到 C++ 并使用 InternalCall 调用它【英文标题】:How to pass a delegate or function pointer from C# to C++ and call it there using InternalCall 【发布时间】:2017-02-08 23:59:51 【问题描述】:

我在 C# 中有以下设置:

public delegate void CallbackDelegate(string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void setCallback(CallbackDelegate aCallback);

public void testCallbacks()

    System.Console.Write("Registering C# callback...\n");
    setCallback(callback01);


public void callback01(string message)

    System.Console.Write("callback 01 called: " + message + "\n");

这在 C++ 中(该函数通过 mono_add_internal_call 正确注册):

typedef void (*CallbackFunction)(const char*);
void setCallback(MonoDelegate* delegate)

    // How to convert the MonoDelegate to a proper function pointer?
    // So that I can call it like func("test");

调用 C++ 函数并将 something 传递给委托变量。 但是现在呢?

我环顾四周,发现函数“mono_delegate_to_ftnptr”被提到了几次,从这些例子来看,它似乎正是我所需要的。 然而,这个函数在我的mono(4.6)发行版中似乎根本不存在,所以我只能猜测它不再存在了。

我还找到了一些如何使用 PInvoke 执行此操作的示例。这是我不想使用的东西 - 因为 InternalCall 对于我的目的来说要快得多。 当然,如果 PInvoke 是唯一的方法,那就这样吧,但我对此表示怀疑。

到最后,我真的不知道如何从这里开始。

【问题讨论】:

否决票的任何具体原因?我应该添加一些东西吗? 在 Microsoft 的 InternalCall 工具链实现 C++/CLI 中,您将使用签名 setCallback(System::Action<System::String^>^ delegate) 直接接受直接接收 .NET 字符串的 .NET 委托。也许如果您展示了如何在 Mono 中向 InternalCall 函数传递 .NET 引用类型,它可能会导致更轻量级的解决方案。 【参考方案1】:

经过几个小时的挖掘,我终于找到了一个(?)解决方案。 基本上,适用于 PInvoke 方法的方法在这里也适用,您可以将函数指针而不是委托从 C# 传递到 C(++)。 我更喜欢一个可以直接传递委托的解决方案,但你总是可以在 C# 中添加一些包装器代码,至少让它看起来像这样。

解决方案:

C#:

public delegate void CallbackDelegate(string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void setCallback(IntPtr aCallback);

private CallbackDelegate del; 
public void testCallbacks()

    System.Console.Write("Registering C# callback...\n");
    del = new CallbackDelegate(callback01);
    setCallback(Marshal.GetFunctionPointerForDelegate(del));

    System.Console.Write("Calling passed C++ callback...\n");


public void callback01(string message)

    System.Console.Write("callback 01 called. Message: " + message + "\n");

C++:

typedef void (*CallbackFunction)(MonoString*);
void setCallback(CallbackFunction delegate)

    std::cout << &delegate << std::endl;
    delegate(mono_string_new(mono_domain_get(), "Test string set in C++"));

但请注意:您需要以某种方式将委托保留在 C# 中(这就是我将其分配给“del”的原因),否则它将被 GC 捕获并且您的回调将变得无效。 当然,这是有道理的,但我觉得在这种情况下很容易忘记这一点。

【讨论】:

我已经用我自己的库尝试了这个解决方案,.NET 似乎调用了两次回调函数,导致访问冲突。为什么会这样?【参考方案2】:

您可以使用 intptr_t 将函数指针作为 C++ 中的参数传递给 C#。

MSDN 不准确,以下代码有效:

在 C++ 中:

static void func(int param)

    //...
  
    
void other_func()

    ptr->SetCallback( reinterpret_cast<intptr_t>(func));

在 C# 中:

public static mydelegatetype somefunc = null;
    
public void SetCallback(IntPtr function_pointer)

    somefunc = (mydelegatetype)
    Marshal.GetDelegateForFunctionPointer(function_pointer, typeof(mydelegatetype));

【讨论】:

以上是关于如何将委托或函数指针从 C# 传递到 C++ 并使用 InternalCall 调用它的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 C++-CLI 回调和委托将字符串从 C++-CLI 传递到 C#

SWIG:如何将 C++ 对象数组从 C# 传递到 C++?

从 C++ 传递函数指针以由 C# 调用 - 函数参数包括宽字符字符串 (LPCWSTR)

C++成员函数指针指向全局函数指针

c++或者C 语言中有没有类似C#中事件委托的机制

使用 void 指针将字符串从 C++ 传递到 C#