How do I listen the event of Running IE with IWebB

2019-07-20 22:08发布

问题:

Currently I want to automate the running IE. I have successfully attached the running IE using below code (I assume there is only one IE within one tab)

#include "atl/atlbase.h"
#include <exdisp.h>
#include <mshtml.h>
CComQIPtr<IWebBrowser2> pCurIE;    
void __fastcall TForm4::Button3Click(TObject *Sender)
{
    bool SuccessToHook = false;
    CComPtr<IShellWindows> m_spSHWinds;
    if (FAILED(m_spSHWinds.CoCreateInstance( __uuidof( ShellWindows)))){

        return ;
    }


    LONG nCount;
    m_spSHWinds->get_Count( &nCount);
    ShowMessage(nCount);
    for (int i = 0; i < nCount; i++) {
        CComPtr<IDispatch> pDisp;
        m_spSHWinds->Item( CComVariant(i), &pDisp);
        CComQIPtr<IWebBrowser2> pIE(pDisp);
        if (pIE == NULL){

            continue ;
        }
        CComPtr<IDispatch> pDispDoc;
        pIE->get_Document(&pDispDoc);
        CComQIPtr<IHTMLDocument2> pHtmlDoc(pDispDoc);
        if (pHtmlDoc){
            pCurIE = pIE;
            SuccessToHook = true;
            break ;
        }
    }
    ShowMessage(SuccessToHook ? "Success to hook." : "Failed to hook." );
}

Now I can control the current running IE like navigating and read the current state. But as I want to show messages when the events like onDocumentComplete Event are fired. I have no idea how to listen the event following my current code. A simple sample code with BCB will be very appreciated as there are some samples with VC++ but my project is on C++ XE2.


Thanks @Remy Lebeau and this link, I finally solve my problem. I leave my code here and hope it maybe helpful for someone else.

A class derived from TEventDispatcher

#include <exdisp.h>
#include <exdispid.h>
#include <mshtml.h>
#include <mshtmdid.h>
#include <utilcls.h>
//---------------------------------------------------------------------------
class TForm4;
class EventHandler:public TEventDispatcher<EventHandler,&DIID_DWebBrowserEvents2>{
    private:
        bool connected;
        TForm4 *theform;
        IUnknown* server;
    protected:
        HRESULT InvokeEvent(DISPID id, TVariant *params){
            switch(id){
                case DISPID_DOCUMENTCOMPLETE:
                    ShowMessage("On Document Complete");
                    break;
                default:
                    break;
            }
        }
    public:
        EventHandler(){
            connected = false; //not connected;
            theform = false; //backptr to form is null
        }
        ~EventHandler(){
            if (connected)
                Disconnect();
        }
        void Connect(TForm4 *form,  IUnknown* srv){
            server = srv;
            theform = form; //back pointer to form to do stuff with it.
            server->AddRef(); //addref the server
            ConnectEvents(server);
        }
        void Disconnect(){
            DisconnectEvents(server);   //disconnect the events
            server->Release();
        }
};

Start to listen

void __fastcall TForm4::Button5Click(TObject *Sender)
{
    Event = new EventHandler();
    Event->Connect(this, pCurIE);
}

Stop listening

void __fastcall TForm4::Button6Click(TObject *Sender)
{
    Event->Disconnect();
}

回答1:

You have to write a class in your code that implements the DWebBrowserEvents2 interface. Then you can query the browser for its IConnectionPointContainer interface, call the IConnectionPointContainer::FindConnectionPoint() method to find the IConnectionPoint that cooresponds to DWebBrowserEvents2, and call the IConnectionPoint::Advise() method passing it an instance of your class. Don't forget to call IConnectionPoint::Unadvise() when you are done using the events.

To help you with that, you can derive your class from the VCL's TEventDispatcher class in utilcls.h. Its ConnectEvents() and DisconnectEvents() methods handle the IConnectionPoint stuff for you. You would then just override the abstract InvokeEvent() method (each of the browser's events have their own DISPID values).