• CDHtmlDialog探索----WebBrowser扩展和网页Javascript错误处理


    当WebBrowser控件(CDHtmlDialog自动创建了WebBrowser控件)加载的网页中含有错误Javascript代码时默认情况下控件会弹出错误信息提示对话框,相对于用户体验来说这样的提示完全不是开发人员想要的,针对这个问题有两个解决方案,一是完全屏蔽掉错误提示,二是控制错误的提示并且记录错误信息同时也可以控制出现错误后Javascript是否继续执行。

    1、屏蔽错误信息提示

    1
    m_pBrowserApp->put_Silent(VARIANT_TRUE);

    在CDHtmlDialog::OnInitDialog()的代码中首先了创建WebBrowser控件,然后把控件的Browser对象赋值给m_pBrowserApp(这是CDHtmlDialog完成的不需要自己处理)。WebBrowser的put_Silent函数在官方给出的说明是禁用所有的对话框,但例外情况是它不会影响SSL安全认证需要的进示对话框。绝大多数情况下这就可以解决问题了,记得很久以前我遇到过一种情况就是虽然调用了put_Silent但是还是有极个别的js错误是无法屏蔽掉的依然会显示出来(在网页含有嵌套页面时会错误无法屏蔽,不知道是否还有其它情况),现在找不到这样的网页了,如果谁遇到这种情况了建议给我发上个URL让我也重温一下当年阳光灿烂的时刻。

    2、控制错误提示并进行记录

      这要比第一种方法复杂上许多,简短的来说就是自定义COleControlSite类并实现IOleCommandTarget接口,IOleCommandTarget接口是错误控制的关健,错误发生时会触发此接口的Exec函数并为nCmdID参数赋值为OLECMDID_SHOWSCRIPTERROR,这样就可以得到错误信息了。

     1 IOleCommandTarget : public IUnknown 
     2     { 
     3     public: 
     4         virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE QueryStatus(  
     5             /* [unique][in] */ __RPC__in_opt const GUID *pguidCmdGroup, 
     6             /* [in] */ ULONG cCmds, 
     7             /* [out][in][size_is] */ __RPC__inout_ecount_full(cCmds) OLECMD prgCmds[  ], 
     8             /* [unique][out][in] */ __RPC__inout_opt OLECMDTEXT *pCmdText) = 0; 
     9           
    10         virtual HRESULT STDMETHODCALLTYPE Exec(  
    11             /* [unique][in] */ __RPC__in_opt const GUID *pguidCmdGroup, 
    12             /* [in] */ DWORD nCmdID, 
    13             /* [in] */ DWORD nCmdexecopt, 
    14             /* [unique][in] */ __RPC__in_opt VARIANT *pvaIn, 
    15             /* [unique][out][in] */ __RPC__inout_opt VARIANT *pvaOut) = 0; 
    16           
    17     };  

    现在我们开始实现自定义的COleControlSite

     1 class CMyControlSite : public COleControlSite  
     2 {   
     3   
     4 public:   
     5     CMyControlSite(COleControlContainer *pCntr):COleControlSite(pCntr) {} 
     6   
     7 protected:   
     8   
     9     DECLARE_INTERFACE_MAP()   
    10     BEGIN_INTERFACE_PART(OleCommandTarget, IOleCommandTarget)   
    11         STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText);   
    12         STDMETHOD(Exec)(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut);   
    13     END_INTERFACE_PART(OleCommandTarget)

    MFC提供了很多宏用于简化COM相关功能的开发,对COM接口的实现方式在MFC中具体体现方式是内嵌类,背后的设计思想是COM聚合,每个接口都产生一个内嵌类,所有的接口都聚合到外层的类。DECLARE_INTERFACE_MAP()  实际上就是定义一个数组以及查询操作,BEGIN_INTERFACE_PART定义一个命名为XOleCommandTarget的内嵌类(内嵌类的命名规则是XName)并定义IUnknown接口的三个方法AddRef、Release、QueryInterface。END_INTERFACE_PART定义一个m_xOleCommandTarget的成员类型为XOleCommandTarget(定义的成员命名规则就是m_xName),并把XOleCommandTarget类声明为外层类的友元类在示例中外层类指CMyControlSite。

    STDMETHOD宏定义为virtual __declspec(nothrow) HRESULT __stdcall,该宏定义函数为虚函数返回值为HRESULT,函数调用约定为__stdcall,并且在此函数上禁止异常。__declspec(nothrow)用定告诉编译器它修饰的函数以及此函数调用的函数不会产生C++异常调用从可以优化代码性能和代码尺寸(默认情况下C++编译器为了进行异常处理会在拥有throw调用的函数中自动生成相关的异常处理代码)。通常情况下HRESULT返回值就表达了错误信息,HRESULT是个32位值,不同的位域用于不同的目的,也可以使用自定义的位域,具体的信息可以参考http://en.wikipedia.org/wiki/HRESULT。由于COM本身的语言中立性所以不应该在COM组件对外公布的信息中掺杂特定语言相关的特性。如果需要提供更详尽的错误信息那么应该实现COM的IErrorInfo接口。言归正传以下是CMyControlSite的类实现代码

     BEGIN_INTERFACE_MAP(CMyControlSite, COleControlSite)   INTERFACE_PART(CMyControlSite, IID_IOleCommandTarget, OleCommandTarget)

      1 <EM id=__mceDel><EM id=__mceDel>END_INTERFACE_MAP()   
      2   
      3   
      4 HRESULT CMyControlSite::XOleCommandTarget::Exec   
      5 (const GUID* pguidCmdGroup, DWORD nCmdID,   
      6  DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut )   
      7 {   
      8     HRESULT hr = OLECMDERR_E_NOTSUPPORTED;   
      9     //return S_OK;   
     10     if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CGID_DocHostCommandHandler))   
     11     {   
     12   
     13         switch (nCmdID)    
     14         {   
     15   
     16         case OLECMDID_SHOWSCRIPTERROR:   
     17             {   
     18                 IHTMLDocument2*             pDoc = NULL;   
     19                 IHTMLWindow2*               pWindow = NULL;   
     20                 IHTMLEventObj*              pEventObj = NULL;   
     21                 BSTR                        rgwszNames[5] =    
     22                 {    
     23                     <SPAN>SysAllocString(L"errorLine"), </SPAN><BR><SPAN>SysAllocString(L"errorCharacter"), </SPAN><BR><SPAN>SysAllocString(L"errorCode"), </SPAN><BR><SPAN>SysAllocString(L"errorMessage"), </SPAN><BR><SPAN>SysAllocString(L"errorUrl")</SPAN> 
     24                 };   
     25                 DISPID                      rgDispIDs[5];   
     26                 VARIANT                     rgvaEventInfo[5];   
     27                 DISPPARAMS                  params;   
     28                 BOOL                        fContinueRunningScripts = true;   
     29   
     30                 params.cArgs = 0;   
     31                 params.cNamedArgs = 0;   
     32                   
     33                 hr = pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2, (void **) &pDoc);       
     34                    
     35                 hr = pDoc->get_parentWindow(&pWindow);   
     36                 pDoc->Release();   
     37                   
     38                 hr = pWindow->get_event(&pEventObj);   
     39                   
     40                 for (int i = 0; i < 5; i++)    
     41                 {     
     42                       
     43                     hr = pEventObj->GetIDsOfNames(IID_NULL, &rgwszNames[i], 1,    
     44                         LOCALE_SYSTEM_DEFAULT, &rgDispIDs[i]);   
     45                   
     46                     hr = pEventObj->Invoke(rgDispIDs[i], IID_NULL,   
     47                         LOCALE_SYSTEM_DEFAULT,   
     48                         DISPATCH_PROPERTYGET, ¶ms, &rgvaEventInfo[i],   
     49                         NULL, NULL);   
     50                     //可以在此记录错误信息</EM></EM> 
     51 
     52 
     53      //必须使用SysFreeString来释放SysAllocString分配的内存,SysAllocString在分配的内存中记录了字符的长度 
     54                     SysFreeString(rgwszNames[i]);   
     55                 }   
     56   
     57                 // At this point, you would normally alert the user with    
     58                 // the information about the error, which is now contained   
     59                 // in rgvaEventInfo[]. Or, you could just exit silently.   
     60   
     61                 (*pvaOut).vt = VT_BOOL;   
     62                 if (fContinueRunningScripts)   
     63                 {   
     64                     // 在页面中继续执行脚本  
     65                     (*pvaOut).boolVal = VARIANT_TRUE;   
     66                 }   
     67                 else 
     68                 {   
     69                     // 停止在页面中执行脚本   
     70                     (*pvaOut).boolVal = VARIANT_FALSE;      
     71                 }    
     72                 break;   
     73             }   
     74         default:   
     75             hr =OLECMDERR_E_NOTSUPPORTED;  
     76             break;   
     77         }   
     78     }   
     79     else 
     80     {   
     81         hr = OLECMDERR_E_UNKNOWNGROUP; 
     82     }   
     83     return (hr);   
     84 }   
     85   
     86   
     87 ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::AddRef()    
     88 {    
     89     METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)    
     90         return pThis->ExternalAddRef();    
     91 }    
     92   
     93   
     94 ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::Release()    
     95 {    
     96     METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)    
     97         return pThis->ExternalRelease();    
     98 }    
     99   
    100 HRESULT FAR EXPORT CMyControlSite::XOleCommandTarget::QueryInterface(REFIID riid, void **ppvObj)    
    101 {    
    102     METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)    
    103         HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);    
    104     return hr;    
    105 }   
    106   
    107 STDMETHODIMP CMyControlSite::XOleCommandTarget::QueryStatus(    
    108     /* [unique][in] */ const GUID __RPC_FAR *pguidCmdGroup,    
    109     /* [in] */ ULONG cCmds,    
    110     /* [out][in][size_is] */ OLECMD __RPC_FAR prgCmds[ ],    
    111     /* [unique][out][in] */ OLECMDTEXT __RPC_FAR *pCmdText    
    112     )    
    113 {    
    114     METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)    
    115         return OLECMDERR_E_NOTSUPPORTED;    
    116 } 

    实现CMyControlSite后需要应用到CDHtmlDialog上,重写CreateControlSite虚函数既可

     1 virtual BOOL CreateControlSite(COleControlContainer* pContainer,  
     2         COleControlSite** ppSite, UINT  nID , REFCLSID  clsid ) 
     3 { 
     4         if(ppSite == NULL) 
     5     { 
     6         ASSERT(FALSE); 
     7         return FALSE; 
     8     } 
     9   
    10     CMyControlSite *pBrowserSite =  
    11         new CMyControlSite (pContainer, this); 
    12     if (!pBrowserSite) 
    13         return FALSE; 
    14   
    15     *ppSite = pBrowserSite; 
    16     return TRUE; 
    17 } 

    现在就可以去编译测试了。到目前还有一个问题没有考虑,如果这段代码整合到现有的CDHtmlDialog应用中而现有的代码使用了其它默认的设定比如说自定义WebBrowser的右健菜单或使用了GetIDispatch函数等情况下原有的代码就不能正常工作了。这部分功能是在MFC的CBrowserControlSite类中处理的,所以CMyControlSite应该把CBrowserControlSite的功能也实现一遍以使CDHtmlDialog的原有封装性不被破坏。在CMyControlSite中实现IDocHostUIHandler接口既可完成此功能。代码声明如下

     1 public: 
     2 1   CMyControlSite(COleControlContainer *pCnt, CDHtmlDialog *pHandler):COleControlSite(pCnt),m_pHandler(pHandler) {} 
     3 1 protected: 
     4 ?
     5 1   CDHtmlDialog *m_pHandler; 
     6 
     7 
     8 
     9 16
    10 17
    11 18 BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler) 
    12         STDMETHOD(ShowContextMenu)(DWORD, LPPOINT, LPUNKNOWN, LPDISPATCH); 
    13         STDMETHOD(GetHostInfo)(DOCHOSTUIINFO*); 
    14         STDMETHOD(ShowUI)(DWORD, LPOLEINPLACEACTIVEOBJECT, 
    15             LPOLECOMMANDTARGET, LPOLEINPLACEFRAME, LPOLEINPLACEUIWINDOW); 
    16         STDMETHOD(HideUI)(void); 
    17         STDMETHOD(UpdateUI)(void); 
    18         STDMETHOD(EnableModeless)(BOOL); 
    19         STDMETHOD(OnDocWindowActivate)(BOOL); 
    20         STDMETHOD(OnFrameWindowActivate)(BOOL); 
    21         STDMETHOD(ResizeBorder)(LPCRECT, LPOLEINPLACEUIWINDOW, BOOL); 
    22         STDMETHOD(TranslateAccelerator)(LPMSG, const GUID*, DWORD); 
    23         STDMETHOD(GetOptionKeyPath)(OLECHAR **, DWORD); 
    24         STDMETHOD(GetDropTarget)(LPDROPTARGET, LPDROPTARGET*); 
    25         STDMETHOD(GetExternal)(LPDISPATCH*); 
    26         STDMETHOD(TranslateUrl)(DWORD, OLECHAR*, OLECHAR **); 
    27         STDMETHOD(FilterDataObject)(LPDATAOBJECT , LPDATAOBJECT*); 
    28     END_INTERFACE_PART(DocHostUIHandler) 

    以下是实现代码

      1 BEGIN_INTERFACE_MAP(CMyControlSite, COleControlSite)   
      2     INTERFACE_PART(CMyControlSite, IID_IDocHostUIHandler, DocHostUIHandler) 
      3     INTERFACE_PART(CMyControlSite, IID_IOleCommandTarget, OleCommandTarget) 
      4 END_INTERFACE_MAP()   
      5   
      6 STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetExternal(LPDISPATCH *lppDispatch) 
      7 { 
      8     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
      9     return pThis->m_pHandler->GetExternal(lppDispatch); 
     10 } 
     11   
     12   
     13   
     14 STDMETHODIMP CMyControlSite::XDocHostUIHandler::ShowContextMenu( 
     15     DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdTarget, LPDISPATCH pdispReserved) 
     16 { 
     17     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     18     return pThis->m_pHandler->ShowContextMenu(dwID, ppt, pcmdTarget, pdispReserved); 
     19 } 
     20   
     21 STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetHostInfo( 
     22     DOCHOSTUIINFO *pInfo) 
     23 { 
     24     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     25     return pThis->m_pHandler->GetHostInfo(pInfo); 
     26 } 
     27   
     28   
     29 STDMETHODIMP CMyControlSite::XDocHostUIHandler::ShowUI( 
     30     DWORD dwID, LPOLEINPLACEACTIVEOBJECT pActiveObject, 
     31     LPOLECOMMANDTARGET pCommandTarget, LPOLEINPLACEFRAME pFrame, 
     32     LPOLEINPLACEUIWINDOW pDoc) 
     33 { 
     34     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     35     return pThis->m_pHandler->ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc); 
     36 } 
     37   
     38 STDMETHODIMP CMyControlSite::XDocHostUIHandler::HideUI(void) 
     39 { 
     40     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     41     return pThis->m_pHandler->HideUI(); 
     42 } 
     43   
     44 STDMETHODIMP CMyControlSite::XDocHostUIHandler::UpdateUI(void) 
     45 { 
     46     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     47     return pThis->m_pHandler->UpdateUI(); 
     48 } 
     49   
     50   
     51 STDMETHODIMP CMyControlSite::XDocHostUIHandler::EnableModeless(BOOL fEnable) 
     52 { 
     53     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     54     return pThis->m_pHandler->EnableModeless(fEnable); 
     55 } 
     56   
     57 STDMETHODIMP CMyControlSite::XDocHostUIHandler::OnDocWindowActivate(BOOL fActivate) 
     58 { 
     59     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     60     return pThis->m_pHandler->OnDocWindowActivate(fActivate); 
     61 } 
     62   
     63 STDMETHODIMP CMyControlSite::XDocHostUIHandler::OnFrameWindowActivate( 
     64     BOOL fActivate) 
     65 { 
     66     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     67     return pThis->m_pHandler->OnFrameWindowActivate(fActivate); 
     68 } 
     69   
     70 STDMETHODIMP CMyControlSite::XDocHostUIHandler::ResizeBorder( 
     71     LPCRECT prcBorder, LPOLEINPLACEUIWINDOW pUIWindow, BOOL fFrameWindow) 
     72 { 
     73     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     74     return pThis->m_pHandler->ResizeBorder(prcBorder, pUIWindow, fFrameWindow); 
     75 } 
     76   
     77 STDMETHODIMP CMyControlSite::XDocHostUIHandler::TranslateAccelerator( 
     78     LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID) 
     79 { 
     80     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     81     return pThis->m_pHandler->TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID); 
     82 } 
     83   
     84   
     85 STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetOptionKeyPath( 
     86     LPOLESTR* pchKey, DWORD dwReserved) 
     87 { 
     88     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     89     return pThis->m_pHandler->GetOptionKeyPath(pchKey, dwReserved); 
     90 } 
     91   
     92   
     93 STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetDropTarget( 
     94     LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget) 
     95 { 
     96     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
     97     return pThis->m_pHandler->GetDropTarget(pDropTarget, ppDropTarget); 
     98 } 
     99   
    100 STDMETHODIMP CMyControlSite::XDocHostUIHandler::TranslateUrl( 
    101     DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut) 
    102 { 
    103     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
    104     return pThis->m_pHandler->TranslateUrl(dwTranslate, pchURLIn, ppchURLOut); 
    105 } 
    106   
    107 STDMETHODIMP CMyControlSite::XDocHostUIHandler::FilterDataObject( 
    108     LPDATAOBJECT pDataObject, LPDATAOBJECT* ppDataObject) 
    109 { 
    110     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
    111     return pThis->m_pHandler->FilterDataObject(pDataObject, ppDataObject); 
    112 } 
    113   
    114   
    115 STDMETHODIMP_(ULONG) CMyControlSite::XDocHostUIHandler::AddRef() 
    116 { 
    117     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
    118         return pThis->ExternalAddRef(); 
    119 } 
    120   
    121 STDMETHODIMP_(ULONG) CMyControlSite::XDocHostUIHandler::Release() 
    122 { 
    123     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
    124         return pThis->ExternalRelease(); 
    125 } 
    126   
    127 STDMETHODIMP CMyControlSite::XDocHostUIHandler::QueryInterface( 
    128     REFIID iid, LPVOID far* ppvObj)      
    129 { 
    130     METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) 
    131         return pThis->ExternalQueryInterface(&iid, ppvObj); 
    132 } 

    STDMETHODIMP宏的定义是HRESULT __stdcall,STDMETHODIMP_宏指定了返回值,这两个宏用在cpp实现文件中,对应用于声明时使用的STDMETHOD和STDMETHOD_。

    METHOD_PROLOGUE_EX_宏定义了pThis指针来指向外层类。

  • 相关阅读:
    [题解] LuoguP1587 [NOI2016]循环之美
    [题解] LuoguP3705 [SDOI2017]新生舞会
    [题解] LuoguP3702 [SDOI2017]序列计数
    [题解] LuoguP6476 [NOI Online 2 提高组]涂色游戏
    [题解] LuoguP4240 毒瘤之神的考验
    [题解] LuoguP6156简单题
    [题解] LuoguP6055 [RC-02] GCD
    [题解] LuoguP5050 【模板】多项式多点求值
    AtCoder Grand Contest 028题解
    Codeforces Round #421 (Div. 1) 题解
  • 原文地址:https://www.cnblogs.com/yangxx-1990/p/4885539.html
Copyright © 2020-2023  润新知