3

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

测试脚本.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)

测试脚本.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;
}
4

2 回答 2

2

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

[已编辑]此更新基于您发布的更新代码。我不知道为什么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方法,也许你只是没有表现出来。您不必从中派生IDispatchExIDispatch就足够了。我建议您以以下 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;
    }
};
于 2013-09-12T07:38:49.310 回答
-1

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

IE9及以上SDK

于 2014-12-04T16:33:09.563 回答