如何实现和注册一个COM回调

本文关键字:一个 COM 回调 何实现 实现 注册 | 更新日期: 2023-09-27 18:03:54

我正在尝试创建一个COM接口,允许从COM回调到调用代码。在c#中,我似乎找不到任何合适的解释。

我有一个正常的COM接口工作,但我不知道如何正确注册和设置回调部分。

我找不到任何关于如何在c#中正确做到这一点的信息,因为我能想到的大多数谷歌搜索词包括"。com"只导致。com站点,并且在谈论回调和事件时不包括实际的。com接口,它们只包括默认的非。com接口。任何帮助都将是感激的,即使它只是指出一个来源,我可以找到更好的,更有用的信息。

我的回调接口是这样的:

using System;
using System.Runtime.InteropServices;
namespace ComAdapter
{
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("7f176b75-7b66-463a-94ab-493943792f88")]
    public interface INet40Callback
    {
        [PreserveSig]
        void Callback(string data);
    }
}

回调类我尝试看起来像这样:

using net40lib;
using System;
using System.Runtime.InteropServices;
namespace ComAdapter
{
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(INet40Callback))]
    [Guid("023e4ad9-77d1-461e-af89-4cfc2b53959c")]
    public class Net40Callback : INet40CallbackRegistration
    {
        [ComVisible(false)]
        public delegate void MyCallbackDelegate(string data);
        public event MyCallbackDelegate Callback;
        public INet40Callback callbackObject;
        public Net40Callback()
        {
            Net40Class.Instance.VersionSent += Instance_VersionSent;
        }
        void Instance_VersionSent(object sender, EventArgs e)
        {
            if (callbackObject != null)
            {
                callbackObject.Callback("blah");
            }
            if (Callback != null)
            {
                Console.WriteLine("Instance_VersionSent Sending Callback");
                Callback("blah");
            }
        }
        public void RegisterCallback(INet40Callback reg)
        {
            if (reg is INet40Callback)
            {
                callbackObject = reg as INet40Callback;
            }
        }
    }
}
到目前为止,调用程序看起来像这样:
using ComAdapter;
using System;
using System.Runtime.InteropServices;
namespace net20lib
{
    class Net40Callback : INet40Callback
    {
        [PreserveSig] 
        public void Callback(string data)
        {
            Console.WriteLine("Callback Called with data {0}", data);
        }
    }
}
注册:

using System;
using System.Runtime.InteropServices;
namespace ComAdapter
{
    [ComVisible(true)]
    [Guid("3b2ab267-9e9a-47be-ab0a-f55db10a971e")]
    public interface INet40CallbackRegistration
    {
        void RegisterCallback(INet40Callback callback);
    }
}

测试类的调用:接口的实现:

internal class Net40Callback : INet40Callback
{
  [PreserveSig]
  public void Callback(string data)
  {
      Console.WriteLine("Callback Called with data {0}", data);
  }
}

注册代码:

INet40Callback callback = new Net40Callback();
Type myCallbackType = Type.GetTypeFromProgID("ComAdapter.Net40Callback");
object myCallbackInstance = Activator.CreateInstance(myCallbackType);
INet40CallbackRegistration registration = (INet40CallbackRegistration)myCallbackInstance;
registration.RegisterCallback(callback);

如何实现和注册一个COM回调

你真的不应该从c# COM客户端测试c# COM服务器,这不是它从非托管代码调用时的工作方式。然后你不需要保持对INet40Callback的引用,event声明应该足以让COM客户端通过IConnectionPointContainer(使用RegAsm生成的类型库)连接事件。除非你真的想同时支持两个选项。

无论如何,为了使您当前的代码工作,尝试将[ComVisible(true)]添加到Net40Callback[ComImport]添加到INet40Callback:

[ComImport]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("7f176b75-7b66-463a-94ab-493943792f88")]
public interface INet40Callback
{
    // ..
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]]
[ComDefaultInterface(typeof(INet40Callback))]
internal class Net40Callback : INet40Callback
{
    // ...
}

更新后的,除了删除public INet40Callback callbackObject引用外,您不需要在c#端做太多更改。但是,您需要一个非托管COM客户端来测试事件。它可以是一个普通的c++ COM应用程序,一个ATL/MFC c++应用程序,一个VB6应用程序,甚至一个HTML应用程序。带有<object>标记的HTA文件)。下面是后者的一个例子(未经测试)。复制到test.hta在Windows资源管理器中运行这个文件:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "">
<html>
<head>
    <title>Managed COM Object Test</title>
    <object id="managedObject"
        classid="clsid:023e4ad9-77d1-461e-af89-4cfc2b53959c"
        codebase="#version=0,0,0,0">
    </object>
    <script for="managedObject" event="Callback(data)" type="text/javascript">
        output.innerText = "Callback(" + data + ") fired";
    </script>
</head>
<body>
    <div id="output">&nbsp;</div>
</body>
</html>