我有一个(长期运行的)控制台应用程序,用 C# 编写,我希望能够通过 COM 操作它(所以没有 InProc DLL 和 regasm.exe)。IDispatch
是我所需要的 - 所以一个经典的 OLE 自动化对象。
在这里,我将展示我尝试做的最小版本。我已经定义了一个这样的 COM 类:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("9009311a-c0b2-42a4-8e7c-f42091d71594")]
public interface ITestEvents {
void OnEvent();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComSourceInterfaces(typeof(ITestEvents))]
public class ComClass {
public event Action OnEvent;
public int Test() {
return 100;
}
}
我只是在Main()
运行对象表 (ROT) 中使用"comTestApp"
Moniker 注册对象并休眠应用程序。你可以在这里看到完整的源代码。
当我尝试调用对象的方法时,这工作得很好。例如,这个 VBScript 工作正常:
Set obj = GetObject("comTestApp")
WScript.Echo obj.Test() Rem prints 100
但是当我尝试连接事件时:
Set obj = GetObject("comTestApp")
WScript.Echo obj.Test() Rem Works
WScript.ConnectObject obj, "obj_" Rem Fails
Sub obj_OnEvent
WScript.Echo "Wish it worked"
End Sub
调用时ConnectObject
出错(错误 0x80020009“无法连接对象”)。
如果我使用 regasm.exe 将程序集注册为 InProc 对象,则相同的代码(只需添加类 GUID/ProgID)可以工作,但我不需要。我需要访问正在运行的应用程序,这就是我使用 ROT 的原因。
我创建了一个简单的 C++ 测试,看看我是否能找到有关该问题的更多信息。来源在这里。我编写了一个最小的 COM 对象来实现IDispatch
应该充当事件接收器的接口。首先我从 ROT 中获取对象,查询IConnectionPointContainer
,然后获取IConnectionPoint
的ITestEvents
IID,最后调用它的Advise()
方法。与 VBScript 一样,它会失败(尽管我收到另一个错误 - 0x80040202)。我在事件接收器的方法上放置了一个断点,QueryInterface
以查看调用时会发生什么Advise()
。我可以看到它QueryInterface
被各种接口调用,最后它请求 my ITestEvents
,我返回并设置 status S_OK
。但是,该Advise()
方法仍然返回上述错误。
我还尝试了另一件事:我已将.的 IID设置ITestEvents
为的 GUID 。现在回来了!我什至模拟了一个事件,并且调用了事件接收器的方法!唉,这通常不能解决问题。如果您直接通过 IID 请求它 - 您会得到它,但它似乎没有被正确枚举,并且 VBScript 仍然无法正常工作。{00020400-0000-0000-C000-000000000046}
IDispatch
Advise()
S_OK
Invoke()
IConnectionPointContainer
ITypeInfo
我几乎没有使用 COM 的经验,所以我不知道从哪里开始。如果我使用IDispatch
IID 就可以工作,这让我想知道ITestEvents
接口是否需要一些自定义编组,尽管它是纯粹的IDispatch
,所以我认为它应该由运行时处理好。
谢谢!