在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1、主要用途:使用户可以在窗体中导航网页。 2、注意:WebBrowser 控件会占用大量资源。使用完该控件后一定要调用 Dispose 方法,以便确保及时释放所有资源。必须在附加事件的同一线程上调用 Dispose 方法,该线程应始终是消息或用户界面 (UI) 线程。 3、WebBrowser 使用下面的成员可以将控件导航到特定 URL、在导航历史记录列表中向后和向前移动,还可以加载当前用户的主页和搜索页: 1.URL属性:可读、可写,用于获取或设置当前文档的 URL。 WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。设置Url属性时,WebBrowser 控件导航到指定的 URL 并将该 URL 添加到历史记录列表的末尾。 WebBrowser 控件在本地硬盘的缓存中存储最近访问过的站点的网页。每个页面都可以指定一个到期日期,指示页面在缓存中保留的时间。当控件定位到某页时,如果该页具有缓存的版本,则直接显示缓存中的内容而不必重新下载该页,从而节省了时间。使用 Refresh 方法强制 WebBrowser控件通过下载来重新加载当前页,从而确保控件显示最新版本。 注意:即使已请求了另一个文档,该属性也包含当前文档的 URL。如果设置该属性的值,然后立即再次检索该值,要是 WebBrowser 控件尚未来得及加载新文档,则检索到的值可能与设置的值不同。 2.Navigate方法: 将指定位置的文档加载到 WebBrowser 控件中。 3.GoBack方法:如果导航历史记录中的上一页可用,则将 WebBrowser 控件导航到该页。 如果导航成功,则返回true;如果导航历史记录中的上一页不可用,则返回false。 WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。可以使用GoForward方法实现一个“后退”按钮。 使用 CanGoBack 属性确定导航历史记录是否可用以及是否包含上一页。处理 CanGoBackChanged 事件,在 CanGoBack 属性值更改时接收通知。 4.GoForward方法:如果导航历史记录中的下一页可用,则将 WebBrowser 控件导航到该页。 如果导航成功,则返回true;如果导航历史记录中的下一页不可用,则返回false。 WebBrowser 控件维护浏览会话期间访问的所有网页的历史记录列表。可以使用 GoForward 方法实现一个“前进”按钮. 使用 CanGoForward 属性确定导航历史记录是否可用以及是否包含当前页之后的页。处理 CanGoForwardChanged 事件,在 CanGoForward 属性值更改时接收通知 5.GoHome方法:将 WebBrowser 控件导航到当前用户的主页。 6.GoSearch方法:将 WebBrowser 控件导航到当前用户的默认搜索页。 默认搜索页存储在注册表的 HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Search Page 注册表项下。 若要使用其他搜索页而不是默认搜索页,请调用 Navigate 方法或指定 Url 属性。 7.Refresh方法:重新加载当前显示在 WebBrowser 控件中的文档。 8.Stop方法:取消所有挂起的导航并停止所有动态页元素(如背景声音和动画)。 如果导航不成功,则显示一页指示出现的问题。使用这些成员中的任何一个进行导航都会导致在导航的不同阶段发生 Navigating、Navigated 和DocumentCompleted 事件。
使用该属性启用 WebBrowser 控件承载的网页与包含 WebBrowser 控件的应用程序之间的通信。使用该属性可以将动态 HTML (DHTML) 代码与客户端应用程序代码集成在一起。为该属性指定的对象可作为 window.external 对象(用于主机访问的内置 DOM 对象)用于网页脚本。 可以将此属性设置为希望其公共属性和方法可用于脚本代码的任何 COM 可见的对象。可以通过使用 ComVisibleAttribute 对类进行标记使其成为 COM 可见的类。 若要从客户端应用程序代码调用网页中定义的函数,请使用可从 Document 属性检索的 HtmlDocument 对象的 HtmlDocument.InvokeScript 方法。 5、AllowNavigation属性:获取或设置一个值,该值指示控件在加载其初始页之后是否可以导航到其他页。 6、AllowWebBrowserDrop属性:获取或设置一个值,该值指示 WebBrowser 控件是否导航到拖放到它上面的文档。 7、WebBrowserShortcutsEnabled属性:是否启用WebBrowser自带的快捷键。 8、ScriptErrorsSuppressed 属性:获取或设置一个值,该值指示出现脚本错误时,WebBrowser 控件是否显示错误对话框。 9、IsWebBrowserContextMenuEnabled属性:是否启用右键菜单。
C#WinForm WebBrowser (二) 实用方法总结 实用方法1:获取状态栏信息 void webBrowser1_StatusTextChanged(object sender, EventArgs e) 实用方法2:页面跳转后改变地址栏地址 //在Navigated事件处理函数中改变地址栏地址是最恰当的: 实用方法3:设置单选框 //建议使用执行单击事件的方式来设置单选框,而不是修改属性: 实用方法4:设置联动型下拉列表 //比较常见的联动型多级下拉列表就是省/市县选择了,这种情况下直接设置选择项的属性不会触发联动,需要在最后执行触发事件函数才能正常工作: 以上四种方法转于:http://www.cnblogs.com/SkyD/archive/2009/04/23/1441696.html 实用方法一:在WinForm中相应Web事件 <html>
HtmlDocument htmlDoc = webBrowser.Document;
btnElement.AttachEventHandler("onclick", new EventHandler(HtmlBtnClose_Click)); 对于其他事件,把"onclick"换成该事件的名字就可以了。例如: formElement.AttachEventHandler("onsubmit", new EventHandler(HtmlForm_Submit));
实用方法二:模拟表单自动填写和提交 假设有一个最简单的登录页面,输入用户名密码,点“登录”按钮即可登录。已知用户名输入框的id(或Name,下同)是username,密码输入框的id是password,“登录”按钮的id是submitbutton,那么我们只需要在webBrowser的DocumentCompleted事件中使用下面的代码即可: HtmlElement btnSubmit = webBrowser.Document.All["submitbutton"]; 关于表单的提交,的确还有另一种方法就是获取form元素而不是button,并用form元素的submit方法: HtmlElement formLogin = webBrowser.Document.Forms["loginForm"]; 本文之所以没有推荐这种方法,是因为现在的网页,很多都在submit按钮上添加onclick事件,以对提交的内容做最基本的验证。如果直接使用form的submit方法,这些验证代码就得不到执行,有可能会引起错误。 实用方法三:调用脚本 首先是调用Web页面的脚本中已经定义好的函数。假设HTML中有如下JavaScript: function DoAdd(a, b) { 那么,我们要在WinForm调用它,只需如下代码即可: object oSum = webBrowser.Document.InvokeScript("DoAdd", new object[] { 1, 2 });
其次,如果我们想执行一段Web页面中原本没有的脚本,该怎么做呢?这次.Net的类没有提供,看来还要依靠COM了。IHTMLWindow2可以将任意的字符串作为脚本代码来执行。 string scriptline01 = @"function ShowPageInfo() {"; 以上三种实用方法转于:http://www.cnblogs.com/smalldust/archive/2006/03/08/345561.html 最后:在脚本中调用WinForm里的代码,这个可能吗? 呵呵,当然是可能的。
C# WinForm WebBrowser (三) 编辑模式 一、启用编辑模式、 浏览模式 及 自动换行 在编辑模式下,可以使用: this.webBrowser.Document.ExecCommand([string],[bool],[object]); 方法来操作WebBrowser中的HTML。 第三个Object类型的参数为:要使用该命令分配的值。并非适用于所有命令。 常见的命令有: private const string HTML_COMMAND_BOLD = "Bold"; //加粗 // 更多的命令请参见: http://msdn.microsoft.com/en-us/library/ms533049.aspx
———————————————————————————————————————————————————————————————— http://dev.csdn.net/develop/article/48/48483.shtm 如何使自定义的控制站点来替换默认的控制站点呢?在MFC7.0中只需重载CHtmlView的虚函数CreateControlSite即可: BOOL CLhpHtmlView::CreateControlSite(COleControlContainer * pContainer, COleControlSite ** ppSite, UINT /*nID*/, REFCLSID /*clsid*/) { *ppSite = new CDocHostSite(pContainer, this);// 创建自己的控制站点实例 return (*ppSite) ? TRUE : FALSE; } VC6.0要替换控制站要复杂的多,这里就不讨论了,如需要6.0版本的请给我发邮件到[email protected]。 HRESULT CDocHostSite::XDocHostUIHandler::ShowContextMenu(DWORD dwID, POINT * ppt, IUnknown * pcmdtReserved, IDispatch * pdispReserved) { METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler); return pThis->m_pView->OnShowContextMenu( dwID, ppt, pcmdtReserved,pdispReserved ); } HRESULT CLhpHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved) { HRESULT result = S_FALSE; switch(m_ContextMenuMode) { case NoContextMenu:// 无菜单 result=S_OK; break; case DefaultMenu:// 默认菜单 break; case TextSelectionOnly:// 仅文本选择菜单 if(!(dwID == CONTEXT_MENU_TEXTSELECT || dwID == CONTEXT_MENU_CONTROL)) result=S_OK; break; case CustomMenu:// 自定义菜单 if(dwID!=CONTEXT_MENU_TEXTSELECT) result=OnShowCustomContextMenu(ppt,pcmdtReserved,pdispReserved); break; } return result; } 在CLhpHtmlView中定义的枚举类型CONTEXT_MENU_MODE举出了定制右键弹出菜单的四种类型 enum CONTEXT_MENU_MODE// 上下文菜单 { NoContextMenu,// 无菜单 DefaultMenu,// 默认菜单 TextSelectionOnly,// 仅文本选择菜单 CustomMenu// 自定义菜单 }; 通过CLhpHtmlView的函数SetContextMenuMode来设置右键菜单的类型。如果设定的右键弹出菜单是“自定义菜单”类型, 我们只要在CLhpHtmlView的派生类中重载OnShowCustomContextMenu虚函数即可,如下代码 CDemoView是CLhpHtmlView的派生类 HRESULT CDemoView::OnShowCustomContextMenu(LPPOINT ppt, LPUNKNOWN pcmdtReserved,LPDISPATCH pdispReserved) { if ((ppt==NULL)||(pcmdtReserved==NULL)||(pcmdtReserved==NULL)) return S_OK; HRESULT hr=0; IOleWindow *oleWnd=NULL; hr=pcmdtReserved->QueryInterface(IID_IOleWindow, (void**)&oleWnd); if((hr != S_OK)||(oleWnd == NULL)) return S_OK; HWND hwnd=NULL; hr=oleWnd->GetWindow(&hwnd); if((hr!=S_OK)||(hwnd==NULL)) { oleWnd->Release(); return S_OK; } IHTMLElementPtrpElem=NULL; hr = pdispReserved->QueryInterface(IID_IHTMLElement, (void**)&pElem); if(hr != S_OK) { oleWnd->Release(); return S_OK; } IHTMLElementPtrpParentElem=NULL; _bstr_ttagID; BOOL Go=TRUE; pElem->get_id(&tagID.GetBSTR()); while(go && tagID.length()==0) { hr=pElem->get_parentElement(&pParentElem); if(hr==S_OK && pParentElem!=NULL) { pElem->Release(); pElem=pParentElem; pElem->get_id(&tagID.GetBSTR()); } else go=FALSE; }; if(tagID.length()==0) tagID="no id"; CMenu Menu,SubMenu; Menu.CreatePopupMenu(); CString strTagID = ToStr(tagID); if(strTagID == "red") Menu.AppendMenu(MF_BYPOSITION, ID_RED, "您点击的是红色"); else if(strTagID == "green") Menu.AppendMenu(MF_BYPOSITION, ID_GREEN, "您点击的是绿色"); else if(strTagID == "blue") Menu.AppendMenu(MF_BYPOSITION, ID_BLUE, "您点击的是蓝色"); else Menu.AppendMenu(MF_BYPOSITION, ID_NONE, "你点了也白点,请在指定的地方点击"); int MenuID=Menu.TrackPopupMenu(TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON,ppt->x, ppt->y, this); switch(MenuID) { case ID_RED: MessageBox("红色"); break; case ID_GREEN: MessageBox("红色"); break; case ID_BLUE: MessageBox("红色"); break; case ID_NONE: MessageBox("haha"); break; } oleWnd->Release(); pElem->Release(); return S_OK; }
HRESULT CDocHostSite::XDocHostUIHandler::GetExternal(IDispatch ** ppDispatch) { METHOD_PROLOGUE(CDocHostSite, DocHostUIHandler); return pThis->m_pView->OnGetExternal( ppDispatch ); } CLhpHtmlView::CLhpHtmlView(BOOL isview) { ...... EnableAutomation();// 允许自动化 } HRESULT CLhpHtmlView::OnGetExternal(LPDISPATCH *lppDispatch) { *lppDispatch = GetIDispatch(TRUE);// 返回自身的IDispatch接口 return S_OK; } 请注意上面代码中,在OnGetExternal返回的是自身IDispatch接口, 这样就不比为脚本扩展而另外写一个从CCmdTarget派生的新类, CLhpHtmlView本身就是从CCmdTarget派生,直接在上面实现接口就是。 --------------------------------------------------------------------------- 文件 DemoView.h --------------------------------------------------------------------------- ....... class CDemoView : public CLhpHtmlView { ...... DECLARE_DISPATCH_MAP() // 构建dispatch映射表以暴露方法或属性 ...... void WobbleWnd();// 抖动窗口 }; --------------------------------------------------------------------------- 文件 DemoView.cpp --------------------------------------------------------------------------- ...... // 把成员函数映射到Dispatch映射表中,暴露方法给脚本 BEGIN_DISPATCH_MAP(CDemoView, CLhpHtmlView) DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP() ...... void CDemoView::WobbleWnd() { // 在这里实现抖动窗口 ...... } --------------------------------------------------------------------------- 文件 Demo.htm --------------------------------------------------------------------------- ...... onclick="external.WobbleWnd()" ...... 这里我要介绍一下DISP_FUNCTION宏,它的作用是将一个函数映射到Dispatch映射表中,我们看 DISP_FUNCTION(CDemoView, "WobbleWnd", WobbleWnd, VT_EMPTY, VTS_NONE) CDemoView是宿主类名, "WobbleWnd"是暴露给外面的名字(脚本调用时使用的名字), VT_EMPTY是返回值得类型为空,VTS_NONE说明此方法没有参数,如果要映射的函数有返回值和参数该 如何映射,通过下面举例来说明 DISP_FUNCTION(CCalendarView,"TestFunc",TestFunc,VT_BOOL,VTS_BSTR VTS_I4 VTS_I4) BOOL TestFunc(LPCSTR param1, int param2, int param3) { ..... } 参数表VTS_BSTR VTS_I4 VTS_I4是用空格分隔,他们的类型映射请参考MSDN,这要提醒的是不要把VTS_BSTR与CString对应,而应与LPCSTR对应。 // 窗口标题"Microsoft Internet Explorer"的资源标识 #define IDS_MESSAGE_BOX_TITLE 2213 HRESULT CLhpHtmlView::OnShowMessage(HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT * plResult) { //载入Shdoclc.dll 和IE消息框标题字符串 HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL")); if (hinstSHDOCLC == NULL) return S_FALSE; CString strBuf,strCaption(lpstrCaption); strBuf.LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE); // 比较IE消息框标题字符串和lpstrCaption // 如果相同,用自定义标题替换 if(strBuf==lpstrCaption) strCaption = m_DefaultMsgBoxTitle; // 创建自己的消息框并且显示 *plResult = MessageBox(CString(lpstrText), strCaption, dwType); //卸载Shdoclc.dll并且返回 FreeLibrary(hinstSHDOCLC); return S_OK; } 从代码中可以看到通过设定m_DefaultMsgBoxTitle的值来改变消息宽的标题,修改此值是同过SetDefaultMsgBoxTitle来实现 void CLhpHtmlView::SetDefaultMsgBoxTitle(CString strTitle) { m_DefaultMsgBoxTitle=strTitle; }
GET /text7.htm HTTP/1.0 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, \ application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Referer: http://localhost Accept-Language: en-us User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Poco 0.31; LHP Browser 1.01; \ .NET CLR 1.1.4322) Host: localhost Connection: Keep-Alive CHtmlView的 void Navigate2( LPCTSTR lpszURL, DWORD dwFlags = 0, LPCTSTR lpszTargetFrameName = NULL, LPCTSTR lpszHeaders = NULL, LPVOID lpvPostData = NULL, DWORD dwPostDataLen = 0 ); 函数参数lpszHeaders可以指定HTTP请求头,示例如下: Navigate2(_T("http://localhost"),NULL,NULL, "MyDefineField: TestValue"); 我们捕获的HTTP头如下: #define WM_NVTO(WM_USER+1000) class NvToParam { public: CString URL; DWORD Flags; CString TargetFrameName; CByteArray PostedData; CString Headers; }; void CDemoView::OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* pbCancel) { CString strHeaders(lpszHeaders); if(strHeaders.Find("User-Agent:LHPBrowser 1.0") < 0)// 检查头里有没有自定义的User-Agent串 { *pbCancel = TRUE;// 没有,取消这次导航 if(!strHeaders.IsEmpty()) strHeaders += "\r\n"; strHeaders += "User-Agent:LHPBrowser 1.0";// 加上自定义的User-Agent串 NvToParam* pNvTo = new NvToParam; pNvTo->URL = lpszURL; pNvTo->Flags = nFlags; pNvTo->TargetFrameName = lpszTargetFrameName; baPostedData.Copy(pNvTo->PostedData); pNvTo->Headers = strHeaders; // 发送一个自定义的导航消息,并把参数发过去 PostMessage(WM_NVTO,(WPARAM)pNvTo); return; } CHtmlView::OnBeforeNavigate2(lpszURL, nFlags, lpszTargetFrameName, baPostedData, lpszHeaders, pbCancel); } LRESULT CDemoView::OnNvTo(WPARAM wParam, LPARAM lParam) { NvToParam* pNvTo = (NvToParam*)wParam; Navigate2((LPCTSTR)pNvTo->URL, pNvTo->Flags, pNvTo->PostedData, (LPCTSTR)pNvTo->TargetFrameName, (LPCTSTR)pNvTo->Headers); delete pNvTo; return 1; } 在OnBeforeNavigate2中如果发现没有自定义的User-Agent串,就加上这个串,并取消本次导航,再Post一个消息(一定要POST,让OnBeforeNavigate2跳出以后再进行导航 ),在消息中再次导航,再次导航时请求头已经有了自己的标识,所以能正常的导航。 Warning: constructing COleException, scode = DISP_E_MEMBERNOTFOUND($80020003). 这是由于CHtmlView在处理WM_SIZE消息时的一点小问题引起的,采用如下代码处理WM_SIZE消息就不会有此警告了 void CLhpHtmlView::OnSize(UINT nType, int cx, int cy) { CFormView::OnSize(nType, cx, cy); if (::IsWindow(m_wndBrowser.m_hWnd)) { CRect rect; GetClientRect(rect); // 就这一句与CHtmlView的不同 ::AdjustWindowRectEx(rect, GetStyle(), FALSE, WS_EX_CLIENTEDGE); m_wndBrowser.SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOACTIVATE | SWP_NOZORDER); } }
HRESULT CLhpHtmlView::OnGetDropTarget(LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget ) { m_DropTarget.SetIEDropTarget(pDropTarget); LPDROPTARGET pMyDropTarget; pMyDropTarget = (LPDROPTARGET)m_DropTarget.GetInterface(&IID_IDropTarget); if(pMyDropTarget) { *ppDropTarget = pMyDropTarget; pMyDropTarget->AddRef(); return S_OK; } return S_FALSE; } m_DropTarget即为自定义的处理拖放的对象。这样就能通过在从CLhpHtmlView派生的类中重载OnDragEnter、OnDragOver、 OnDrop、OnDragLeave虚函数来处理拖放了。在这里顺带讲一下视图是怎样处理拖放的。 要使视图处理拖放,首先在视图里添加一个COleDropTarget(或派生类)成员变量,如CLhpHtmlView中的“CMyOleDropTarget m_DropTarget;”,再在 视图创建时调用COleDropTarget对象的Register,即把视图与COleDropTarget对象关联起来,如CLhpHtmlView中的“m_DropTarget.Register(this);”,再对拖放 触发的事件进行相应的处理, OnDragEnter 把某对象拖入到视图时触发,在此检测拖入的对象是不是视图想接受的对象,如是返回“DROPEFFECT_MOVE”表示接受此对象,如 if(pDataObject->IsDataAvailable(CF_HDROP))// 被拖对象是文件吗? return DROPEFFECT_MOVE; OnDragOver 被拖对象在视图上移动,同OnDragEnter一样检测拖入对象,如果要接受此对象返回“DROPEFFECT_MOVE”。 OnDrop 拖着被拖对象在视图上放开鼠标,在这里对拖入对象做出处理; OnDragLeave 拖着被拖对象离开视图。 C++的代码写好了,但事情还没完,还必须在网页里用脚本对拖放事件进行处理, 即页面里哪个元素要接受拖放对象哪个元素就要处理ondragenter、ondragover、ondrop,代码其实很简单,让事件的返回值为false即可,这样 C++的代码才有机会处理拖放事件,代码如下: ...... <td ondragenter="event.returnValue = false" ondragover="event.returnValue = false" \ ondrop="event.returnValue = false"> ...... 如果要使整个视图都接受拖放,则在Body元素中处理此三个事件。 注意:别忘了让工程对OLE的支持即在初始化应用程序时调用AfxOleInit()。 · 禁用浏览器的3D的边缘; · 禁止滚动条; · 禁用脚本; · 定义双击处理的方式; · 禁用浏览器的自动完成功能; ...... 更多详情请参考MSDN的DOCHOSTUIFLAG帮助。 HRESULT CLhpHtmlView::OnGetHostInfo(DOCHOSTUIINFO * pInfo) { pInfo->cbSize = sizeof(DOCHOSTUIINFO); pInfo->dwFlags = DOCHOSTUIFLAG_DIALOG | DOCHOSTUIFLAG_THEME | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_SCROLL_NO; pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; return S_OK; } 用脚本也可实现: 在Head中加入脚本: document.onselectstart=new Function(''return false''); 或者 <body onselectstart="return false">。
BOOL PutElementHtml(CString ElemID,CString Html); 取表单元素的值: BOOL GetElementValue(CString ElemID,CString& Value); 设置表单元素的值: BOOL PutElementValue(CString ElemID,CString Value); 给表单元素设置焦点: void ElementSetFocus(CString EleName); 转载:http://www.vckbase.com/document/viewdoc/?id=1486 ———————————————————————————————————————————————————————————————— 自定义浏览器 本教程提供了自定义浏览器控件的行为和外观的一些方法。你将看到高级的宿主接口,IDocHostUIHandler, IDocHostUIHandler2, IDocHostShowUI, 和ICustomDoc。本文也讨论其他自定义方法,例如在宿主的IDispatch实现中处理DISPID_AMBIENT_DLCONTROL来进行下载控制;以及使用IHostDialogHelper。 本文分为如下章节 · 前提和需求 · 介绍 · 浏览器自定义架构 · IDocHostUIHandler · IDocHostUIHandler2 · GetOptionKeyPath 和 GetOverrideKeyPath的比较 · 控制导航 · IDocHostShowUI · 控制下载和执行 · IHostDialogHelper · 控制新的窗口 · 显示证书对话框(New!) · 信息栏(New!) · 结论 前提和需求 为了理解和使用本教程,你需要 · 对C++和COM的深入了解 · · 熟悉活动模板库 (ATL) · · 安装了Microsoft(R) Internet Explorer (IE)6 或更高版本 · · 开发环境具有用于IE6或更高版本的头文件和库文件;特别是Mshtmhst.h.(译者注:可以在http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ 这里下载最新的Internet Development SDK) 许多自定义特性是在IE5或者5.5版本就可以使用的,但是有几个特性需要IE6。一些特性需要IE6的Windows XP SP2版本。使用某个特性之前,应该检查参考文档以获得版本信息。 介绍 集成浏览器控件是快速软件开发的强有力的工具。通过成为浏览器的宿主,你可以利用便于使用的Dynamic HTML (DHTML), HTML, 和Extensible Markup Language (XML)来显示信息和开发一个用户界面。但是,浏览器控件的行为可能不确切符合你的需求。例如,默认的状态允许用户通过快捷菜单的查看源代码选项查看一个显示的页面的源代码,你可能需要禁用或者干脆去掉这个选项。你可能更进一步,需要用你自己的快捷菜单替换默认的快捷菜单。 在刚刚提到的自定义特性之外,高级宿主特性允许 · 在显示的页面上的按钮和其他控件可以调用你的应用程序的内建方法,有效地扩展DHTML对象模型(DOM)。 · 改变拖放的行为 · 限制浏览器的导航,例如,限制于指定的页面/域,或者站点 · 捕获用户键入,并且在需要的时候处理。比如说,你可能需要捕获CTRL+O来阻止用户在新的IE中打开网页而不是使用你的程序打开, · 改变默认字体和显示设置 · 控制下载内容,以及当下载完成之后浏览器的处理。例如,你可能禁用视频的播放,脚本的执行,点击链接时打开新的窗口,或者Microsoft(R) ActiveX 控件的下载和执行。 · 限制查看源代码 · 捕获搜索 · 捕获导航错误 · 替代/修改快捷菜单或者禁用,替代,自定义,或者添加快捷菜单项 · 为你的应用程序改变注册表设定 · 控制和修改浏览器控件显示的消息框 · 控制新窗口的创建方式 在下列节中,我们将会看到多数,但是不是全部的这些可能性而且讨论该如何实现他们。 浏览器自定义架构 介绍 IDocHostUIHandler , IDocHostUIHander2 , IDocHostShowUI 和 ICustomDoc下面三个接口是浏览器控件用户界面的自定义的核心:IDocHostUIHandler ,IDocHostUIHandler2 和 IDocHostShowUI。当你修改浏览器控件的时候 , 这些是你在你的应用程序中实现的接口。也有一些服务接口。 ICustomDoc 被MSHTML实现并且提供一个方法在某些情况下能够自定义浏览器控件。IHostDialogHelper提供一个方法打开可信的对话框,没有像IE对话框那样为他们(译者注:在标题栏上)作标记。 除了使用这些接口,你还可以做其他两件事。其一,你能通过在IDispatch实现中拦截环境特性的变化来控制下载;其次,你能通过在IDispatch实现中拦截DISPID_NEWWINDOW2来控制窗口的创建方式。 译者注:MFC7中的DHTML类,例如CHtmlView和CDHtmlDialog实现了这些接口,但是对于使用其他的类库的程序员,可能需要自己实现这些接口。 如何工作当一个容器提供对ActiveX 控件支持的时候 , 浏览器控件自定义机制被设计为被自动化。当浏览器控件被实例化的时候,如果可能的话,它尝试找来自宿主的 IDocHostUIHandler , IDocHostUIHandler2 和 IDocHostShowUI 实现。浏览器控件通过调用宿主的IOleClientSite接口的一个QueryInterface方法来查找。 译者注:IE5.5有个Bug,没有查询IDocHostUIHandler2 接口的实现,这使得宿主程序不能覆盖默认的参数。需要更多信息的话,参考微软知识库文章 Q272968 BUG:IDocHostUIHandler2 没有在浏览器控件中调用。 这一个结构为一个实现一个IOleClientSite接口的应用程序自动地工作,通过调用浏览器的IOleObject::SetClientSite方法传递给浏览器控件一个IOleClientSite接口。浏览器控件的一个典型的实例化可能看起来像这样: 例子 //为了明确起见,省略错误检查 CComPtr<IOleObject> spOleObj; //创建 WebBrowser--在类成员变量 m_spWebBrowser中保存指针 CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IWebBrowser2, (void**)&m_spWebBrowser); // 查询WebBrowser的IOleObject接口 m_spWebBrowser->QueryInterface(IID_IOleObject, (void**)&spOleObj); //设置用户站点 spOleObj->SetClientSite(this); //本地激活浏览器控件 RECT rcClient GetClientRect(&rcClient); spOleObj->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, GetTopLevelWindow(), &rcClient); //容器拦截浏览器事件的注册 AtlAdvise(m_spWebBrowser,GetUnknown(), DIID_DWebBrowserEvents2,&m_dwCookie); //导航到启动页 m_spWebBrowser->Navigate(L"res://webhost.exe/startpage.htm", NULL, NULL, NULL, NULL); 然而,如果你的应用程序没有一个IOleClientSite接口,你并没失去全部希望。IE提供ICustomDoc接口,这样你能自己传递你的IDocHostUIHandler接口给浏览器。你不能使用IDocHostUIHandler2和 IDocHostShowUI接口而不提供一个浏览器控件宿主的IOleClientSite接口。 译者注: MFC7中引入的类COleControlContainer和一大堆DHTML类曾经搞得我晕头转向,最后我不得不放弃了自己对IOleClientSite的实现,而通过ICustomDoc来显式地设置IDocHostUIHandler接口。这样必须在第一个页面下载完成之后才能够开始自定义浏览器,因为暴露ICustomDoc接口的对象只有在第一个页面下载完成之后才可用。一个ICustomDoc的示例可以在CSDN文档中心找到,网址是http://www.csdn.net/develop/Read_Article.asp?Id=8813 当浏览器控件获得了对这些接口之中的任何一个的一个指针的时候,接口的方法在适当的时候在浏览器控件的生命期中被调用。举例来说, 当用户右击在浏览器控件的客户区的任何地点时,在IE显示它的默认快捷菜单之前,你的IDocHostUIHandler::ShowContextMenu的实现将会被调用。这给你一个机会显示你自己的快捷菜单而且取消IE的快捷菜单显示。 译者注:一些屏蔽快捷菜单的示例可以在CSDN文档中心找到,网址是http://www.csdn.net/develop/article/18/18541.shtm 当初始化浏览器控件的时候 ,记住几个重点。你的应用程序应该使用 OleInitialize而不是CoInitialize启动COM。OleInitialize启用剪贴簿支持,拖放,对象连接与嵌入(OLE)和本地激活。当你的应用程序结束的时候使用OleUninitialize关闭COM库。 ATL COM 向导使用 CoInitialize而不是OleInitialize打开COM库。 如果你使用这一个向导建立一个可运行的程序,你需要将 CoInitialize 和 CoUninitialize 调用换成 OleInitialize 和 OleUninitialize。对于一个微软基础类 (MFC) 应用程序, 确定你的应用程序调用 AfxOleInit, 它在它的初始化程序中调用OleInitialize。 如果你不需要在你的应用程序中支持拖放,你可以调用IWebBrowser2::RegisterAsDropTarget,传递VARIANT_TRUE(译者注:原文如此,按照接口的文档,似乎应该传递VARIANT_FALSE), 避免任何在你的浏览器控件实例上的拖放操作。 一个浏览器控件宿主应用程序也需要IOleInPlaceSite的一个实现, 由于 IOleInPlaceSite派生自IOleWindow,应用程序将需要IOleWindow的一个实现。你需要这些实现使得你的应用程序具有一个窗口,显示浏览器控件,以及处理它的显示设置。 这些接口和IOleClientSite的实现在许多情况可能是最小的或不存在的。IOleClientSite的所有方法都可以返回E_NOTIMPL。 一些IOleInPlaceSite和IOleWindow的方法需要一个实现来覆盖返回值。可以在示例代码中查看IOleInPlaceSite和IOleWindow的最小实现的样例代码。 既然我们已经完成了初始化的准备,让我们看一看浏览器控件自定义的每一个接口。 IDocHostUIHandler IDocHostUIHandler自IE5以后已经是可用的。它提供15个方法。大体上,一些较重要的方法是IDocHostUIH |
请发表评论