【问题标题】:Javascript post message event handler in ATL C++ BHOATL C++ BHO 中的 Javascript 发布消息事件处理程序
【发布时间】:2013-09-16 09:26:38
【问题描述】:

在网页上,我有一个带有 JavaScript 发布消息的按钮。在我的 BHO IE 插件中,我需要一个事件监听器来监听这个消息事件。任何线索如何做到这一点?我的OnDocumentComplete 如下。您能否提供更多指示,我们可以在其中编写处理此事件的代码。我想从此消息处理程序进行 REST API 调用。

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

【问题讨论】:

    标签: events atl bho postmessage


    【解决方案1】:

    如果您的意思是 window.postMessage,您需要从 BHO 为 DOM window 对象 (window.addEventListener("message")) 上的 message 事件添加一个侦听器。要获取window 对象,请使用IWebBrowser2::get_DocumentIHTMLDocument2::get_parentWindow,然后在window 中查询IEventTarget 并调用addEventListener。给它一个IDispatch 的实现作为listener 参数。发布消息时,它将被回调为IDispatch::Invoke(DISPID_VALUE)

    [EDITED] 此更新基于您发布的更新代码。 我不知道为什么 IEventTarget 仍然为您未定义(也许,您的 Visual Studio 包含路径配置存在问题)。所以,只需从这里获取定义:

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

    接下来,您的CIEUIEventListener 看起来不像是COM 对象实现。我没有看到任何IUnknownIDispatch 方法,也许你只是没有展示它。您也不必从IDispatchEx 派生,IDispatch 就足够了。我建议你以以下 DOM 事件接收器的实现为基础,它是不言自明的:

    // 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;
        }
    };
    

    【讨论】:

    • 请您详细说明一下。
    • 您的事件处理程序需要一个单独的 COM 对象。例如,从这个answer 检查CEventSink
    • 我创建了类 code CIEUIEventListener : public IDispatch code 并在其中调用方法。现在从 OnDocumentComplete code CComPtr spIEventTarget;code 给出 'IEventTarget' : undeclared identifier error。
    • 从那里下载Headers and Libraries for Windows Internet Explorer 9并使用mshtml.h,或者使用#import &lt;mshtml.tlb&gt; raw_interfaces_only, raw_native_types, no_namespace, named_guids, auto_search, auto_rename导入类型库。
    • IEventTarget 仍然不存在我认为这是由于我的系统中存在 32 位 64 位程序和库。以为我在 XP 32 位上尝试过,但默认的 Microsoft sdk 6.0 也没有 IEventTarget。 IE9 windows 标头具有这些定义,但如果我包含它们,它会在包含和引用的 .h 文件中给出编译语法错误。我正在考虑在 C# 中制作 BHO,因为它们之间的 .net 层可能不会出现这些问题。仍然在 C# BHO 中处理 windows.postmessage 事件可能是一个挑战。感谢@Noseratio 的指导!
    【解决方案2】:

    如果 IEventTarget 不可见,请务必下载 IE9 SDK,因为 windows SDK (7.x) 中的 mshtml 标头和 idl 还没有。

    IE9 and above SDK

    【讨论】:

    • 链接失效
    猜你喜欢
    • 2011-04-04
    • 2023-04-04
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多