eventsatlbhopostmessage

Javascript post message event handler in ATL C++ BHO


On a webpage I have a button with JavaScript post message. In my BHO IE add-on I need an event listener for this message event. Any clue how this can be done? My OnDocumentComplete is as below. Can you give some more pointers where we can write code for handling this event. I want to make a REST API call from this message handler.

TestScript.h:

// TestScript.h : Declaration of the CTestScript

#pragma once
#include "resource.h"       // main symbols
#include "TestBHO_i.h"
#include <mshtml.h>         // DOM interfaces

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

// CTestScript

class ATL_NO_VTABLE CTestScript :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CTestScript, &CLSID_TestScript>,
    public IObjectWithSiteImpl<CTestScript>,
    public IDispatchImpl<ITestScript, &IID_ITestScript, &LIBID_TestBHOLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IDispEventImpl<1, CTestScript, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>,
    //public IPersistPropertyBagImpl<CTestScript>,
    public IObjectSafetyImpl<CTestScript, INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA>
{
public:
    CTestScript()
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_TESTSCRIPT)

DECLARE_NOT_AGGREGATABLE(CTestScript)

BEGIN_COM_MAP(CTestScript)
    COM_INTERFACE_ENTRY(ITestScript)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

public:
    BEGIN_SINK_MAP(CTestScript)
        SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
        //SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, OnNavigationComplete)
    END_SINK_MAP()

    void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL);
    //void STDMETHODCALLTYPE OnNavigationComplete(IDispatch *pDisp, VARIANT *pvarURL);

    STDMETHOD(SetSite)(IUnknown *pUnkSite);

    HRESULT STDMETHODCALLTYPE DoSomething(){
        ::MessageBox(NULL, L"Hello", L"World", MB_OK);
        return S_OK;
    }
public:

//private:
    // InstallBHOMethod();
private:
    void EnableOpenOnDesktopButton(IHTMLDocument2 *pDocument);

private:
    void AddPostMessage(IHTMLDocument2 *pDocument); 

private:
    CComPtr<IWebBrowser2>  m_spWebBrowser;
    BOOL m_fAdvised;
};

OBJECT_ENTRY_AUTO(__uuidof(TestScript), CTestScript)

TestScript.cpp:

// TestScript.cpp : Implementation of CTestScript

#include "stdafx.h"
#include "TestScript.h"

// CTestScript

void debug(LPWSTR msg)
{
    ::MessageBox(NULL,msg,L"Debug",MB_OK);;
}

STDMETHODIMP CTestScript::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        // Cache the pointer to IWebBrowser2.
        HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser);
        if (SUCCEEDED(hr))
        {
            // Register to sink events from DWebBrowserEvents2.
            hr = DispEventAdvise(m_spWebBrowser);
            if (SUCCEEDED(hr))
            {
                m_fAdvised = TRUE;
            }
        }
    }
    else
    {
        // Unregister event sink.
        if (m_fAdvised)
        {
            DispEventUnadvise(m_spWebBrowser);
            m_fAdvised = FALSE;
        }

        // Release cached pointers and other resources here.
        m_spWebBrowser.Release();
    }

    // Call base class implementation.
    return IObjectWithSiteImpl<CTestScript>::SetSite(pUnkSite);
}

void STDMETHODCALLTYPE CTestScript::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
    HRESULT hr = S_OK;

    // Query for the IWebBrowser2 interface.
    CComQIPtr<IWebBrowser2> spTempWebBrowser = pDisp;
    //CComPtr<IEventTarget> spIEventTarget;
    // Is this event associated with the top-level browser?
    if (spTempWebBrowser && m_spWebBrowser &&
        m_spWebBrowser.IsEqualObject(spTempWebBrowser))
    {
        // Get the current document object from browser...
        CComPtr<IDispatch> spDispDoc;
        hr = m_spWebBrowser->get_Document(&spDispDoc);
        if (SUCCEEDED(hr))
        {
            // ...and query for an HTML document.
            CComQIPtr<IHTMLDocument2> spHTMLDoc = spDispDoc;
            if (spHTMLDoc != NULL)
            {
              EnableOpenOnDesktopButton(spHTMLDoc);
              AddPostMessage(spHTMLDoc) ;
            }
        }
    }
}


