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

 

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

1、屏蔽错误信息提示

1m_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,这样就可以得到错误信息了。

01IOleCommandTarget : public IUnknown
02{
03public:
04virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE QueryStatus(
05/* [unique][in] */ __RPC__in_opt const GUID *pguidCmdGroup,
06/* [in] */ ULONG cCmds,
07/* [out][in][size_is] */ __RPC__inout_ecount_full(cCmds) OLECMD prgCmds[ ],
08/* [unique][out][in] */ __RPC__inout_opt OLECMDTEXT *pCmdText) = 0;
09
10virtual 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};
18

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

01class CMyControlSite : public COleControlSite
02{
03 
04public:
05CMyControlSite(COleControlContainer *pCntr):COleControlSite(pCntr) {}
06 
07protected:
08 
09DECLARE_INTERFACE_MAP()
10BEGIN_INTERFACE_PART(OleCommandTarget, IOleCommandTarget)
11STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText);
12STDMETHOD(Exec)(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut);
13END_INTERFACE_PART(OleCommandTarget)

MFC提供了很多宏用于简化COM相关功能的开发,对COM接口的实现方式在MFC中具体体现方式是内嵌类,背后的设计思想是COM聚合,每个接口都产生一个内嵌类,所有的接口都聚合到外层的类。DECLARE_INTERFACE_MAP() 实际上就是定义一个数组以及查询操作,BEGIN_INTERFACE_PART定义一个命名为XOleCommandTarget的内嵌类(内嵌类的命名规则是XName)并定义IUnknown接口的三个方法AddRefReleaseQueryInterfaceEND_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的类实现代码

01BEGIN_INTERFACE_MAP(CMyControlSite, COleControlSite)
02INTERFACE_PART(CMyControlSite, IID_IOleCommandTarget, OleCommandTarget)
03END_INTERFACE_MAP()
04 
05 
06HRESULT CMyControlSite::XOleCommandTarget::Exec
07(const GUID* pguidCmdGroup, DWORD nCmdID,
08DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut )
09{
10HRESULT hr = OLECMDERR_E_NOTSUPPORTED;
11//return S_OK;
12if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CGID_DocHostCommandHandler))
13{
14 
15switch (nCmdID)
16{
17 
18case OLECMDID_SHOWSCRIPTERROR:
19{
20IHTMLDocument2* pDoc = NULL;
21IHTMLWindow2* pWindow = NULL;
22IHTMLEventObj* pEventObj = NULL;
23BSTR rgwszNames[5] =
24{
25SysAllocString(L"errLine"),
26SysAllocString(L"errCharacter"),
27SysAllocString(L"errCode"),
28SysAllocString(L"errMsg"),
29SysAllocString(L"errUrl")
30};
31DISPID rgDispIDs[5];
32VARIANT rgvaEventInfo[5];
33DISPPARAMS params;
34BOOL fContinueRunningScripts = true;
35 
36params.cArgs = 0;
37params.cNamedArgs = 0;
38
39hr = pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2, (void **) &pDoc);
40
41hr = pDoc->get_parentWindow(&pWindow);
42pDoc->Release();
43
44hr = pWindow->get_event(&pEventObj);
45
46for (int i = 0; i < 5; i++)
47{
48
49hr = pEventObj->GetIDsOfNames(IID_NULL, &rgwszNames[i], 1,
50LOCALE_SYSTEM_DEFAULT, &rgDispIDs[i]);
51
52hr = pEventObj->Invoke(rgDispIDs[i], IID_NULL,
53LOCALE_SYSTEM_DEFAULT,
54DISPATCH_PROPERTYGET, ¶ms, &rgvaEventInfo[i],
55NULL, NULL);
56//可以在此记录错误信息
01//必须使用SysFreeString来释放SysAllocString分配的内存,SysAllocString在分配的内存中记录了字符的长度
02SysFreeString(rgwszNames[i]);
03}
04 
05// At this point, you would normally alert the user with
06// the information about the error, which is now contained
07// in rgvaEventInfo[]. Or, you could just exit silently.
08 
09(*pvaOut).vt = VT_BOOL;
10if (fContinueRunningScripts)
11{
12// 在页面中继续执行脚本
13(*pvaOut).boolVal = VARIANT_TRUE;
14}
15else
16{
17// 停止在页面中执行脚本
18(*pvaOut).boolVal = VARIANT_FALSE;
19}
20break;
21}
22default:
23hr =OLECMDERR_E_NOTSUPPORTED;
24break;
25}
26}
27else
28{
29hr = OLECMDERR_E_UNKNOWNGROUP;
30}
31return (hr);
32}
33 
34 
35ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::AddRef()
36{
37METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)
38return pThis->ExternalAddRef();
39}
40 
41 
42ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::Release()
43{
44METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)
45return pThis->ExternalRelease();
46}
47 
48HRESULT FAR EXPORT CMyControlSite::XOleCommandTarget::QueryInterface(REFIID riid, void **ppvObj)
49{
50METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)
51HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);
52return hr;
53}
54 
55STDMETHODIMP CMyControlSite::XOleCommandTarget::QueryStatus(
56/* [unique][in] */ const GUID __RPC_FAR *pguidCmdGroup,
57/* [in] */ ULONG cCmds,
58/* [out][in][size_is] */ OLECMD __RPC_FAR prgCmds[ ],
59/* [unique][out][in] */ OLECMDTEXT __RPC_FAR *pCmdText
60)
61{
62METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)
63return OLECMDERR_E_NOTSUPPORTED;
64}

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

