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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
IOleCommandTarget : public IUnknown { public : virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE QueryStatus( /* [unique][in] */ __RPC__in_opt const GUID *pguidCmdGroup, /* [in] */ ULONG cCmds, /* [out][in][size_is] */ __RPC__inout_ecount_full(cCmds) OLECMD prgCmds[ ], /* [unique][out][in] */ __RPC__inout_opt OLECMDTEXT *pCmdText) = 0; virtual HRESULT STDMETHODCALLTYPE Exec( /* [unique][in] */ __RPC__in_opt const GUID *pguidCmdGroup, /* [in] */ DWORD nCmdID, /* [in] */ DWORD nCmdexecopt, /* [unique][in] */ __RPC__in_opt VARIANT *pvaIn, /* [unique][out][in] */ __RPC__inout_opt VARIANT *pvaOut) = 0; }; |
现在我们开始实现自定义的COleControlSite
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class CMyControlSite : public COleControlSite { public : CMyControlSite(COleControlContainer *pCntr):COleControlSite(pCntr) {} protected : DECLARE_INTERFACE_MAP() BEGIN_INTERFACE_PART(OleCommandTarget, IOleCommandTarget) STDMETHOD(QueryStatus)( const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText); STDMETHOD(Exec)( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut); END_INTERFACE_PART(OleCommandTarget) |
1
|
|
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
<em id= "__mceDel" ><em id= "__mceDel" >END_INTERFACE_MAP() HRESULT CMyControlSite::XOleCommandTarget::Exec ( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut ) { HRESULT hr = OLECMDERR_E_NOTSUPPORTED; //return S_OK; if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CGID_DocHostCommandHandler)) { switch (nCmdID) { case OLECMDID_SHOWSCRIPTERROR: { IHTMLDocument2* pDoc = NULL; IHTMLWindow2* pWindow = NULL; IHTMLEventObj* pEventObj = NULL; BSTR rgwszNames[5] = { <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> }; DISPID rgDispIDs[5]; VARIANT rgvaEventInfo[5]; DISPPARAMS params; BOOL fContinueRunningScripts = true ; params.cArgs = 0; params.cNamedArgs = 0; hr = pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2, ( void **) &pDoc); hr = pDoc->get_parentWindow(&pWindow); pDoc->Release(); hr = pWindow->get_event(&pEventObj); for ( int i = 0; i < 5; i++) { hr = pEventObj->GetIDsOfNames(IID_NULL, &rgwszNames[i], 1, LOCALE_SYSTEM_DEFAULT, &rgDispIDs[i]); hr = pEventObj->Invoke(rgDispIDs[i], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &rgvaEventInfo[i], NULL, NULL); //可以在此记录错误信息</em></em> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
//必须使用SysFreeString来释放SysAllocString分配的内存,SysAllocString在分配的内存中记录了字符的长度 SysFreeString(rgwszNames[i]); } // At this point, you would normally alert the user with // the information about the error, which is now contained // in rgvaEventInfo[]. Or, you could just exit silently. (*pvaOut).vt = VT_BOOL; if (fContinueRunningScripts) { // 在页面中继续执行脚本 (*pvaOut).boolVal = VARIANT_TRUE; } else { // 停止在页面中执行脚本 (*pvaOut).boolVal = VARIANT_FALSE; } break ; } default : hr =OLECMDERR_E_NOTSUPPORTED; break ; } } else { hr = OLECMDERR_E_UNKNOWNGROUP; } return (hr); } ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::AddRef() { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) return pThis->ExternalAddRef(); } ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::Release() { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) return pThis->ExternalRelease(); } HRESULT FAR EXPORT CMyControlSite::XOleCommandTarget::QueryInterface(REFIID riid, void **ppvObj) { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) HRESULT hr = ( HRESULT )pThis->ExternalQueryInterface(&riid, ppvObj); return hr; } STDMETHODIMP CMyControlSite::XOleCommandTarget::QueryStatus( /* [unique][in] */ const GUID __RPC_FAR *pguidCmdGroup, /* [in] */ ULONG cCmds, /* [out][in][size_is] */ OLECMD __RPC_FAR prgCmds[ ], /* [unique][out][in] */ OLECMDTEXT __RPC_FAR *pCmdText ) { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) return OLECMDERR_E_NOTSUPPORTED; } |
实现CMyControlSite后需要应用到CDHtmlDialog上,重写CreateControlSite虚函数既可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
virtual BOOL CreateControlSite(COleControlContainer* pContainer, COleControlSite** ppSite, UINT nID , REFCLSID clsid ) { if (ppSite == NULL) { ASSERT(FALSE); return FALSE; } CMyControlSite *pBrowserSite = new CMyControlSite (pContainer, this ); if (!pBrowserSite) return FALSE; *ppSite = pBrowserSite; return TRUE; } |
现在就可以去编译测试了。到目前还有一个问题没有考虑,如果这段代码整合到现有的CDHtmlDialog应用中而现有的代码使用了其它默认的设定比如说自定义WebBrowser的右健菜单或使用了GetIDispatch函数等情况下原有的代码就不能正常工作了。这部分功能是在MFC的CBrowserControlSite类中处理的,所以CMyControlSite应该把CBrowserControlSite的功能也实现一遍以使CDHtmlDialog的原有封装性不被破坏。在CMyControlSite中实现IDocHostUIHandler接口既可完成此功能。代码声明如下
1
|
public : |
1
|
CMyControlSite(COleControlContainer *pCnt, CDHtmlDialog *pHandler):COleControlSite(pCnt),m_pHandler(pHandler) {} |
1
|
protected : |
1
|
CDHtmlDialog *m_pHandler; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler) STDMETHOD(ShowContextMenu)( DWORD , LPPOINT, LPUNKNOWN, LPDISPATCH); STDMETHOD(GetHostInfo)(DOCHOSTUIINFO*); STDMETHOD(ShowUI)( DWORD , LPOLEINPLACEACTIVEOBJECT, LPOLECOMMANDTARGET, LPOLEINPLACEFRAME, LPOLEINPLACEUIWINDOW); STDMETHOD(HideUI)( void ); STDMETHOD(UpdateUI)( void ); STDMETHOD(EnableModeless)( BOOL ); STDMETHOD(OnDocWindowActivate)( BOOL ); STDMETHOD(OnFrameWindowActivate)( BOOL ); STDMETHOD(ResizeBorder)(LPCRECT, LPOLEINPLACEUIWINDOW, BOOL ); STDMETHOD(TranslateAccelerator)(LPMSG, const GUID*, DWORD ); STDMETHOD(GetOptionKeyPath)(OLECHAR **, DWORD ); STDMETHOD(GetDropTarget)(LPDROPTARGET, LPDROPTARGET*); STDMETHOD(GetExternal)(LPDISPATCH*); STDMETHOD(TranslateUrl)( DWORD , OLECHAR*, OLECHAR **); STDMETHOD(FilterDataObject)(LPDATAOBJECT , LPDATAOBJECT*); END_INTERFACE_PART(DocHostUIHandler) |
以下是实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
BEGIN_INTERFACE_MAP(CMyControlSite, COleControlSite) INTERFACE_PART(CMyControlSite, IID_IDocHostUIHandler, DocHostUIHandler) INTERFACE_PART(CMyControlSite, IID_IOleCommandTarget, OleCommandTarget) END_INTERFACE_MAP() STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetExternal(LPDISPATCH *lppDispatch) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->GetExternal(lppDispatch); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::ShowContextMenu( DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdTarget, LPDISPATCH pdispReserved) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->ShowContextMenu(dwID, ppt, pcmdTarget, pdispReserved); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetHostInfo( DOCHOSTUIINFO *pInfo) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->GetHostInfo(pInfo); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::ShowUI( DWORD dwID, LPOLEINPLACEACTIVEOBJECT pActiveObject, LPOLECOMMANDTARGET pCommandTarget, LPOLEINPLACEFRAME pFrame, LPOLEINPLACEUIWINDOW pDoc) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::HideUI( void ) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->HideUI(); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::UpdateUI( void ) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->UpdateUI(); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::EnableModeless( BOOL fEnable) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->EnableModeless(fEnable); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::OnDocWindowActivate( BOOL fActivate) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->OnDocWindowActivate(fActivate); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::OnFrameWindowActivate( BOOL fActivate) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->OnFrameWindowActivate(fActivate); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::ResizeBorder( LPCRECT prcBorder, LPOLEINPLACEUIWINDOW pUIWindow, BOOL fFrameWindow) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->ResizeBorder(prcBorder, pUIWindow, fFrameWindow); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::TranslateAccelerator( LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetOptionKeyPath( LPOLESTR* pchKey, DWORD dwReserved) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->GetOptionKeyPath(pchKey, dwReserved); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::GetDropTarget( LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->GetDropTarget(pDropTarget, ppDropTarget); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::TranslateUrl( DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->TranslateUrl(dwTranslate, pchURLIn, ppchURLOut); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::FilterDataObject( LPDATAOBJECT pDataObject, LPDATAOBJECT* ppDataObject) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->m_pHandler->FilterDataObject(pDataObject, ppDataObject); } STDMETHODIMP_( ULONG ) CMyControlSite::XDocHostUIHandler::AddRef() { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->ExternalAddRef(); } STDMETHODIMP_( ULONG ) CMyControlSite::XDocHostUIHandler::Release() { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->ExternalRelease(); } STDMETHODIMP CMyControlSite::XDocHostUIHandler::QueryInterface( REFIID iid, LPVOID far* ppvObj) { METHOD_PROLOGUE_EX_(CMyControlSite, DocHostUIHandler) return pThis->ExternalQueryInterface(&iid, ppvObj); } |
STDMETHODIMP宏的定义是HRESULT __stdcall,STDMETHODIMP_宏指定了返回值,这两个宏用在cpp实现文件中,对应用于声明时使用的STDMETHOD和STDMETHOD_。
METHOD_PROLOGUE_EX_宏定义了pThis指针来指向外层类。
以上代码基于VS2008,由于不同版本的VS所带的MFC库版本不尽一致所以需要根本具体的版本来处理,目前已知的不同部分主要集中在CreateControlSite上。
CDHtmlDialog探索----WebBrowser扩展和网页Javascript错误处理的更多相关文章
- CDHtmlDialog探索----Javascript与窗体交互
CDHtmlDialog提供了C++与网页的双向交互,通此一系统简单的宏调用可以把网页中各元素的事件直接映射到C++程序中,而在网页中调用C++功能代码就显的不那么直观了.归根结底交互的基理就是实现相 ...
- 【转】Javascript错误处理——try…catch
无论我们编程多么精通,脚本错误怎是难免.可能是我们的错误造成,或异常输入,错误的服务器端响应以及无数个其他原因. 通常,当发送错误时脚本会立刻停止,打印至控制台. 但try...catch语法结构可以 ...
- JavaScript错误/异常处理
JavaScript Try...Catch 语句 介绍:JavaScript中的try...carch语句的作用和C#中的try...catch语句的作用一样, 都是捕获并处理异常. 语法: try ...
- 使用 Google Analytics 跟踪 JavaScript 错误
Google Analytics(谷歌分析)不仅仅是一个流量统计工具,你还可以用它来测量广告活动的有效性,跟踪用户多远到所需的页面流(从点击广告到购物车到结账页面)获取,并基于用户的信息设置浏览器和语 ...
- javascript错误处理与调试(转)
JavaScript 在错误处理调试上一直是它的软肋,如果脚本出错,给出的提示经常也让人摸不着头脑. ECMAScript 第 3 版为了解决这个问题引入了 try...catch 和 throw 语 ...
- 第一百二十三节,JavaScript错误处理与调试
JavaScript错误处理与调试 学习要点: 1.浏览器错误报告 2.错误处理 3.错误事件 4.错误处理策略 5.调试技术 6.调试工具 JavaScript在错误处理调试上一直是它的软肋,如果脚 ...
- Visual Studio 2013中因SignalR的Browser Link引起的Javascript错误一则
众所周知Visual Studio 2013中有一个由SignalR机制实现的Browser Link功能,意思是开发人员可以同时使用多个浏览器进行调试,当按下IDE中的Browser Link按钮后 ...
- Java抓取网页数据(原网页+Javascript返回数据)
有时候由于种种原因,我们需要采集某个网站的数据,但由于不同网站对数据的显示方式略有不同! 本文就用Java给大家演示如何抓取网站的数据:(1)抓取原网页数据:(2)抓取网页Javascript返回的数 ...
- JavaScript错误处理
JavaScript 错误 - Throw.Try 和 Catch JavaScript 测试和捕捉 try 语句允许我们定义在执行时进行错误测试的代码块. catch 语句允许我们定义当 try 代 ...
随机推荐
- linux中文件多行合并为一行的例子
现网中经常遇到匹配到某一关键字下的所有行合并到同一行,再次匹配到相关关键字再和下面的合并,示例如下: # line1ab# line2cde# line3f想要变成: # line1 a b# lin ...
- ActiveMQ详细入门使用教程
ActiveMQ介绍 MQ是消息中间件,是一种在分布式系统中应用程序借以传递消息的媒介,常用的有ActiveMQ,RabbitMQ,kafka.ActiveMQ是Apache下的开源项目,完全支持JM ...
- 如何转换cdr文件
You will need to copy the type library from corelDRAW: C:\Program Files (x86)\Corel\CorelDRAW Graphi ...
- 打印流PrintStream
打印流PrintStream PrintStream extends OutputStream 1.打印流的特点 只负责数据的输出,不负责数据的读取 与其他的流不同,打印流永远不会抛出IOExcept ...
- JDK8新特性03 Lambda表达式03_Java8 内置的四大核心函数式接口
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.functio ...
- 使用 JS 实现文字上下跑马灯
Ø 前言 今天在做商城首页时,遇到一个上下跑马灯功能,因为之前也只是接触过左右的跑马灯,一时还不知道从何下手.在网上看了几个 demo,并亲自运行了一下,是可以实现的.但是,能运行不知其所以然也不行 ...
- 嫁给程序员的好处,你get到了吗?
首先,我们要知道,什么是程序员?程序员是做什么的? "程序员(英文Programmer)是从事程序开发.维护的专业人员.一般将程序员分为程序设计人员和程序编码人员,但两者的界限并不非常清楚, ...
- 多线程this逃逸
this逃逸, 是指在构造函数返回之前,其它线程就持有该对象的引用,调用尚未构造完全的对象的方法,可能引发令人疑惑的错误,应该避免this逃逸事件的发生. this逃逸经常发生在构造函数中启动线程或 ...
- excel数据 入库mysql 和 mysql数据 导入excel文件
1.excel数据入库mysql 首先准备excel文件, 标红的地方需要留意,一个是字段名所在行,一个表名对应页: 然后私用mysql工具 navicat, 选择数据库,然后导入文件, 选中相应ex ...
- python-类型转化
s='1234' a=int(s) 字符串转换成整数 s=str(a) 整数转换成字符串