Exposing COM events to VBScript (ATL)

旧城冷巷雨未停 提交于 2019-11-30 23:01:24

Implementation of IProvideClassInfo2 should be sufficient (though I was under impression that it should work even without it). I created a minimalistic ATL project from template as a sample. This is ATL DLL + COM Class + a method and an event + IProvideClassInfo2:

Source code [SVN, Browser Friendly] + Win32 Binary.

Test .vbs is the following:

Function Test_Event(Text)
  WScript.Echo "Event: " + Text
End Function

Dim Test
Set Test = WScript.CreateObject("AlaxInfo.VbsEvents.Foo", "Test_")
Test.Method("Event Fun")

And the method itself is:

// IFoo
    STDMETHOD(Method)(BSTR sText) throw()
    {
        ATLTRACE(atlTraceCOM, 4, _T("sText \"%s\"\n"), CString(sText));
        ATLVERIFY(SUCCEEDED(Fire_Event(sText)));
        return S_OK;
    }

And having executed this, the output is:

I thought you might be having issues with events in your sample project if you are firing them from a background thread. You can also step your project with debugger and check if you have any errors in the Fire_ call.

You can turn your VBScript function into IDispatch* pointers. For example the onreadystatechange handler in the Microsoft.XMLHTTP object.

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!"

Outputs:

ReadyState: 1
Waiting for 20 seconds to allow OnReadyStateChange events to finish.
ReadyState: 2
ReadyState: 3
ReadyState: 4
Done!

Extracting the IDL for C:\Windows\System32\msxml3.dll shows we're setting an IDispatch* property:

[
  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);
};

When adapting this approach to your C++ application, your calling code will need to use IDispatch::Invoke with a DISPID of DISPID_VALUE, for example:

// 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);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!