webbrowser处理HTML Element Event事件

http://blog.csdn.net/sstower/article/details/6253409?reload

Example of Handling HTML Element Events in Microsoft Internet Explorer


The technique makes use of the Microsoft Foundation Classes (MFC) CHTMLView web browser component to gain access to the DHTML Object Model (Microsoft's HTML DOM). Portions of this technique were extracted from an original coding example found on the Microsoft Developer Network workshop area on the Microsoft Web

Once access to the DOM has been achieved, the IE DOM will be used to enumerate all the DOM elements of which we will select one to add an event listener to monitor the onClick event.

It is assumed that a Microsoft Foundation Classes (MFC) application is hosting the WebBrowser control, or launching Internet Explorer.

This article is divided into the following sections:

  • Accessing the DHTML Object Model using MFC's CHTMLView
  • Accessing an Element on the Page
  • Receiving Element Events

    Accessing the DHTML Object Model using MFC's CHTMLView

    The DHTML Object Model is used to access and manipulate the contents of an HTML page and is not available until the page is loaded. Your application determines that the page is loaded by handling the Document Complete event of the Web Browser control. MFC provides a web browser control called CHTMLView which we can easily extend and embed in our application.

    CHTMlView provides a virtual OnDocumentComplete method which you would create and use to monitor when the document is loaded. Once loaded, you can gain access to the DOM from the CHTMLView HtmlDocument method.

    This event may be fired once for each frame in the page, and once when the top frame of the document is loaded.

    In this case you would need to access the COM IDispatch interface pointer directly for the WebBrowser control and register a listener for DocumentComplete. When your listener was activated you would be able to compare the IDispatch pointer received to that of the web browser control. If they matched you would know you had the top level document.

    For simplicity purposes, this technique will register listeners when the CHTMLView MFC component OnDocumentComplete method is called.

    The example states that in order to listen to DOM events in Internet Explorer you need to first have a valid document. IE provides an IWebBrowser2 interface with a Navigate2 function. When the Navigate2 function is called with a URL a page is loaded into the browser and displayed.

    When the document has been completely loaded the browser component fires an onDocumentComplete event. You cannot access the document until it is fully loaded. This code sample makes use of the CHTMLView COM Component to gain access to the DOM when the Document is loaded. The reference to the DOM IHTMLDocument2 interface is then used for further processing.

     

    class MyCHTMLView :: public CHTMLView {
    
    }
    
    MyCHTMLView :: OnDocumentComplete( LPCTSTR lpszURL ) {
    
      
       IDispatch * pDocDisp = NULL;
       
       // get the DOM
       
       pDocDisp = this->GetHtmlDocument();
       
       if (pDocDisp != NULL) {
          // Obtained the document object by specifying the IHTMLDocument2 Interface.
          pDocDisp->QueryInterface( IID_IHTMLDocument2, (void**)&pDoc );
          if ( SUCCEEDED(hr) )
          {
              // Obtained the IHTMLDocument2 interface for the document object
              ProcessDocument( pDocDisp );
          }
          pDocDisp->Release();
       }            
    
    }
    

    Accessing an Element on the Page

    Using the IHTMLDocument2 interface pointer, you can request a collection of all elements in the HTML document through the IHTMLDocument2::get_all property.

    void CMyClass::ProcessDocument(IHTMLDocument2* pDoc)
    {
    IHTMLElementCollection* pElemColl = NULL;
    
        hr = pDoc->get_all( &pElemColl );
        if ( SUCCEEDED(hr) )
        {
            // Obtained element collection.
            ProcessElementCollection( pElemColl );
            pElemColl->Release();
        }
    }
    

    The IHTMLDocument2::get_all property returns a collection of all the HTML elements on the page through an IHTMLElementCollection interface pointer. You can use the IHTMLElementCollectioninterface to call the IHTMLElementCollection::item method and pass the name or ID of an element as a parameter, as shown in the following code.

    Note The item property will return a collection if there is more than one element with the specified name or ID. To prevent a collection from being returned, provide an index as the second parameter of item to specify which element should be returned.

    void CMyClass::ProcessElementCollection(IHTMLElementCollection* pElemColl)
    {
    IDispatch* pElemDisp = NULL;
    IHTMLElement* pElem = NULL;
    _variant_t varID( "myID", VT_BSTR );
    _variant_t varIdx( 0, VT_I4 );
    
        hr = pElemColl->item( varID, varIdx, &pElemDisp );
        if ( SUCCEEDED(hr) )
        {
            hr = pElemDisp->QueryInterface( IID_IHTMLElement, (void**)&pElem );
            if ( SUCCEEDED(hr) )
            {
                // Obtained element with ID of "myID".
                ConnectEvents( pElem );
                pElem->Release();
            }
            pElemDisp->Release();
        }
    }
    

    If you are working with HTML tags of a specific type, such as A tags, theIHTMLElementCollection::tags method returns a collection of all the elements that have the requested HTML tag name, also through an IHTMLElementCollection interface pointer.

    Receiving Element Events

    Each element in the DHTML Object Model supports an outgoing HTMLElementEvents2 interface. This interface defines the events that an HTML element can fire. You implement this interface to provide anevent sink, which is a COM object that implements an outgoing interface and is used as the mechanism for firing events.

    Note Interfaces implemented by a server usually have their methods called by the client, but to fire an event, the server calls the respective method on the client event sink. These interface are called outgoing interfaces. A COM object that implements an outgoing interface is also known as a connectable object.

    The following steps are required to receive events from an outgoing interface:

    1. Implement the event sink.

      The event sink implements the appropriate outgoing interface and methods. Internet Explorer event interfaces are dispinterfaces, so calls to event methods are made throughIDispatch::Invoke. This means that you only need to implement the IDispatch interface to handle events.

    2. Determine if the server is a connectable object.

      Call QueryInterface to retrieve a pointer to the IConnectionPointContainer interface.

    3. Find the appropriate connection point.

      Call the IConnectionPointContainer::FindConnectionPoint method to find the connection point you need. For Internet Explorer WebBrowser events, such as DocumentComplete, this isDWebBrowserEvents2. For element events, this is HTMLElementEvents2. You can also call theIConnectionPointContainer::EnumConnectionPoints to enumerate through all the connection points a server supports.

    4. Advise the connection point that you want to receive events.

      Using the IConnectionPoint interface pointer returned in the previous step, callIConnectionPoint::Advise, passing the IUnknown interface pointer of your event sink.

      Note The connectable object will use the IUnknown interface pointer to query the client for the event sink interface. If the event sink does not support the outgoing interface, Internet Explorer will query the client for the IDispatch interface.

    5. When you no longer want to receive events, you can call the IConnectionPoint::Unadvisemethod, passing the cookie you received from the call to Advise.

    The following sample code demonstrates how to begin receiving HTML element events for an element on an HTML page.

    void CMyClass::ConnectEvents(IHTMLElement* pElem)
    {
    HRESULT hr;
    IConnectionPointContainer* pCPC = NULL;
    IConnectionPoint* pCP = NULL;
    DWORD dwCookie;
    
        // Check that this is a connectable object.
        hr = pElem->QueryInterface( IID_IConnectionPointContainer, (void**)&pCPC );
        if ( SUCCEEDED(hr) )        
        {
            // Find the connection point.
            hr = pCPC->FindConnectionPoint( DIID_HTMLElementEvents2, &pCP );
            if ( SUCCEEDED(hr) )           
            {
                // Advise the connection point.
                // pUnk is the IUnknown interface pointer for your event sink
                hr = pCP->Advise( pUnk, &dwCookie );
                if ( SUCCEEDED(hr) )              
                {
                    // Successfully advised
                }
                pCP->Release();
            }
            pCPC->Release();        
        }
    } 
    

    The following sample code demonstrates how you would detect the firing of an onclick event in your implementation of IDispatch::Invoke.

    STDMETHODIMP CEventSink::Invoke(
                DISPID dispidMember,
                REFIID riid,
                LCID lcid,
                WORD wFlags,
                DISPPARAMS* pdispparams,
                VARIANT* pvarResult,
                EXCEPINFO* pexcepinfo,
                UINT* puArgErr)
    {
        switch ( dispidMember )
        {
        case DISPID_HTMLELEMENTEVENTS2_ONCLICK:
            OnClick();
            break;
    
        default:
            break;
        }
        return S_OK;
    }
注意的地方:
1. vc6在编译时找不到DIID_HTMLElementEvents2的定义,报错:
error LNK2001: unresolved external symbol _DIID_HTMLElementEvents2
可在vs2008中通过调试得出该值:
static const GUID DIID_HTMLElementEvents2 = 
{ 0x3050f60f, 0x98b5, 0x11cf, { 0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b } };
2. 根据接管的具体元素,这里的:
pCPC->FindConnectionPoint( DIID_HTMLElementEvents2, &pCP );
DIID会不一样,如接管
 <input type=button id=myid />
这时应该是DIID_HTMLinputTextElementEvents2
static const GUID DIID_HTMLinputTextElementEvents2 = 
{ 0x3050f618, 0x98b5, 0x11cf, { 0xbb, 0x82, 0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b } };
3.事件消息类可从IDispatch或者CCmdTarget继承下来:
class CMyEventSink : public IDispatch  
{
public:
 CMyEventSink();
 virtual ~CMyEventSink();
STDMETHODIMP QueryInterface(const struct _GUID &iid,void ** ppv);
ULONG __stdcall AddRef(void);
ULONG __stdcall Release(void);
STDMETHODIMP GetTypeInfoCount(unsigned int *);
STDMETHODIMP GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** );
STDMETHODIMP GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *);
STDMETHODIMP Invoke(
 long dispID,
 const struct _GUID &,
 unsigned long,
 unsigned short,
 struct tagDISPPARAMS * pParams,
 struct tagVARIANT *,
 struct tagEXCEPINFO *,
 unsigned int *);
};
//
// Construction/Destruction
//
CMyEventSink::CMyEventSink()
{
}
CMyEventSink::~CMyEventSink()
{
}
STDMETHODIMP CMyEventSink::QueryInterface(const struct _GUID &iid,void ** ppv)
{
 *ppv=this;
 return S_OK;
}
ULONG __stdcall CMyEventSink::AddRef(void)
{ return 1; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的
ULONG __stdcall CMyEventSink::Release(void)
{ return 0; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的
STDMETHODIMP CMyEventSink::GetTypeInfoCount(unsigned int *)
{ return E_NOTIMPL; } // 不用实现,反正也不用
STDMETHODIMP CMyEventSink::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )
{ return E_NOTIMPL; } // 不用实现,反正也不用
STDMETHODIMP CMyEventSink::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *)
{ return E_NOTIMPL; } // 不用实现,反正也不用
#define DISPID_CLICK                    (-600)
#define DISPID_DBLCLICK                 (-601)
#define DISPID_KEYDOWN                  (-602)
#define DISPID_KEYPRESS                 (-603)
#define DISPID_KEYUP                    (-604)
#define DISPID_MOUSEDOWN                (-605)
#define DISPID_MOUSEMOVE                (-606)
#define DISPID_MOUSEUP                  (-607)
#define DISPID_ERROREVENT               (-608)
#define DISPID_READYSTATECHANGE         (-609)
#define DISPID_CLICK_VALUE              (-610)
#define DISPID_RIGHTTOLEFT              (-611)
#define DISPID_TOPTOBOTTOM              (-612)
#define DISPID_THIS                     (-613)
STDMETHODIMP CMyEventSink::Invoke(
 long dispID,
 const struct _GUID &,
 unsigned long,
 unsigned short,
 struct tagDISPPARAMS * pParams,
 struct tagVARIANT *,
 struct tagEXCEPINFO *,
 unsigned int *)
{  // 只需要实现这个就足够啦
 switch(dispID) // 根据不同的dispID,完成不同的回调函数
 {
 case 1:
   // 这里就能接收到 COM 发出的事件啦
  break;
 case 2:
   // 事件的代号 dispID 其实就是 IDL 文件中的连接点函数的id(n)的号码
  break;
 case DISPID_CLICK:
  AfxMessageBox("click event");
 default: break;
 }
 return S_OK;
}

如果从CCmdTarget继承:
注意添加消息处理映射:
BEGIN_DISPATCH_MAP(CDHTMLEventSink, CCmdTarget)
 //{{AFX_DISPATCH_MAP(CDHTMLEventSink)
  // NOTE - the ClassWizard will add and remove mapping macros here.
 DISP_FUNCTION_ID(CDHTMLEventSink,"onclick",DISPID_CLICK,OnClick,VT_EMPTY, VTS_DISPATCH)
 //}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
消息处理函数:
void CDHTMLEventSink::OnClick(DISPID pEvtObj)
{
    // 鼠标点击处理代码
    AfxMessageBox("onc  lick");
}

样例下载


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值