void CTestScript::EnableOpenOnDesktopButton(IHTMLDocument2* pDocument)
{
    CComPtr<IHTMLElement> bodypt;
    CComPtr<IHTMLElement> html;
    pDocument->get_body(&bodypt);
    bodypt->get_parentElement(&html);
    //TODO: concatinate old class and new class and apply toht
    BSTR className = L" my-browser-extension";
    html->put_className(className);
}


void CTestScript::AddPostMessage(IHTMLDocument2* pDocument)
{
    HRESULT hr = S_OK;
    CComPtr<IHTMLWindow2>  _spWindow;
    hr = pDocument->get_parentWindow(reinterpret_cast<IHTMLWindow2 **>(&_spWindow));
    if (SUCCEEDED(hr) && _spWindow)
    {
        CComPtr<IEventTarget> spIEventTarget;
        hr = _spWindow->QueryInterface(IID_IEventTarget, reinterpret_cast<void **>(&spIEventTarget));
        if (SUCCEEDED(hr) && spIEventTarget)
        {
            _pIEUIEventListener = new CIEUIEventListener(); // This class derives from IDispatch
            hr = spIEventTarget->addEventListener(_bstr_t("message"), _pIEUIEventListener,  VARIANT_TRUE);
            if (SUCCEEDED(hr))
            {
                debug(L"HEREE");
            }
        }
    }
}

IEUIEventListener.h:

#pragma once

class CIEUIEventListener : public IDispatchEx 
{
public:
    CIEUIEventListener(void);
    ~CIEUIEventListener(void);

    HRESULT STDMETHODCALLTYPE Invoke(       
    DISPID dispIdMember,
    REFIID riid,
    LCID lcid,
    WORD wFlags,
    DISPPARAMS *pDispParams,
    VARIANT *pVarResult,
    EXCEPINFO *pExcepInfo,
    UINT *puArgErr);
};

IEUIEventListener.cpp:

#include "StdAfx.h"
#include "IEUIEventListener.h"

CIEUIEventListener::CIEUIEventListener(void)
{
}

CIEUIEventListener::~CIEUIEventListener(void)
{
}
HRESULT STDMETHODCALLTYPE CIEUIEventListener::Invoke(   
    DISPID dispIdMember,
    REFIID riid,
    LCID lcid,
    WORD wFlags,
    DISPPARAMS *pDispParams,
    VARIANT *pVarResult,
    EXCEPINFO *pExcepInfo,
    UINT *puArgErr)
{       
    ::MessageBox(NULL,L"FYYYYYYYY",L"Debug",MB_OK);;
    return S_OK;
}

