我使用“ATL 简单对象”向导在 C++ 中使用 ATL 构建了一个 COM 服务器 DLL。我遵循了 Microsoft 的ATLDLLCOMServer示例。一切正常,除了一件事:我没有收到 VBScript 中的 COM 事件。我确实收到了 C# 中的事件。在早期的基于 MFC 的实现中,我曾在 VBScript 中作为 ActiveX 控件工作。
我的控件定义如下:
class ATL_NO_VTABLE CSetACLCOMServer :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CSetACLCOMServer, &CLSID_SetACLCOMServer>,
public IConnectionPointContainerImpl<CSetACLCOMServer>,
public CProxy_ISetACLCOMServerEvents<CSetACLCOMServer>,
public IDispatchImpl<ISetACLCOMServer, &IID_ISetACLCOMServer, &LIBID_SetACLCOMLibrary, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IProvideClassInfo2Impl<&CLSID_SetACLCOMServer, &DIID__ISetACLCOMServerEvents, &LIBID_SetACLCOMLibrary> /* Required for event support in VBS */
{
public:
BEGIN_COM_MAP(CSetACLCOMServer)
COM_INTERFACE_ENTRY(ISetACLCOMServer)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()
BEGIN_CONNECTION_POINT_MAP(CSetACLCOMServer)
CONNECTION_POINT_ENTRY(__uuidof(_ISetACLCOMServerEvents))
END_CONNECTION_POINT_MAP()
[...]
在 VBScript 中,我像这样使用 COM 对象:
set objSetACL = WScript.CreateObject("SetACL.SetACL", "SetACL_")
' Catch and print messages from the SetACL COM server which are passed (fired) as events.
' The name postfix of this function (MessageEvent) must be identical to the event name
' as defined by SetACL.
' The prefix (SetACL_) can be set freely in the call to WScript.CreateObject
sub SetACL_MessageEvent (Message)
WScript.Echo Message
end sub
IDL 的相关部分如下所示:
[
object,
uuid(E1B57CA5-FD7F-4304-BC0A-8BEBDE231D53),
dual,
nonextensible,
pointer_default(unique),
helpstring("SetACL COM Interface")
]
interface ISetACLCOMServer : IDispatch
{
};
[
uuid(00D4DCD3-02B9-4A71-AB61-2283504620C8),
version(1.0),
helpstring ("SetACL Type Library")
]
library SetACLCOMLibrary
{
importlib("stdole2.tlb");
[
uuid(35F76182-7F52-4D6A-BD6E-1317345F98FB),
helpstring ("SetACL Event Interface")
]
dispinterface _ISetACLCOMServerEvents
{
properties:
methods:
[id(1), helpstring("Receives string messages that would appear on the screen in the command line version")]
void MessageEvent([in] BSTR message);
};
[
uuid(13379563-8F21-4579-8AC7-CBCD488735DB),
helpstring ("SetACL COM Server"),
]
coclass SetACLCOMServer
{
[default] interface ISetACLCOMServer;
[default, source] dispinterface _ISetACLCOMServerEvents;
};
};
问题: SetACL_MessageEvent永远不会被调用。
我试过的:
- 在阅读了这篇知识库文章后,我添加了 IProvideClassInfo2 的实现,但这并没有帮助。
- 此常见问题解答提到传出接口不应定义为双接口。我从我的接口定义中删除了“双重”,但这并没有帮助。
- 我发现这个 SO question似乎描述了一个类似的问题。从来没有给出答案,只有一个解决方法。
更新 1:
我现在知道它失败的地方,但不知道为什么:在 _ISetACLCOMServerEvents_CP.h 的触发事件方法中,调用 Invoke 失败并显示 E_UNEXPECTED。这是行:
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);
解决方案:
问题是我从 COM 对象中的后台线程调用事件触发函数,换句话说,从错误的单元。