01virtual BOOL CreateControlSite(COleControlContainer* pContainer,
02COleControlSite** ppSite, UINT nID , REFCLSID clsid )
03{
04if(ppSite == NULL)
05{
06ASSERT(FALSE);
07return FALSE;
08}
09 
10CMyControlSite *pBrowserSite =
11new CMyControlSite (pContainer, this);
12if (!pBrowserSite)
13return FALSE;
14 
15*ppSite = pBrowserSite;
16return TRUE;
17}

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

1public:
1CMyControlSite(COleControlContainer *pCnt, CDHtmlDialog *pHandler):COleControlSite(pCnt),m_pHandler(pHandler) {}
1protected:
1CDHtmlDialog *m_pHandler;<br>
01BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler)
02STDMETHOD(ShowContextMenu)(DWORD, LPPOINT, LPUNKNOWN, LPDISPATCH);
03STDMETHOD(GetHostInfo)(DOCHOSTUIINFO*);
04STDMETHOD(ShowUI)(DWORD, LPOLEINPLACEACTIVEOBJECT,
05LPOLECOMMANDTARGET, LPOLEINPLACEFRAME, LPOLEINPLACEUIWINDOW);
06STDMETHOD(HideUI)(void);
07STDMETHOD(UpdateUI)(void);
08STDMETHOD(EnableModeless)(BOOL);
09STDMETHOD(OnDocWindowActivate)(BOOL);
10STDMETHOD(OnFrameWindowActivate)(BOOL);
11STDMETHOD(ResizeBorder)(LPCRECT, LPOLEINPLACEUIWINDOW, BOOL);
12STDMETHOD(TranslateAccelerator)(LPMSG, const GUID*, DWORD);
13STDMETHOD(GetOptionKeyPath)(OLECHAR **, DWORD);
14STDMETHOD(GetDropTarget)(LPDROPTARGET, LPDROPTARGET*);
15STDMETHOD(GetExternal)(LPDISPATCH*);
16STDMETHOD(TranslateUrl)(DWORD, OLECHAR*, OLECHAR **);
17STDMETHOD(FilterDataObject)(LPDATAOBJECT , LPDATAOBJECT*);
18END_INTERFACE_PART(DocHostUIHandler)

以下是实现代码

