如何将C ++回调函数传递给C#COM方法

时间:2018-10-15 15:10:44

标签: c# visual-c++ com

我有一个用C#完成的COM服务器,其中一种方法带有回调函数。

   [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class REDIServer
    {
        public bool Connect([MarshalAs(UnmanagedType.FunctionPtr)] ref CallBackFunction callback)
        {
            // send callback deeper into C# code
            return _service.Connect(callback);
        }

从该dll生成tlb代码以便可以在我的C ++客户端中使用它时,它看起来像这样:

  virtual HRESULT __stdcall Connect (
    /*[in,out]*/ long * callback,
    /*[out,retval]*/ VARIANT_BOOL * pRetVal ) = 0;

最后,当我尝试使用C ++中的Connect()调用时,我会像这样使用它:

void Callback()
{
    cout << "got a callback\n";
}

int main()
{
    HRESULT hr = CoInitialize(NULL);

    try
    {
        REDIXLServer::_REDIServerPtr ptr(__uuidof(REDIXLServer::REDIServer));

        VARIANT_BOOL ret;
        hr = ptr->Connect((long *) &Callback, &ret);
    }
    catch (_com_error & e)
    {
        cout << e.ErrorMessage() << endl;
    }

该程序在Conect()调用时崩溃。我收到一个消息框“ Managed Debugging Assistant'InvalidFunctionPointerInDelegate已检测到问题...。无效的函数指针已传递到运行时中,以转换为委托。...”

那我做错了什么?谢谢

1 个答案:

答案 0 :(得分:0)

首先,如果要与本机COM客户端进行互操作,我建议使用COM events instead of callbacks

此外,我建议不要使用[ClassInterface(ClassInterfaceType.AutoDual)],因为它在生成的tlh / tli文件中会产生很多杂物。而是定义一个接口并在您的类中实现它。

public delegate double FunctionCallback(int arg);

[ComVisible(true)]
[Guid("5F11C485-0AA8-461D-BB56-32D620D17311")]
public interface ITestServer
{
  double Connect([MarshalAs(UnmanagedType.FunctionPtr)] FunctionCallback callback, int arg);
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class TestServer : ITestServer
{ 
  public double Connect(FunctionCallback callback, int arg)
  {
    Console.Out.WriteLine("In server, calling callback...");
    return callback(arg);
  }  
}

您不需要通过ref(ref CallBackFunction发送回调,因为您将不会更改它以指向不同的实现,并且由于Callback&Callback相同,因此您无论如何都无法取消引用-这就是它崩溃的原因。

在您的本机客户端中,回调将建模为long

double Connect (long callback, long arg );

发送函数指针时,只需将其强制转换为long

double triple(int arg) {
    return arg * 3.0;
}

double halve(int arg) {
    return arg / 2.0;
}

int main() {
    CoInitialize(NULL);
    server::ITestServerPtr svr(__uuidof(server::TestServer));
    printf("tripled: %g\n", svr->Connect((long)triple, 5));
    printf("halved: %g\n", svr->Connect((long)halve, 5));
    return 0;
}