2

我有一个(长期运行的)控制台应用程序,用 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,然后获取IConnectionPointITestEventsIID,最后调用它的Advise()方法。与 VBScript 一样,它会失败(尽管我收到另一个错误 - 0x80040202)。我在事件接收器的方法上放置了一个断点,QueryInterface以查看调用时会发生什么Advise()。我可以看到它QueryInterface被各种接口调用,最后它请求 my ITestEvents,我返回并设置 status S_OK。但是,该Advise()方法仍然返回上述错误。

我还尝试了另一件事:我已将.的 IID设置ITestEvents为的 GUID 。现在回来了!我什至模拟了一个事件,并且调用了事件接收器的方法!唉,这通常不能解决问题。如果您直接通过 IID 请求它 - 您会得到它,但它似乎没有被正确枚举,并且 VBScript 仍然无法正常工作。{00020400-0000-0000-C000-000000000046}IDispatchAdvise()S_OKInvoke()IConnectionPointContainerITypeInfo

我几乎没有使用 COM 的经验,所以我不知道从哪里开始。如果我使用IDispatchIID 就可以工作,这让我想知道ITestEvents接口是否需要一些自定义编组,尽管它是纯粹的IDispatch,所以我认为它应该由运行时处理好。

谢谢!

4

1 回答 1

0

我的结论是不可能提供接口 IID 未在注册表中注册的连接接收器(到进程外对象)。

似乎编组信息是强制性的,以便将接收器接口发送到服务器。我希望由于 C# 类提供 full ITypeInfo,COM 运行时会发现接口是IDispatch类型并使用默认代理。唉,情况似乎并非如此,所以唯一的选择仍然存在 - 注册表。

作为最低限度,我发现这是需要的:

[HKEY_CLASSES_ROOT\Interface\{9009311a-c0b2-42a4-8e7c-f42091d71594}\ProxyStubClsid32]
@="{00020424-0000-0000-C000-000000000046}"

这定义了我们的事件接口,并表示我们使用标准 IDispatch 代理进行代理。

我已经在 Windows 7 和 10 上对此进行了测试,并且可以正常工作!- VBScript 可以ConnectObject并且一切正常。

意外地,我发现了一个 Win7 安装,但它不起作用(win 6.1 build 7601 sp1)。它仅在注册类型库 ( regasm.exe /tlb) 后才起作用。有问题的安装禁用了 Windows 更新(我的其他 Win7 已完全更新)所以我猜在某些时候发生了一些变化,即使没有类型库,也可以根据我们指定默认 IDispatch 代理的事实来编组接口.

最后,由于我想让我的应用程序可移植且独立于注册表,因此我求助于手动事件实现。可以在这里看到类似的东西。

于 2018-03-16T09:55:52.063 回答