【问题标题】:how to pass C++ callback function into C# COM method如何将 C++ 回调函数传递给 C# COM 方法
【发布时间】:2019-03-20 01:27:05
【问题描述】:

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

   [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() 时崩溃。我收到一个消息框“托管调试助手 'InvalidFunctionPointerInDelegate 检测到问题......无效的函数指针已传递到运行时以转换为委托......”

那我做错了什么?谢谢

【问题讨论】:

标签: c# visual-c++ com


【解决方案1】:

首先,如果与本机 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&amp;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;
}

【讨论】:

  • 回调是一个指针,因此如果您想正确支持 32 位和 64 位二进制文​​件,则不应使用 long(C/C++ 中为 32 位)而是使用指针类型。跨度>
  • 当然,但是您无法控制工具生成的互操作类型,这些类型定义了在 .tlh/.tli 文件中声明的类型。对于 32 位组件,它使用 long,因此该示例显示了在这种情况下使用什么(从 OP 的示例中可以清楚地看出这是 32 位环境)。对于 64 位组件,将使用 __int64,因此可以安全地转换为由 typelib 定义的类型。
猜你喜欢
  • 2021-02-16
  • 1970-01-01
  • 2013-04-12
  • 2013-04-14
  • 2021-02-23
  • 2011-11-29
  • 1970-01-01
  • 1970-01-01
  • 2016-10-08
相关资源
最近更新 更多