我使用“ATL简单对象”向导在C ++中使用ATL构建了一个COM服务器DLL。我遵循了微软的ATLDLLCOMServer示例。一切都很好,除了一件事:我没有收到VBScript中的COM事件。我确实收到了C#中的事件。我在VBScript中使用早期基于MFC的实现中的事件作为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。
我尝试过:
更新1:
我现在知道它失败了,但不知道原因:在_ISetACLCOMServerEvents_CP.h中的fire事件方法中,对Invoke的调用因E_UNEXPECTED而失败。这是一行:
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, NULL, NULL, NULL);
解决方案:
问题是我从COM对象的后台线程调用事件触发函数,换句话说是从错误的公寓调用。
答案 0 :(得分:5)
IProvideClassInfo2
的实施应该足够了(尽管我的印象是即使没有它也应该有效)。我从模板中创建了一个简约的ATL项目作为样本。这是ATL DLL + COM Class +一个方法和一个事件+ IProvideClassInfo2
:
源代码[SVN,Browser Friendly] + Win32 Binary。
测试.vbs
如下:
Function Test_Event(Text)
WScript.Echo "Event: " + Text
End Function
Dim Test
Set Test = WScript.CreateObject("AlaxInfo.VbsEvents.Foo", "Test_")
Test.Method("Event Fun")
方法本身是:
// IFoo
STDMETHOD(Method)(BSTR sText) throw()
{
ATLTRACE(atlTraceCOM, 4, _T("sText \"%s\"\n"), CString(sText));
ATLVERIFY(SUCCEEDED(Fire_Event(sText)));
return S_OK;
}
执行此操作后,输出为:
如果您从后台线程触发它们,我认为您的示例项目中可能存在事件问题。您也可以使用调试器来执行项目,并检查Fire_
调用中是否有任何错误。
答案 1 :(得分:0)
您可以将VBScript函数转换为IDispatch*
指针。例如onreadystatechange
对象中的Microsoft.XMLHTTP
处理程序。
Option Explicit
Dim xmlhttp
Function OnReadyStateChange
WScript.Echo "ReadyState: " & xmlhttp.readyState
End Function
Set xmlhttp = CreateObject("Microsoft.XMLHTTP")
xmlhttp.onreadystatechange = GetRef("OnReadyStateChange")
xmlhttp.open "GET", "http://stackoverflow.com", True
xmlhttp.send
WScript.Echo "Waiting for 20 seconds to allow OnReadyStateChange events to finish."
WScript.Sleep 20000
WScript.Echo "Done!"
输出:
ReadyState: 1
Waiting for 20 seconds to allow OnReadyStateChange events to finish.
ReadyState: 2
ReadyState: 3
ReadyState: 4
Done!
提取C:\Windows\System32\msxml3.dll
的IDL表示我们正在设置IDispatch*
属性:
[
odl,
uuid(ED8C108D-4349-11D2-91A4-00C04F7969E8),
helpstring("IXMLHTTPRequest Interface"),
dual,
oleautomation
]
interface IXMLHTTPRequest : IDispatch {
// ...
[id(0x0000000e), propput, helpstring("Register a complete event handler")]
HRESULT onreadystatechange([in] IDispatch* rhs);
};
在将此方法应用于您的C ++应用程序时,您的调用代码需要使用DISID为IDispatch::Invoke
的{{1}},例如:
DISPID_VALUE