暴露COM事件的VBScript(ATL)(Exposing COM events to VBScr

2019-06-26 15:11发布

我已经使用“ATL简单对象”向导以C COM服务器DLL ++与ATL。 我跟着微软的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不会被调用。

我曾尝试:

  • 看完这个知识库文章 ,我添加IProvideClassInfo2的实施,但这并没有帮助。
  • 此FAQ提到,出接口不应该定义为一个双接口。 我删除了“双反”从我的接口定义,但是这并没有帮助。
  • 我发现这太问题 ,似乎描述了类似的问题。 答案是从来没有放弃,只有一种解决方法。

更新1:

我现在知道它是失败的,但不是原因:在_ISetACLCOMServerEvents_CP.h火灾事件方法进行的调用失败,E_UNEXPECTED。 这是该行:

hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

解:

问题是,我所谓的事件在COM对象从后台线程发射功能,从错误的公寓等字样。

Answer 1:

实现IProvideClassInfo2应该是足够的(虽然我下的印象,它应该工作,即使没有的话)。 我创建了一个简约ATL项目从模板作为样本。 这是ATL DLL + COM +类的方法和事件+ IProvideClassInfo2

源代码[ SVN , 浏览器友好 ] + 的Win32二进制 。

测试.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_电话。



Answer 2:

你可以把你的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!

提取IDL为C:\Windows\System32\msxml3.dll表明,我们设定一个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 ++应用程序,调用代码将需要使用IDispatch::Invoke用的DISPID DISPID_VALUE ,例如:

// IDispatch* pOnReadyStateChange <-- set from VBScript.
HRESULT hr = S_OK;
CComVariant vResult;
EXCEPINFO ei = { };
DISPPARAMS dispParams = { NULL, 0, 0, 0 };
hr = pOnReadyStateChange->Invoke(
    DISPID_VALUE,
    IID_NULL,
    0,
    DISPATCH_METHOD,
    &dispParams,
    &vResult,
    &ei,
    NULL);


文章来源: Exposing COM events to VBScript (ATL)