Solution

  • If you mean window.postMessage, you'd need to add a listener for message event on DOM window object (window.addEventListener("message")) from your BHO. To get the window object, use IWebBrowser2::get_Document, IHTMLDocument2::get_parentWindow, then query the window for IEventTarget and call addEventListener. Give it an implementation of IDispatch as listener parameter. It will be called back as IDispatch::Invoke(DISPID_VALUE) when a message is posted.

    [EDITED] This update is based upon the updated code you posted. I can't tell why IEventTarget is still undefined for you (perhaps, there's a problem with your Visual Studio include path configuration). So, just grab the definitions from here:

    MIDL_INTERFACE("305104b9-98b5-11cf-bb82-00aa00bdce0b")
    IEventTarget : public IDispatch
    {
    public:
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE addEventListener( 
            /* [in] */ __RPC__in BSTR type,
            /* [in] */ __RPC__in_opt IDispatch *listener,
            /* [in] */ VARIANT_BOOL useCapture) = 0;
    
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE removeEventListener( 
            /* [in] */ __RPC__in BSTR type,
            /* [in] */ __RPC__in_opt IDispatch *listener,
            /* [in] */ VARIANT_BOOL useCapture) = 0;
    
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE dispatchEvent( 
            /* [in] */ __RPC__in_opt IDOMEvent *evt,
            /* [out][retval] */ __RPC__out VARIANT_BOOL *pfResult) = 0;
    
    };
    
    MIDL_INTERFACE("305104ba-98b5-11cf-bb82-00aa00bdce0b")
    IDOMEvent : public IDispatch
    {
    public:
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_bubbles( 
            /* [out][retval] */ __RPC__out VARIANT_BOOL *p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_cancelable( 
            /* [out][retval] */ __RPC__out VARIANT_BOOL *p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_currentTarget( 
            /* [out][retval] */ __RPC__deref_out_opt IEventTarget **p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_defaultPrevented( 
            /* [out][retval] */ __RPC__out VARIANT_BOOL *p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_eventPhase( 
            /* [out][retval] */ __RPC__out USHORT *p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_target( 
            /* [out][retval] */ __RPC__deref_out_opt IEventTarget **p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_timeStamp( 
            /* [out][retval] */ __RPC__out ULONGLONG *p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_type( 
            /* [out][retval] */ __RPC__deref_out_opt BSTR *p) = 0;
    
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE initEvent( 
            /* [in] */ __RPC__in BSTR eventType,
            /* [in] */ VARIANT_BOOL canBubble,
            /* [in] */ VARIANT_BOOL cancelable) = 0;
    
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE preventDefault( void) = 0;
    
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE stopPropagation( void) = 0;
    
        virtual /* [id] */ HRESULT STDMETHODCALLTYPE stopImmediatePropagation( void) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_isTrusted( 
            /* [out][retval] */ __RPC__out VARIANT_BOOL *p) = 0;
    
        virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_cancelBubble( 
            /* [in] */ VARIANT_BOOL v) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_cancelBubble( 
            /* [out][retval] */ __RPC__out VARIANT_BOOL *p) = 0;
    
        virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_srcElement( 
            /* [out][retval] */ __RPC__deref_out_opt IHTMLElement **p) = 0;
    
    };
    

    Next, your CIEUIEventListener doesn't look a like a COM object implementation. I don't see any IUnknown and IDispatch methods, perhaps you just didn't show it. You don't have to derive from IDispatchEx either, IDispatch is enough. I suggest you take the following implementation of DOM event sink as a base, it's self-explanatory:

    // Usage:
    //
    // CComPtr<CEventSink> eventSink;
    // CEventSink::Create(pTestScript, &eventSink); // pass eventSink where IDispatch* is expected
    //
    
    class CEventSink: 
        public CComObjectRoot,
        public IDispatch
    {
    protected:
        CTestScript* m_pParent;
    
        CEventSink() { m_pParent = NULL; }
    
    public:
        BEGIN_COM_MAP(CEventSink)
            COM_INTERFACE_ENTRY(IDispatch)
        END_COM_MAP()
    
        // create and initialize
        static HRESULT Create(CTestScript* pParent, CEventSink** pp)
        {
            CComObject<CEventSink>* pThis = NULL;
            CComObject<CEventSink>::CreateInstance(&pThis);
            if (!pThis) 
                return E_OUTOFMEMORY;
    
            pThis->m_pParent = pParent;
    
            (*pp = pThis)->AddRef();
            return S_OK;
        }
    
        // IDispatch
        STDMETHODIMP GetTypeInfoCount(UINT* pctinfo)
        {
            return E_NOTIMPL; 
        }
    
        STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
        {
            return E_NOTIMPL; 
        }
    
        STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
        {
            return DISP_E_UNKNOWNNAME;
        }
    
        STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
        {
            if ( dispidMember == DISPID_VALUE )
            {
                // handle the event
                // for example, call some method on m_pParent
            }
            return DISP_E_MEMBERNOTFOUND;
        }
    };