如何实现和注册一个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);
你真的不应该从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"> </div>
</body>
</html>