C# 运行 活动自动化对象 - 如何获取事件源?
C# Running Active Automation Object - How to Source events?
我有一个(长 运行)控制台应用程序,用 C# 编写,我希望能够通过 COM 对其进行操作(因此没有 InProc DLL 和 regasm.exe)。 IDispatch
是我所需要的 - 所以一个 classic OLE Automation 对象。
在这里,我将展示我尝试做的事情的最小版本。我定义了一个 COM class 这样的:
[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()
中,我只是在 运行 对象 Table (ROT) 中使用 "comTestApp"
Moniker 注册了对象,然后让应用程序休眠。
你可以看到完整的来源 here.
当我尝试调用该对象的方法时,它工作得很好。例如,此 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 "Could not connect object")。
如果我使用 regasm.exe 将程序集注册为 InProc 对象,则相同的代码(仅需添加 class GUID/ProgID)有效,但我不需要它。我需要访问 运行 应用程序,这就是我使用 ROT 的原因。
我创建了一个简单的 C++ 测试,看看是否可以找到有关该问题的更多信息。来源是here。我已经编写了一个实现 IDispatch
接口的最小 COM 对象,它应该充当事件接收器。首先,我从 ROT 中获取对象,查询 IConnectionPointContainer
,然后获取 IConnectionPoint
以获得 ITestEvents
的 IID,最后调用其 Advise()
方法。与 VBScript 一样,它失败了(尽管我收到另一个错误 - 0x80040202)。我在事件接收器的 QueryInterface
方法上放置了一个断点,以查看调用 Advise()
时会发生什么。我可以看到 QueryInterface
被各种接口调用,最后它请求我的 ITestEvents
,我 return 并设置状态 S_OK
。但是,Advise()
方法 return 仍然会出现上述错误。
我还尝试了另一件事:我将 ITestEvents
的 GUID 设置为 {00020400-0000-0000-C000-000000000046}
,这是 IDispatch
的 IID。而现在 Advise()
returns S_OK
!我什至模拟了一个事件,事件接收器的 Invoke()
方法被调用了!唉,这并不能解决一般的问题。如果您通过 IID 直接从 IConnectionPointContainer
请求它 - 您会得到它,但它似乎没有被 ITypeInfo
正确枚举并且 VBScript 仍然不起作用。
我几乎没有使用 COM 的经验,所以我不确定从这里该何去何从。事实上,如果我使用 IDispatch
IID 使其工作,让我想知道 ITestEvents
接口是否需要一些自定义封送处理,尽管它是纯 IDispatch
所以我认为它应该是运行时处理得很好。
谢谢!
我的结论是,无法提供接口 IID 未在注册表中注册的连接接收器(到进程外对象)。
似乎编组信息是强制性的,以便将接收器接口发送到服务器。我希望由于 C# 类 提供完整 ITypeInfo
,COM 运行时将确定接口是 IDispatch
类型并使用默认代理。 las,情况似乎并非如此,因此唯一的选择仍然存在 - 注册表。
作为最低限度,我发现这是必需的:
[HKEY_CLASSES_ROOT\Interface\{9009311a-c0b2-42a4-8e7c-f42091d71594}\ProxyStubClsid32]
@="{00020424-0000-0000-C000-000000000046}"
这定义了我们的事件接口,并表示我们使用标准 IDispatch 代理作为代理。
我已经在 Windows 7 和 10 上测试过它并且有效! - VBScript 可以 ConnectObject
并且一切正常。
无意中我发现了一个 Win7 安装,但它不起作用 (win ver 6.1 build 7601 sp1)。它仅在注册类型库 (regasm.exe /tlb
) 后起作用。有问题的安装已禁用 Windows 更新(我的另一个 Win7 已完全更新)所以我猜在某些时候已经更改了一些内容,这使得即使没有类型库也可以编组接口,基于我们指定的事实默认 IDispatch 代理。
最后,由于我想让我的应用程序保持可移植性并独立于注册表,我求助于手动事件实现。可以看到类似的东西 here.
我有一个(长 运行)控制台应用程序,用 C# 编写,我希望能够通过 COM 对其进行操作(因此没有 InProc DLL 和 regasm.exe)。 IDispatch
是我所需要的 - 所以一个 classic OLE Automation 对象。
在这里,我将展示我尝试做的事情的最小版本。我定义了一个 COM class 这样的:
[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()
中,我只是在 运行 对象 Table (ROT) 中使用 "comTestApp"
Moniker 注册了对象,然后让应用程序休眠。
你可以看到完整的来源 here.
当我尝试调用该对象的方法时,它工作得很好。例如,此 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 "Could not connect object")。
如果我使用 regasm.exe 将程序集注册为 InProc 对象,则相同的代码(仅需添加 class GUID/ProgID)有效,但我不需要它。我需要访问 运行 应用程序,这就是我使用 ROT 的原因。
我创建了一个简单的 C++ 测试,看看是否可以找到有关该问题的更多信息。来源是here。我已经编写了一个实现 IDispatch
接口的最小 COM 对象,它应该充当事件接收器。首先,我从 ROT 中获取对象,查询 IConnectionPointContainer
,然后获取 IConnectionPoint
以获得 ITestEvents
的 IID,最后调用其 Advise()
方法。与 VBScript 一样,它失败了(尽管我收到另一个错误 - 0x80040202)。我在事件接收器的 QueryInterface
方法上放置了一个断点,以查看调用 Advise()
时会发生什么。我可以看到 QueryInterface
被各种接口调用,最后它请求我的 ITestEvents
,我 return 并设置状态 S_OK
。但是,Advise()
方法 return 仍然会出现上述错误。
我还尝试了另一件事:我将 ITestEvents
的 GUID 设置为 {00020400-0000-0000-C000-000000000046}
,这是 IDispatch
的 IID。而现在 Advise()
returns S_OK
!我什至模拟了一个事件,事件接收器的 Invoke()
方法被调用了!唉,这并不能解决一般的问题。如果您通过 IID 直接从 IConnectionPointContainer
请求它 - 您会得到它,但它似乎没有被 ITypeInfo
正确枚举并且 VBScript 仍然不起作用。
我几乎没有使用 COM 的经验,所以我不确定从这里该何去何从。事实上,如果我使用 IDispatch
IID 使其工作,让我想知道 ITestEvents
接口是否需要一些自定义封送处理,尽管它是纯 IDispatch
所以我认为它应该是运行时处理得很好。
谢谢!
我的结论是,无法提供接口 IID 未在注册表中注册的连接接收器(到进程外对象)。
似乎编组信息是强制性的,以便将接收器接口发送到服务器。我希望由于 C# 类 提供完整 ITypeInfo
,COM 运行时将确定接口是 IDispatch
类型并使用默认代理。 las,情况似乎并非如此,因此唯一的选择仍然存在 - 注册表。
作为最低限度,我发现这是必需的:
[HKEY_CLASSES_ROOT\Interface\{9009311a-c0b2-42a4-8e7c-f42091d71594}\ProxyStubClsid32]
@="{00020424-0000-0000-C000-000000000046}"
这定义了我们的事件接口,并表示我们使用标准 IDispatch 代理作为代理。
我已经在 Windows 7 和 10 上测试过它并且有效! - VBScript 可以 ConnectObject
并且一切正常。
无意中我发现了一个 Win7 安装,但它不起作用 (win ver 6.1 build 7601 sp1)。它仅在注册类型库 (regasm.exe /tlb
) 后起作用。有问题的安装已禁用 Windows 更新(我的另一个 Win7 已完全更新)所以我猜在某些时候已经更改了一些内容,这使得即使没有类型库也可以编组接口,基于我们指定的事实默认 IDispatch 代理。
最后,由于我想让我的应用程序保持可移植性并独立于注册表,我求助于手动事件实现。可以看到类似的东西 here.