原本想暂时将BHO放放,结果发现了IDispEventImpl 的基类 IDispEventSimpleImpl,原来真正牛逼的方法都在这里呢。
0 #include <mshtmdid.h> // DISPID_HTMLDOCUMENTEVENTS2_ONCLICK
1 新添加两种事件捕获,因为注册的基类有多个类有相同的方法,调用的时候需要指明,最好用typedef方便书写
class ATL_NO_VTABLE CMyRock : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMyRock, &CLSID_MyRock>, public IObjectWithSiteImpl<CMyRock>, public IDispatchImpl<IMyRock, &IID_IMyRock, &LIBID_RockBHOLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, public IDispEventImpl<1, CMyRock, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>, public IDispEventImpl<2, CMyRock, &DIID_HTMLDocumentEvents2, &LIBID_MSHTML, 4, 0>, public IDispEventImpl<3, CMyRock, &DIID_HTMLWindowEvents2, &LIBID_MSHTML, 4, 0> { protected: typedef IDispEventImpl<1, CMyRock, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>BrowerEvents; typedef IDispEventImpl<2, CMyRock, &DIID_HTMLDocumentEvents2, &LIBID_MSHTML, 4, 0>DocumentEvents; typedef IDispEventImpl<3, CMyRock, &DIID_HTMLWindowEvents2, &LIBID_MSHTML, 4, 0>WindowEvents;
2 注册回调时要指明哪个基类
BEGIN_SINK_MAP(CMyRock) SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, OnBeforeNavigate2) // 网页打开之前时调用 SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete) // 网页打开完毕时调用 SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_ONQUIT, OnQuit) // 窗口关闭时调用 SINK_ENTRY_EX(2, DIID_HTMLDocumentEvents2, DISPID_HTMLDOCUMENTEVENTS2_ONCLICK, OnMouseClick) // 点击鼠标时调用 SINK_ENTRY_EX(2, DIID_HTMLDocumentEvents2, DISPID_HTMLDOCUMENTEVENTS2_ONKEYPRESS, OnKeyPress) // 按键盘时调用 END_SINK_MAP()
3 定义注册需要的参数
CComPtr<IWebBrowser2> m_spWebBrowser; CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> m_spDoc;
4 DispEventUnadvise时要指明基类,注意DispEventUnadvise,重复DispEventUnadvise会出错
STDMETHODIMP CMyRock::SetSite(IUnknown* pUnkSite) { if (pUnkSite != NULL) { CComPtr<IServiceProvider> spSP; HRESULT hr = pUnkSite->QueryInterface(&spSP); if(SUCCEEDED(hr) && spSP) hr = spSP->QueryService(IID_IWebBrowserApp, &m_spWebBrowser); if (SUCCEEDED(hr)) { // 注册以从 DWebBrowserEvents2 中汇集事件。 hr = BrowerEvents::DispEventAdvise(m_spWebBrowser); if (SUCCEEDED(hr)) { m_fAdvised = TRUE; } } } else { // 取消注册事件 if (m_fAdvised) { BrowerEvents::DispEventUnadvise(m_spWebBrowser); m_fAdvised = FALSE; } // 在此释放缓存的指针和其他资源。 m_spWebBrowser.Release(); } // 调用基类实现。 return IObjectWithSiteImpl<CMyRock>::SetSite(pUnkSite); } void STDMETHODCALLTYPE CMyRock::OnQuit() { if(m_fAdvisedDoc) { // m_spDoc是指针的拷贝,这里不用Release DocumentEvents::DispEventUnadvise(m_spDoc); m_fAdvisedDoc = FALSE; } DestroyDlg(); } void STDMETHODCALLTYPE CMyRock::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL) { HWND hwnd; HRESULT hr = m_spWebBrowser->get_HWND((LONG_PTR*)&hwnd); CComPtr<IDispatch> pDocument; hr = m_spWebBrowser->get_Document(&pDocument); if (SUCCEEDED(hr)) { if(m_fAdvisedDoc) { DocumentEvents::DispEventUnadvise(m_spDoc); } m_spDoc = pDocument; DocumentEvents::DispEventAdvise(m_spDoc, &DIID_HTMLDocumentEvents2); m_fAdvisedDoc = TRUE; } else m_fAdvisedDoc = FALSE; }
5 这样就可以捕获键盘/鼠标事件了
VARIANT_BOOL STDMETHODCALLTYPE OnMouseClick(IHTMLEventObj* _pEvtObj) { // IHTMLEventObj里包括了当前所有操作信息,具体按F1吧 // 如果VARIANT_FALSE将忽略此事件 return VARIANT_FALSE; } VARIANT_BOOL STDMETHODCALLTYPE OnKeyPress(IHTMLEventObj *_pEvtObj) { long t; _pEvtObj->get_keyCode(&t); t = t + 1; _pEvtObj->put_keyCode(t); return VARIANT_TRUE; }