001BEGIN_INTERFACE_MAP(CMyControlSite, COleControlSite)
002INTERFACE_PART(CMyControlSite, IID_IDocHostUIHandler, DocHostUIHandler)
003INTERFACE_PART(CMyControlSite, IID_IOleCommandTarget, OleCommandTarget)
004END_INTERFACE_MAP()
005 
006STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetExternal(LPDISPATCH *lppDispatch)
007{
008METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
009return pThis->m_pHandler->GetExternal(lppDispatch);
010}
011 
012 
013 
014STDMETHODIMP CMyControlSite::XDocHostUIHandler::ShowContextMenu(
015DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdTarget, LPDISPATCH pdispReserved)
016{
017METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
018return pThis->m_pHandler->ShowContextMenu(dwID, ppt, pcmdTarget, pdispReserved);
019}
020 
021STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetHostInfo(
022DOCHOSTUIINFO *pInfo)
023{
024METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
025return pThis->m_pHandler->GetHostInfo(pInfo);
026}
027 
028 
029STDMETHODIMP CMyControlSite::XDocHostUIHandler::ShowUI(
030DWORD dwID, LPOLEINPLACEACTIVEOBJECT pActiveObject,
031LPOLECOMMANDTARGET pCommandTarget, LPOLEINPLACEFRAME pFrame,
032LPOLEINPLACEUIWINDOW pDoc)
033{
034METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
035return pThis->m_pHandler->ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc);
036}
037 
038STDMETHODIMP CMyControlSite::XDocHostUIHandler::HideUI(void)
039{
040METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
041return pThis->m_pHandler->HideUI();
042}
043 
044STDMETHODIMP CMyControlSite::XDocHostUIHandler::UpdateUI(void)
045{
046METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
047return pThis->m_pHandler->UpdateUI();
048}
049 
050 
051STDMETHODIMP CMyControlSite::XDocHostUIHandler::EnableModeless(BOOL fEnable)
052{
053METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
054return pThis->m_pHandler->EnableModeless(fEnable);
055}
056 
057STDMETHODIMP CMyControlSite::XDocHostUIHandler::OnDocWindowActivate(BOOL fActivate)
058{
059METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
060return pThis->m_pHandler->OnDocWindowActivate(fActivate);
061}
062 
063STDMETHODIMP CMyControlSite::XDocHostUIHandler::OnFrameWindowActivate(
064BOOL fActivate)
065{
066METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
067return pThis->m_pHandler->OnFrameWindowActivate(fActivate);
068}
069 
070STDMETHODIMP CMyControlSite::XDocHostUIHandler::ResizeBorder(
071LPCRECT prcBorder, LPOLEINPLACEUIWINDOW pUIWindow, BOOL fFrameWindow)
072{
073METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
074return pThis->m_pHandler->ResizeBorder(prcBorder, pUIWindow, fFrameWindow);
075}
076 
077STDMETHODIMP CMyControlSite::XDocHostUIHandler::TranslateAccelerator(
078LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID)
079{
080METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
081return pThis->m_pHandler->TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID);
082}
083 
084 
085STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetOptionKeyPath(
086LPOLESTR* pchKey, DWORD dwReserved)
087{
088METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
089return pThis->m_pHandler->GetOptionKeyPath(pchKey, dwReserved);
090}
091 
092 
093STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetDropTarget(
094LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget)
095{
096METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
097return pThis->m_pHandler->GetDropTarget(pDropTarget, ppDropTarget);
098}
099 
100STDMETHODIMP CMyControlSite::XDocHostUIHandler::TranslateUrl(
101DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut)
102{
103METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
104return pThis->m_pHandler->TranslateUrl(dwTranslate, pchURLIn, ppchURLOut);
105}
106 
107STDMETHODIMP CMyControlSite::XDocHostUIHandler::FilterDataObject(
108LPDATAOBJECT pDataObject, LPDATAOBJECT* ppDataObject)
109{
110METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
111return pThis->m_pHandler->FilterDataObject(pDataObject, ppDataObject);
112}
113 
114 
115STDMETHODIMP_(ULONG) CMyControlSite::XDocHostUIHandler::AddRef()
116{
117METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
118return pThis->ExternalAddRef();
119}
120 
121STDMETHODIMP_(ULONG) CMyControlSite::XDocHostUIHandler::Release()
122{
123METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
124return pThis->ExternalRelease();
125}
126 
127STDMETHODIMP CMyControlSite::XDocHostUIHandler::QueryInterface(
128REFIID iid, LPVOID far* ppvObj)
129{
130METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler)
131return pThis->ExternalQueryInterface(&iid, ppvObj);
132}

STDMETHODIMP宏的定义是HRESULT __stdcallSTDMETHODIMP_宏指定了返回值,这两个宏用在cpp实现文件中,对应用于声明时使用的STDMETHODSTDMETHOD_

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

以上代码基于VS2008,由于不同版本的VS所带的MFC库版本不尽一致所以需要根本具体的版本来处理,目前已知的不同部分主要集中在CreateControlSite上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值