c#winform使用WebBrowser 大全
C#?WinForm?WebBrowser?(一)?MSDN資料?
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?事件。
4、ObjectForScripting?屬性:獲取或設置一個對象,該對象可由顯示在?WebBrowser?控件中的網頁所包含的腳本代碼訪問。
使用該屬性啟用?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屬性:是否啟用右鍵菜單。
源:MSDN?http://msdn.microsoft.com/zh-cn/library/system.windows.forms.webbrowser(v=vs.80).aspx
C#WinForm?WebBrowser?(二)?實用方法總結?
實用方法1:獲取狀態欄信息
void?webBrowser1_StatusTextChanged(object?sender,?EventArgs?e)
{
????label1.Text?=?webBrowser1.StatusText;
}
實用方法2:頁面跳轉后改變地址欄地址
//在Navigated事件處理函數中改變地址欄地址是最恰當的:
private?void?webBrowser1_Navigated(object?sender,?WebBrowserNavigatedEventArgs?e)
{
????textBox1.Text?=?webBrowser1.Url.ToString();
}
實用方法3:設置單選框
//建議使用執行單擊事件的方式來設置單選框,而不是修改屬性:
webBrowser1.Document.GetElementById("RBT_A").InvokeMember("click");
實用方法4:設置聯動型下拉列表
//比較常見的聯動型多級下拉列表就是省/市縣選擇了,這種情況下直接設置選擇項的屬性不會觸發聯動,需要在最后執行觸發事件函數才能正常工作:
foreach?(HtmlElement?f?in?s.GetElementsByTagName("option"))
{
????if?(f.InnerText?==?"北京")
????{
????????f.SetAttribute("selected",?"selected");
????}
????else
????{
????????f.SetAttribute("selected",?"");
????}
}
s.RaiseEvent("onchange");
以上四種方法轉于:http://www.cnblogs.com/SkyD/archive/2009/04/23/1441696.html
實用方法一:在WinForm中相應Web事件
假設HTML源代碼如下:
<html>?
<body>?
<input?type="button"?id="btnClose"?value="關閉"?/>?
</body>?
</html>
??
HtmlDocument?htmlDoc?=?webBrowser.Document;?
HtmlElement?btnElement?=?htmlDoc.All["btnClose"];?
if?(btnElement?!=?null)?
{?
????btnElement.click?+=?new?HtmlElementEventHandler(HtmlBtnClose_Click);?
}
//很簡單吧?那么稍稍高級一點的——我們都知道一個HTML元素可能有很多各種各樣的事件,而HtmlElement這個類只給出最常用、共通的幾個。那么,如何響應其他事件呢?這也很簡單,只需要調用HtmlElement的AttachEventHandler就可以了:
btnElement.AttachEventHandler("onclick",?new?EventHandler(HtmlBtnClose_Click));??
//這一句等價于上面的btnElement.click?+=?new?HtmlElementEventHandler(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"];?
HtmlElement?tbUserid?=?webBrowser.Document.All["username"];?
HtmlElement?tbPasswd?=?webBrowser.Document.All["password"];?
if?(tbUserid?==?null?||?tbPasswd?==?null?||?btnSubmit?==?null)?
????return;?
tbUserid.SetAttribute("value",?"smalldust");?
tbPasswd.SetAttribute("value",?"12345678");?
btnSubmit.InvokeMember("click");
關于表單的提交,的確還有另一種方法就是獲取form元素而不是button,并用form元素的submit方法:
HtmlElement?formLogin?=?webBrowser.Document.Forms["loginForm"];??
//……??
formLogin.InvokeMember("submit");?
本文之所以沒有推薦這種方法,是因為現在的網頁,很多都在submit按鈕上添加onclick事件,以對提交的內容做最基本的驗證。如果直接使用form的submit方法,這些驗證代碼就得不到執行,有可能會引起錯誤。?
實用方法三:調用腳本
首先是調用Web頁面的腳本中已經定義好的函數。假設HTML中有如下Javascript:
function?DoAdd(a,?b)?{
????return?a?+?b;
}
那么,我們要在WinForm調用它,只需如下代碼即可:
object?oSum?=?webBrowser.Document.InvokeScript("DoAdd",?new?object[]?{?1,?2?});
int?sum?=?Convert.ToInt32(oSum);
?
其次,如果我們想執行一段Web頁面中原本沒有的腳本,該怎么做呢?這次.Net的類沒有提供,看來還要依靠COM了。IHTMLWindow2可以將任意的字符串作為腳本代碼來執行。
string?scriptline01?=?@"function?ShowPageInfo()?{";
string?scriptline02?=?@"?????var?numLinks?=?document.links.length;?";
string?scriptline03?=?@"?????var?numForms?=?document.forms.length;?";
string?scriptline04?=?@"?????var?numImages?=?document.images.length;?";
string?scriptline05?=?@"?????var?numScripts?=?document.scripts.length;?";
string?scriptline06?=?@"?????alert('網頁的統計結果:\r\n鏈接數:'?+?numLinks?+?";
string?scriptline07?=?@"????????'\r\n表單數:'?+?numForms?+?";
string?scriptline08?=?@"????????'\r\n圖像數:'?+?numImages?+?";
string?scriptline09?=?@"????????'\r\n腳本數:'?+?numScripts);}";
string?scriptline10?=?@"ShowPageInfo();";
string?strScript?=?scriptline01?+?scriptline02?+?scriptline03?+?scriptline04?+?scriptline05?+
???????????????????scriptline06?+?scriptline07?+?scriptline08?+?scriptline09?+?scriptline10;
IHTMLWindow2?win?=?(IHTMLWindow2)webBrowser.Document.Window.DomWindow;
win.execScript(strScript,?"Javascript");
?
以上三種實用方法轉于:http://www.cnblogs.com/smalldust/archive/2006/03/08/345561.html
最后:在腳本中調用WinForm里的代碼,這個可能嗎??呵呵,當然是可能的。
下面的代碼示例演示如何使用?ObjectForScripting?屬性。在該示例中,ObjectForScripting?屬性被設置為當前窗體。
view?sourceprint?
| using?System;? | |
| using?System.Windows.Forms;? | |
| using?System.Security.Permissions;? | |
| ?? | |
| [PermissionSet(SecurityAction.Demand,?Name="FullTrust")]? | |
| [System.Runtime.InteropServices.ComVisibleAttribute(true)]? | |
| public?class?Form1?:?Form? | |
| {? | |
| ????private?WebBrowser?webBrowser1?=?new?WebBrowser();? | |
| ????private?Button?button1?=?new?Button();? | |
| ?? | |
| ????[STAThread]? | |
| ????public?static?void?Main()? | |
| ????{? | |
| ????????Application.EnableVisualStyles();? | |
| ????????Application.Run(new?Form1());? | |
| ????}? | |
| ?? | |
| ????public?Form1()? | |
| ????{? | |
| ????????button1.Text?=?"call?script?code?from?client?code";? | |
| ????????button1.Dock?=?DockStyle.Top;? | |
| ????????button1.Click?+=?new?EventHandler(button1_Click);? | |
| ????????webBrowser1.Dock?=?DockStyle.Fill;? | |
| ????????Controls.Add(webBrowser1);? | |
| ????????Controls.Add(button1);? | |
| ????????Load?+=?new?EventHandler(Form1_Load);? | |
| ????}? | |
| ?? | |
| ????private?void?Form1_Load(object?sender,?EventArgs?e)? | |
| ????{? | |
| ????????webBrowser1.AllowWebBrowserDrop?=?false;? | |
| ????????webBrowser1.IsWebBrowserContextMenuEnabled?=?false;? | |
| ????????webBrowser1.WebBrowserShortcutsEnabled?=?false;? | |
| ????????webBrowser1.ObjectForScripting?=?this;? | |
| ????????//?Uncomment?the?following?line?when?you?are?finished?debugging.? | |
| ????????//webBrowser1.ScriptErrorsSuppressed?=?true;? | |
| ?? | |
| ????????webBrowser1.DocumentText?=? | |
| ????????????"<html><head><script>"?+? | |
C#?WinForm?WebBrowser?(三)?編輯模式?
一、啟用編輯模式、?瀏覽模式?及?自動換行
?
????????///?<summary>
????????///?編輯模式
????????///?</summary>
????????public?void?EditMode()
????????{
????????????if?(this.webBrowser.Document?!=?null)
????????????{
????????????????mshtml.IHTMLDocument2?doc?=?this.webBrowser.Document.DomDocument?as?mshtml.IHTMLDocument2;
????????????????if?(doc?!=?null)
????????????????{
????????????????????doc.designMode?=?"on";
????????????????}
????????????}
????????}
????????///?<summary>
????????///?啟用瀏覽模式
????????///?</summary>
????????public?void?BrowseMode()
????????{
????????????if?(this.webBrowser.Document?!=?null)
????????????{
????????????????mshtml.IHTMLDocument2?doc?=?this.webBrowser.Document.DomDocument?as?mshtml.IHTMLDocument2;
????????????????if?(doc?!=?null)
????????????????{
????????????????????doc.designMode?=?"off";
????????????????}
????????????}
????????}
????????///?<summary>
????????///?設置自動換行
????????///?</summary>
????????///?<param?name="value"></param>
????????public?void?SetAutoWrap(bool?value)
????????{
????????????mshtml.HTMLDocument?doc?=?this.webBrowser.Document.DomDocument?as?mshtml.HTMLDocument;
????????????if?(doc?!=?null)
????????????{
????????????????mshtml.HTMLBody?body?=?doc.body?as?mshtml.HTMLBody;
????????????????if?(body?!=?null)
????????????????{
????????????????????body.noWrap?=?!value;
????????????????}
????????????}
????????}
在編輯模式下,可以使用:
??this.webBrowser.Document.ExecCommand([string],[bool],[object]);
方法來操作WebBrowser中的HTML。
其中第一個字符串類型的參數為:要執行的命令的名稱?。
第二個布爾類型的參數為:?是否向用戶顯示命令特定的對話框或消息框。
第三個Object類型的參數為:要使用該命令分配的值。并非適用于所有命令。
常見的命令有:
????????private?const?string?HTML_COMMAND_BOLD?=?"Bold";???????????????????????//加粗
????????private?const?string?HTML_COMMAND_UNDERLINE?=?"Underline";?????????????//下劃線
????????private?const?string?HTML_COMMAND_ITALIC?=?"Italic";???????????????????//斜體
????????private?const?string?HTML_COMMAND_SUBSCRIPT?=?"Subscript";?????????????//下標
????????private?const?string?HTML_COMMAND_SUPERSCRIPT?=?"Superscript";?????????//上標
????????private?const?string?HTML_COMMAND_STRIKE_THROUGH?=?"StrikeThrough";????//刪除線
????????private?const?string?HTML_COMMAND_FONT_NAME?=?"FontName";??????????????//字體
????????private?const?string?HTML_COMMAND_FONT_SIZE?=?"FontSize";??????????????//字號
????????private?const?string?HTML_COMMAND_FORE_COLOR?=?"ForeColor";????????????//字體前景色
????????private?const?string?HTML_COMMAND_BACK_COLOR?=?"BackColor";????????????//字體背景色
????????private?const?string?HTML_COMMAND_INSERT_FORMAT_BLOCK?=?"FormatBlock";?//加粗
????????private?const?string?HTML_COMMAND_REMOVE_FORMAT?=?"RemoveFormat";??????//清楚樣式
????????private?const?string?HTML_COMMAND_JUSTIFY_LEFT?=?"JustifyLeft";????????//文本左對齊
????????private?const?string?HTML_COMMAND_JUSTIFY_CENTER?=?"JustifyCenter";????//文本中間對齊
????????private?const?string?HTML_COMMAND_JUSTIFY_RIGHT?=?"JustifyRight";??????//文本右對齊
????????private?const?string?HTML_COMMAND_JUSTIFY_FULL?=?"JustifyFull";????????//文本兩端對齊
????????private?const?string?HTML_COMMAND_INDENT?=?"Indent";???????????????????//增大縮進量
????????private?const?string?HTML_COMMAND_OUTDENT?=?"Outdent";?????????????????//減小縮進量
????????private?const?string?HTML_COMMAND_INSERT_LINE?=?"InsertHorizontalRule";//插入分割符
????????private?const?string?HTML_COMMAND_INSERT_LIST?=?"Insert{0}List";?//?replace?with?(Un)Ordered?插入項目符號或項目編號
????????private?const?string?HTML_COMMAND_INSERT_IMAGE?=?"InsertImage";?????????//插入圖像
????????private?const?string?HTML_COMMAND_INSERT_LINK?=?"CreateLink";???????????//插入鏈接
????????private?const?string?HTML_COMMAND_REMOVE_LINK?=?"Unlink";???????????????//移除鏈接
????????private?const?string?HTML_COMMAND_TEXT_CUT?=?"Cut";?????????????????????//剪切
????????private?const?string?HTML_COMMAND_TEXT_COPY?=?"Copy";???????????????????//復制
????????private?const?string?HTML_COMMAND_TEXT_PASTE?=?"Paste";?????????????????//粘貼
????????private?const?string?HTML_COMMAND_TEXT_DELETE?=?"Delete";???????????????//刪除
????????private?const?string?HTML_COMMAND_TEXT_UNDO?=?"Undo";???????????????????//撤銷
????????private?const?string?HTML_COMMAND_TEXT_REDO?=?"Redo";???????????????????//恢復 ????????private?const?string?HTML_COMMAND_TEXT_SELECT_ALL?=?"SelectAll";????????//全選 ????????private?const?string?HTML_COMMAND_TEXT_UNSELECT?=?"Unselect";???????????//取消選擇 ????????private?const?string?HTML_COMMAND_TEXT_PRINT?=?"Print";?????????????????//?打印 ????????private?const?string?HTML_COMMAND_EDITMODE?=?"EditMode";????????????????//?編輯模式 ????????private?const?string?HTML_COMMAND_BROWSEMODE?=?"BrowseMode";????????????//?瀏覽模式 ????????private?const?string?HTML_COMMAND_OVERWRITE?=?"OverWrite";?????????????//轉換插入、覆寫模式
//?更多的命令請參見:
http://msdn.microsoft.com/en-us/library/ms533049.aspx
C#?WinForm?WebBrowser?(四)?自定義操作【轉】?
————————————————————————————————————————————————————————————————
由于本人在開發中經常要在程序中嵌入瀏覽器,為了符合自己的需求經常要對瀏覽器進行擴展和定制,?解決這些問題需在網上找資料和學習的過程,我想可能很多開發者或許會遇到同樣的問題,特寫此文,以供大家參考。?
在MFC中使用瀏覽器
在MFC中微軟為我們提供了CHtmlView、CDHtmlDialog類讓我們的程序很方便的嵌入瀏覽器和進行瀏覽器的二次開發,這比直?接使用WebBrowser控件要方便很多,所以本文中討論的瀏覽器的問題都是針對CHtmlView來討論的。文中將提到一個類CLhpHtmlView,?它是CHtmlView的派生類,文中提及的擴展或定制都將在CLhpHtmlView類(或派生類)上實現。
怎樣擴展或定制瀏覽器
瀏覽器定義了一些擴展接口(如IDocHostUIHandler可以定制瀏覽器界面有關的行為),以便開發者進行定制和擴展。瀏覽?器會在需要的時候向他的控制站點查詢這些接口,在控制站點里實現相應的接口就可以進行相應的擴展。在MFC7.01類?庫中,CHtmlView使用的控制站點是CHtmlControlSite的,在CHtmlControlSite類中?只實現了接口IDocHostUIHandler,?而要實現更多的擴展接口,必須用自定義的控制站類來取代CHtmlControlSite,在下文中提及的類CDocHostSite即為自定義?的控制站類。?
關于接口的介紹請參考:?
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版本的請給我發郵件到yourshine@21cn.com。
定制鼠標右鍵彈出出菜單
要定制瀏覽器的鼠標右鍵彈出菜單,必須在自定義的控制站點類中實現IDocHostUIHandler2接口,并且IE的?版本是5.5或以上。在接口IDocHostUIHandler2的ShowContextMenu方法中調用瀏覽器類的OnShowContextMenu虛函數,我們?在瀏覽器類的派生類重載此虛函數即可實現右鍵菜單的定制,參見代碼?
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;
}
實現腳本擴展(很重要的external接口)
在你嵌入了瀏覽器的工程中,如果網頁的腳本中能調用C++代碼,那將是一件很愜意的事情,要實現這種交互,就必須實現腳本擴展。實現腳本擴展就是在程序中實現一個IDispatch接口,通過CHtmlView類的OnGetExternal虛函數返回此接口指針,這樣就可以在腳本中通過window.external.XXX(關鍵字window可以省略)來?引用接口暴露的方法或屬性(XXX為方法或屬性名)。在MFC中從CCmdTarget派生的類都可以實現自動化,而不必在MFC工程中引入繁雜的ATL。從CCmdTarget派生的類實現自動化接口的時候不要忘了在構造函數中調用EnableAutomation函數。
要使虛函數OnGetExternal發揮作用必須在?自定義的控制站點類中實現IDocHostUIHandler,在接口IDocHostUIHandler的GetExternal方法中調用瀏覽器類的OnGetExternal虛函數,我們在瀏覽器類的派生類重載OnGetExternal虛函數,?通過參數lppDispatch返回一個IDispatch指針,這樣腳本中引用window.external時就是引用的返回的接口,參見代碼?
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派生,直接在上面實現接口就是。?
下用具體示例來說明怎樣實現腳本擴展?
示例會在網頁上點擊一個按鈕而使整個窗口發生抖動
從CLhpHtmlView派生一個類CDemoView,在類中實現IDispatch,?并通過IDispatch暴露方法WobbleWnd?
---------------------------------------------------------------------------
文件?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
---------------------------------------------------------------------------
......?οnclick="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對應。
C++代碼中如何調用網頁腳本中的函數
IHTMLDocument2::scripts屬性表示HTML文檔中所有腳本對象。使用腳本對象的IDispatch接口的GetIDsOfNames方法可以得到腳本函數的?DispID,得到DispID后,使用IDispatch的Invoke函數可以調用對應的腳本函數。CLhpHtmlView提供了方便的調用JavaScript的函數,請參考CLhpHtmlView中有關鍵字“JScript”的代碼。
定制消息框的標題
我們在腳本中調用alert彈出消息框時,消息框的標題是微軟預定義的“Microsoft?Internet?Explorer”,如下圖:
在自定義的控制站點類中實現IDocHostShowUI接口,在接口的ShowMessage方法中調用瀏覽器的OnShowMessage,我們重載?OnShowMessage虛函數即可定制消息框的標題,實現代碼如下:?
//?窗口標題"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;
}
怎樣定制、修改瀏覽器向Web服務器發送的HTTP請求頭
在集成了WebBrowser控件的應用中,Web服務器有時可能希望客戶端(瀏覽器)發送的HTTP請求中附帶一些額外的信息或自定義的?HTTP頭字段,這樣就必須在瀏覽器中控制向Web服務器發送的HTTP請求。?下面是捕獲的一個普通的用瀏覽器發送的HTTP請求頭:?
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頭如下:?
怎樣修改瀏覽器標識
在HTTP請求頭中User-Agent字段表明了瀏覽器的版本以及操作系統的版本等信息。WEB服務器經常需要知道用戶請求頁面時是來自IE還是來自自己的客戶端中的WebBrowser控件,?以便分開處理,而WebBrowser控件向WEB服務器發送的瀏覽器標識(User-Agent字段)跟用IE發送的是一樣的,怎樣區分自己的瀏覽器和IE呢??微軟沒有提供現成的方法,要自己想法解決。?前面討論的定制HTTP請求頭就是為這一節準備的。?思路是這樣的:?在自己的瀏覽器里處理每一個U頁面請求,把請求頭User-Agent改成自己想要的。?在CHtmlView的OnBeforeNavigate2虛函數里來修改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跳出以后再進行導航?),在消息中再次導航,再次導航時請求頭已經有了自己的標識,所以能正常的導航。?
去掉討厭的異常警告?
在程序中使用了CHtmlView以后,我們在調整窗口大小的時候經常會看到輸出窗口輸出的異常警告:?ReusingBrowser.exe?中的?0x77e53887?處最可能的異常:?Microsoft?C++?exception:?COleException?@?0x0012e348?。?
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);?
}?
}
怎樣處理瀏覽器內的拖放?
有時可能有這樣的需求,我們希望在資源管理器里托一個文件到瀏覽器而做出相應的處理,甚至是將文件拖到某一個網頁元素上來做出相應的處理,而瀏覽器默認的處理拖放文件操作是將文件打開,但WebBrowser控件給了我們一個自己處理拖放的機會。?那就是在自定義的控制站點類中實現IDocHostUIHandler,在接口IDocHostUIHandler的GetDropTarget方法中調用?瀏覽器類的OnGetDropTarget虛函數。要處理網頁內的拖放,必需在OnGetDropTarget函數中返回一個自己定義的IDropTarget接口指針,?所以我們自己寫一個類CMyOleDropTarget從COleDropTarget類派生,并且在實現IDropTarget接口,此類的代碼在這就不列出了,請下載演示?程序,參考文件MyOleDropTarget.h和MyOleDropTarget.cpp。我們看CLhpHtmlView中OnGetDropTarget的代碼?
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?οndragenter="event.returnValue?=?false"?οndragοver="event.returnValue?=?false"?\
οndrοp="event.returnValue?=?false">
......
如果要使整個視圖都接受拖放,則在Body元素中處理此三個事件。?注意:別忘了讓工程對OLE的支持即在初始化應用程序時調用AfxOleInit()。
怎樣禁止網頁元素的選取
用網頁做界面時多數情況下是不希望網頁上的元素是能夠被鼠標選中的,?要使網頁元素不能被選中做法是:給瀏覽器的“宿主信息標記”加上DOCHOSTUIFLAG_DIALOG標記。
“宿主信息標記”用N個標記位來控制瀏覽器的許多性質,如:?
·?禁用瀏覽器的3D的邊緣;?
·?禁止滾動條;?
·?禁用腳本;?
·?定義雙擊處理的方式;?
·?禁用瀏覽器的自動完成功能;?
......?更多詳情請參考MSDN的DOCHOSTUIFLAG幫助。
怎樣修改“宿主信息標記”?
在CDocHostSite中實現IDocHostUIHandler,?在GetHostInfo方法中調用瀏覽器的OnGetHostInfo虛函數,在虛函數OnGetHostInfo中便可指定“宿主信息標記”,如:
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">。
其它?
在CLhpHtmlView中還提供了幾個函數,?修改網頁元素的內容:?
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 個方法。大體上,一些較重要的方法是 IDocHostUIHandler::GetExternal,?IDocHostUIHandler::GetHostInfo,?IDocHostUIHandler::GetOptionKeyPath,?IDocHostUIHandler::ShowContextMenu,?和?IDocHostUIHandler::TranslateAccelerator。 當然,方法對你的重要性將會依賴于你的應用程序。
IDocHostUIHandler::GetHostInfo
你使用IDocHostUIHandler::GetHostInfo告訴MSHTML有關你的應用程序的能力和需求。通過它你能控制很多東西,?舉例來說。
·?你能禁用瀏覽器的3D的邊緣。
·?你能避免滾動條或改變他們的外觀。
·?你能禁用腳本。
·?你能定義雙擊處理的方式。
·?你能禁用瀏覽器的自動完成功能。
IDocHostUIHandler::GetHostInfo有一個參數,被?MSHTML分配的DOCHOSTUIINFO?結構的一個指針。你的工作是要將在結構中填充你傳給MSHTML的信息。
DOCHOSTUIINFO結構有四個成員。第一個成員是?cbSize,是結構的大小。你應該自己像下面的示例代碼那樣設置。第二個成員是dwFlags,由來自DOCHOSTUIFLAG枚舉的數值位與組成。第三個成員是dwDoubleClick,來自DOCHOSTUIDBLCLK枚舉的一個數值。第四個成員是pchHostCss。你可以將pchHostCss設定為瀏覽器控件顯示的頁面中應用的全局樣式表(CSS)規則的一個字符串的指針。DOCHOSTUIINFO?的最后一個成員是pchHostNs。你可以設置為你提供的分號分隔的命名空間列表字符串。在你正在瀏覽器控件中顯示的頁上使用自定義標簽的時候使用這一個成員。這樣你能聲明一個全局的命名空間列表,而不需要在每個顯示的頁面上聲明他們。
確定使用CoTaskMemAlloc為pchHostCss或pchHostNS分配字符串。(譯者注:看起來調用者用CoTaskMemFree釋放這些字符串)。
例子
HRESULT?GetHostInfo(?DOCHOSTUIINFO*?pInfo)
{
????WCHAR*?szCSS?=?L"BODY?{background-color:#ffcccc}";
????WCHAR*?szNS?=?L"IE;MyTags;MyTags2='www.microsoft.com'";
????#define?CCHMAX?256
????size_t?cchLengthCSS,cchLengthszNS;
????HRESULT?hr=StringCchLengthW(szCSS,?CCHMAX,&cchLengthCSS)
????//TODO:?在這里處理錯誤。
????OLECHAR*?pCSSBuffer=(OLECHAR*)?CoTaskMemAlloc((cchLengthCSS+1)*sizeof(OLECHAR));
????//TODO:?在這里處理錯誤,確定內存成功地被分配。
????hr=StringCchLengthW(szNS,?CCHMAX,&cchLengthszNS)
????//TODO:?在這里處理錯誤。
????OLECHAR*?pNSBuffer=(OLECHAR*)?CoTaskMemAlloc((cchLengthszNS+1)*sizeof(OLECHAR));
????//TODO:?在這里處理錯誤,確定內存成功地被分配。
????hr=StringCchCopyW(pCSSBuffer?,?cchLengthCSS+1,szCSS)
????//TODO:?在這里處理錯誤。
????hr=StringCchCopyW(pNSBuffer?,?cchLengthszNS+1,szNS)
????//TODO:?在這里處理錯誤。
????pInfo->?cbSize=?sizeof(DOCHOSTUIINFO)
????pInfo->?dwFlags=DOCHOSTUIFLAG_NO3DBORDER|DOCHOSTUIFLAG_SCROLL_NO|DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE;
????pInfo->?dwDoubleClick=?DOCHOSTUIDBLCLK_DEFAULT;
????pInfo->?pchHostCss=?pCSSBuffer;
????pInfo->?pchHostNS=?pNSBuffer;
????return?S_OK;
}
如果你沒有什么需要告訴MSHTML的,你可以在這個方法中返回E_NOTIMPL?。
IDocHostUIHandler::ShowContextMenu
通過實現這一個方法,?你獲得在當一個用戶右擊時被瀏覽器控件顯示的快捷菜單的控制。你能通過在這個方法中返回S_OK?阻止IE顯示它的默認快捷菜單。返回一些其他的數值?,?像S_FALSE或E_NOTIMPL,允許IE繼續執行它的默認快捷菜單行為。
如果你僅僅在這個方法中返回S_OK,?你能避免任何瀏覽器控件的右擊行為。?這可能是你在許多場合中的全部需求,但是你能做到更多。通常,你使用這一個方法在返回?S_OK?之前產生并且顯示你自己的快捷菜單。如果你知道瀏覽器控件顯示的菜單的資源,而且它如何選擇他們,你能也有效地自定義默認的瀏覽器控件快捷菜單。讓我們看看它如何工作。
瀏覽器控件由Shdoclc.dll獲得它的快捷菜單資源。這個知識和一些?#define給予你一個機會操縱瀏覽器的菜單。讓我們舉例來說,假定你對默認菜單感到滿意,除了你想要除去查看源代碼項之外。下列代碼載入來自Shdoclc.dll的瀏覽器控件快捷菜單資源,根據環境選擇正確的菜單,移除IDM_VIEWSOURCE命令對應的菜單項,然后顯示菜單。
例子
HRESULT?CBrowserHost::ShowContextMenu(DWORD?dwID,
????POINT?*ppt,
????IUnknown?*pcmdTarget,
????IDispatch?*pdispObject)
{
????#define?IDR_BROWSE_CONTEXT_MENU?24641
????#define?IDR_FORM_CONTEXT_MENU?24640
????#define?SHDVID_GETMIMECSETMENU?27
????#define?SHDVID_ADDMENUEXTENSIONS?53
????HRESULT?hr;
????HINSTANCE?hinstSHDOCLC;
????HWND?hwnd;
????HMENU?hMenu;
????CComPtr<IOleCommandTarget>?spCT;
????CComPtr<IOleWindow>?spWnd;
????MENUITEMINFO?mii={0};
????CComVariant?var,?var1,?var2;
????hr?=?pcmdTarget->QueryInterface(IID_IOleCommandTarget,?(void**)&spCT);
????hr?=?pcmdTarget->QueryInterface(IID_IOleWindow,?(void**)&spWnd);
????hr?=?spWnd->GetWindow(&hwnd);
????hinstSHDOCLC?=?LoadLibrary(TEXT("SHDOCLC.DLL"));
????if?(hinstSHDOCLC?==?NULL)
????{
????????//?載入模塊錯誤?--?盡可能安全地失敗
????????return;
????}
????hMenu=LoadMenu(hinstSHDOCLC,
????????MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));
????hMenu=GetSubMenu(hMenu,dwID);
????//獲得語言子菜單
????hr?=?spCT->Exec(&CGID_ShellDocView,?SHDVID_GETMIMECSETMENU,?0,?NULL,?&var);
????mii.cbSize?=?sizeof(mii);
????mii.fMask?=?MIIM_SUBMENU;
????mii.hSubMenu?=?(HMENU)?var.byref;
????//加入語言子菜單到編碼上下文菜單
????SetMenuItemInfo(hMenu,?IDM_LANGUAGE,?FALSE,?&mii);
????//插入來自注冊表的快捷菜單擴展
????V_VT(&var1)?=?VT_INT_PTR;
????V_BYREF(&var1)?=?hMenu;
????V_VT(&var2)?=?VT_I4;
????V_I4(&var2)?=?dwID;
????hr?=?spCT->Exec(&CGID_ShellDocView,?SHDVID_ADDMENUEXTENSIONS,?0,?&var1,?&var2);
????//刪除查看源代碼
????DeleteMenu(hMenu,?IDM_VIEWSOURCE,?MF_BYCOMMAND);
????//顯示快捷菜單
????int?iSelection?=?::TrackPopupMenu(hMenu,
????????TPM_LEFTALIGN?|?TPM_RIGHTBUTTON?|?TPM_RETURNCMD,
????????ppt->x,
????????ppt->y,
????????0,
????????hwnd,
????????(RECT*)NULL);
????//發送選定的快捷菜單項目指令到外殼
????LRESULT?lr?=?::SendMessage(hwnd,?WM_COMMAND,?iSelection,?NULL);
????FreeLibrary(hinstSHDOCLC);
????return?S_OK;
}
安全警告:不正確地使用LoadLibrary能載入錯誤的動態鏈接庫(DLL)來威脅你的應用程序的安全。關于該如何正確地用微軟Windows?的不同版本載入DLL的信息,參照LoadLibrary的文檔。
IDocHostUIHandler::GetExternal:?擴充文檔對象模型
IDocHostUIHandler?提供一個讓你用在你自己的應用程序中實現的你自己的對象,方法和特性擴充IE文檔對象模型?(DOM)的方法。你的實現是提供給MSHTML一個IDispatch接口指針,指向你自定義的COM自動化對象,實現你自定義的對象、屬性和方法。這些對象,特性和方法之后可以在瀏覽器控件顯示的任何頁面中通過文檔的外部對象訪問。
這一個方法的實現可以是非常簡單的,?假定你的IDispatch接口在實現IDocHostUIHandler的相同對象上。
HRESULT?CBrowserHost::GetExternal(IDispatch?**ppDispatch)
{
????*ppDispatch?=?this;
????return?S_OK;
}
只要?MSHTML有對你的?IDispatch?的一個指針,MSHTML將會傳遞網頁上對任何外部對象的調用到你的應用程序的自動化方法:
<SCRIPT?language="JScript">
function?MyFunc(iSomeData)
{
????external.MyCustomMethod("Some?text",?iSomeData);
}
</SCRIPT>
你也能使用這技術傳遞整個對象到一個網頁。為了實現它,在你的IDispatch實現中創建一個方法,傳遞回你的網頁可以用的對象。
<SCRIPT?language="JScript">
function?MyFunc(iSomeData)
{
????var?oCustCalendarObj;
????external.GetCustomCalender(oCustCalenderObj);
????oCustCalerdarObj.doStuffWithIt();
}
</SCRIPT>
可以看看示例代碼中使用?ATL的IDispatch自動化實現的一個例子?。
譯者注:IE也擴展了瀏覽器的文檔對象模型,使得你在腳本中可以通過擴展對象的menuArguments屬性訪問當前窗口對象。
IDocHostUIHandler::GetOptionKeyPath
IDocHostUIHandler::GetOptionKeyPath是自定義瀏覽器控件的一個非常有力的工具。?許多瀏覽器控件顯示和行為設定被儲存在注冊表中HKEY_CURRENT_USER鍵的下面。IDocHostUIHandler::GetOptionKeyPath給你一個機會為你的瀏覽器控件的特定實例覆蓋這些注冊表設定。它通過讓你提供一個替代的注冊表位置來實現,瀏覽器控件將會在這里讀取注冊表設置。
IDocHostUIHandler::GetOptionKeyPath的一個實現傳遞給你讓瀏覽器控件讀取注冊表設置的位置的一個字符串。瀏覽器控件將會找尋在HKEY_CURRENT_USER鍵下面的這一個鍵。
例子
HRESULT?CBrowserHost::GetOptionKeyPath(LPOLESTR?*pchKey,
????DWORD?dwReserved)
{
????HRESULT?hr;
????#define?CCHMAX?256
????size_t?cchLength;
????if?(pchKey)
????{
????????WCHAR*?szMyKey?=?L"Software\MyCompany\MyApp";
????????hr?=?StringCchLengthW(szMyKey,?CCHMAX,?&cchLength);
????????//TODO:?在這里處理錯誤。
????????*pchKey?=?(LPOLESTR)CoTaskMemAlloc((cchLength?+?1)?*?sizeof(WCHAR));
????????if?(*pchKey)
????????????hr?=?StringCchCopyW(*pchKey,?cchLength?+?1,?szKey);
????????//TODO:?在這里處理錯誤。
????????hr?=?(*pchKey)???S_OK?:?E_OUTOFMEMORY;
????}
????else
????????hr?=?E_INVALIDARG;
????return?hr;
}
和IDocHostUIHandler::GetHostInfo一樣,確保為你的字符串使用?CoTaskMemAlloc分配內存。
告訴瀏覽器控件該在哪里找尋你的注冊表設置實際上是第一步——就程序運行來說是第二步。?你的程序必須在被IDocHostUIHandler::GetOptionKeyPath告訴的位置設置一個注冊表鍵,這樣瀏覽器控件才可以去讀取。有多種方法來完成這個步驟。一個方法是當應用程序被安裝的時候執行一個注冊表腳本。另外的一個方法是當應用程序啟動的時候,用代碼來完成。這里是改變默認值字體,大小和顏色的一個設定。
例子
HRESULT?SetSomeKeys()
{
????HKEY?hKey?=?NULL;
????HKEY?hKey2?=?NULL;
????HKEY?hKey3?=?NULL;
????DWORD?dwDisposition?=?NULL;
????LONG?lResult?=?NULL;
????#define?CBMAX?256
????size_t?cbLength;
????RegCreateKeyEx(HKEY_CURRENT_USER,?_T("Software\MyCompany\MyApp"),
????????NULL,?NULL,?REG_OPTION_NON_VOLATILE,?KEY_SET_VALUE,
????????NULL,?&hKey,?&dwDisposition);
????RegCreateKeyEx(hKey,?_T("Main"),?NULL,?NULL,?REG_OPTION_NON_VOLATILE,
????????KEY_SET_VALUE,?NULL,?&hKey2,?&dwDisposition);
????RegSetValueEx(hKey2,?_T("Use_DlgBox_Colors"),?NULL,?REG_SZ,
????????(CONST?BYTE*)_T("no"),?sizeof(_T("no")));
????RegCloseKey(hKey2);
????RegCreateKeyEx(hKey,?_T("Settings"),?NULL,?NULL,?REG_OPTION_NON_VOLATILE,
????????KEY_SET_VALUE,?NULL,?&hKey2,?&dwDisposition);
????RegSetValueEx(hKey2,?_T("Anchor?Color"),?NULL,?REG_SZ,
????????(CONST?BYTE*)_T("0,255,255"),?sizeof(_T("0,255,255")));
????RegSetValueEx(hKey2,?_T("Text?Color"),?NULL,?REG_SZ,
????????(CONST?BYTE*)_T("255,0,255"),?sizeof(_T("255,0,255")));
????RegCloseKey(hKey2);
????RegCreateKeyEx(hKey,?_T("International\Scripts"),?NULL,?NULL,
????????REG_OPTION_NON_VOLATILE,?KEY_SET_VALUE,?NULL,
????????&hKey2,?&dwDisposition);
????BYTE?bDefaultScript?=?0x3;
????RegSetValueEx(hKey2,?_T("Default_Script"),?NULL,?REG_BINARY,
????????&bDefaultScript,?sizeof(bDefaultScript));
????RegCreateKeyEx(hKey2,?_T("3"),?NULL,?NULL,?REG_OPTION_NON_VOLATILE,
????????KEY_SET_VALUE,?NULL,?&hKey3,?&dwDisposition);
????BYTE?bSize?=?0x4;?//?Value?from?0?-?4.?2?is?medium.
????TCHAR*?szFontName?=?_T("Comic?Sans?MS");
????TCHAR*?szFixedFontName?=?_T("Courier");
????HRESULT?hr?=?StringCbLength(szFontName,?CBMAX,?&cbLength);
????//TODO:?在這里處理錯誤。
????RegSetValueEx(hKey3,?_T("IEPropFontName"),?NULL,?REG_SZ,
????????(CONST?BYTE*)szFontName,?cbLength?+?sizeof(TCHAR));
????hr?=?StringCbLength(szFixedFontName,?CBMAX,?&cbLength);
????//TODO:?在這里處理錯誤。
????RegSetValueEx(hKey3,?_T("IEFixedFontName"),?NULL,?REG_SZ,
????????(CONST?BYTE*)szFixedFontName,?cbLength?+?sizeof(TCHAR));
????RegSetValueEx(hKey3,?_T("IEFontSize"),?NULL,?REG_BINARY,?&bSize,?sizeof(bSize));
????RegCloseKey(hKey3);
????RegCloseKey(hKey2);
????RegCloseKey(hKey);
????return?S_OK;
}
IDocHostUIHandler2
IDocHostUIHandler2?只有一個方法,IDocHostUIHandler2::GetOverrideKeyPath。它運行非常類似于IDocHostUIHandler::GetOptionKeyPath的一個功能。它指出你修改自默認注冊表設置的集成瀏覽器使用的注冊表設置的位置。IDocHostUIHandler2::GetOverrideKeyPath?的一個實現看起來會很類似于IDocHostUIHandler::GetOptionKeyPath的一個實現。
GetOptionKeyPath?和?GetOverrideKeyPath?的比較
你或許沒看到IDocHostUIHandler::GetOptionKeyPath和IDocHostUIHandler2::GetOverrideKeyPath之間的任何不同。在他們之間的不同是微妙的,?但是重要的。如果你實現?IDocHostUIHandler::GetOptionKeyPath,你的瀏覽器控件實例將會忽略任何IE的用戶設定。這些設定被儲存在注冊表的HKEY_CURRENT_USER/Software/Microsoft/Internet?Explorer下面。如果你實現IDocHostUIHandler2::GetOverrideKeyPath,你的瀏覽器控件實例將會合并任何的用戶設定—字體設定,菜單擴展,諸如此類——到它的顯示和行為中。
舉例說明在IDocHostUIHandler::GetOptionKeyPath和IDocHostUIHandler2::GetOverrideKeyPath之間的不同,讓我們重新看看IDocHostUIHandler::ShowContextMenu那段的示例代碼。記住這一行:
spCT->Exec(&CGID_ShellDocView,?SHDVID_ADDMENUEXTENSIONS,?0,?&var1,?&var2);
如果你已經實現IDocHostUIHandler::GetOptionKeyPath,因為菜單擴展信息被儲存在當前用戶的注冊表信息中,所以這一行不會加入任何自定義項目到快捷菜單。如果你已經實現IDocHostUIHandler2::GetOverrideKeyPath,?這一個行會添加在HKEY_CURRENT_USER/Software/Microsoft/Internet?Explorer/MenuExt面定義的任何目前用戶定義的菜單擴展,?除非你明確地在你的自定義注冊信息位置提供一個空的或替代的MenuExt鍵。
控制導航
你可能想知道在IDocHostUIHandler那一節為什么不提到?IDocHostUIHandler::TranslateUrl,作為在你希望控制頁面導航時實現的方法。原因是這一個方法不是控制導航的最通用的技術。?除非你直接地集成MSHTML,這一個方法將沒有控制導航的效果。作為替代,通過實現IDispatch::Invoke,處理DISPID_BEFORENAVIGATE2,你可以控制導航。例如,下列代碼避免導航到一個特別的網址,如果使用者嘗試這么做,會顯示?"沒有允許導航"?錯誤頁。
例子
case?DISPID_BEFORENAVIGATE2:
{
????CComBSTR?url?=?((*pDispParams).rgvarg)[5].pvarVal->bstrVal;
????if?(url?==?"http://www.adatum.com"?||?url?==?"http://www.adatum.com/")
????{
????????CComPtr<IWebBrowser2>?spBrowser;
????????CComPtr<IDispatch>?spDisp?=?((*pDispParams).rgvarg)[6].pdispVal;
????????spDisp->QueryInterface(IID_IWebBrowser2,?(void**)&spBrowser);
????????spBrowser->Stop();
????????CComBSTR?newURL?=?"L"res://webhost.exe/nonavigate.htm";
????????spBrowser->Navigate(newURL,?NULL,?NULL,?NULL,?NULL);
????????((*pDispParams).rgvarg)[0].boolVal?=?TRUE;
????}
????break;
}
IDocHostShowUI
這個接口給你對瀏覽器控件顯示的信息對話框和幫助的控制。它工作機理和IDocHostUIHandler和IDocHostUIHandler2一樣,你實現它,這樣在瀏覽器控件顯示它自己的任何的信息或幫助之前?,能調用你的IDocHostShowUI的方法。這給你一個機會阻止瀏覽器控件顯示任何東西,而且使你能夠改為顯示你自己的自定義信息或幫助。?IDocHostShowUI有兩個方法,IDocHostShowUI::ShowMessage和IDocHostShowUI::ShowHelp。
IDocHostShowUI::ShowMessage
返回?S_OK禁用瀏覽器控件的信息對話框。任何其他的返回數值,像S_FALSE或E_NOTIMPL,允許瀏覽器控件顯示它的信息對話框。
你通過這個方法能做的一件好的事情是為你的應用程序自定義信息框標題,替代?"Microsoft?Internet?Explorer"?。你能通過比較lpstrCaption和儲存在Shdoclc.dll中的IE使用的字符串資源來完成它。它的ID是IDS_MESSAGE_BOX_TITLE,數值是2213。下列示例代碼演示你可能需要做的工作。
例子
HRESULT?CBrowserHost::ShowMessage(HWND?hwnd,
????LPOLESTR?lpstrText,
????LPOLESTR?lpstrCaption,
????DWORD?dwType,
????LPOLESTR?lpstrHelpFile,
????DWORD?dwHelpContext,
????LRESULT?*plResult)
{
????USES_CONVERSION;
????TCHAR?pBuffer[50];
????//?窗口標題"Microsoft?Internet?Explorer"的資源標識
????#define?IDS_MESSAGE_BOX_TITLE?2213
????//載入Shdoclc.dll?和IE消息框標題字符串
????HINSTANCE?hinstSHDOCLC?=?LoadLibrary(TEXT("SHDOCLC.DLL"));
????if?(hinstSHDOCLC?==?NULL)
????{
????????//?載入模塊錯誤?--?盡可能安全地失敗
????????return;
????}
????LoadString(hinstSHDOCLC,?IDS_MESSAGE_BOX_TITLE,?pBuffer,?50);
????//?比較IE消息框標題字符串和lpstrCaption
????//?如果相同,用自定義標題替換
????if?(_tcscmp(OLE2T(lpstrCaption),?pBuffer)?==?0)
????????lpstrCaption?=?L"Custom?Caption";
????//?創建自己的消息框并且顯示
????????*plResult?=?MessageBox(OLE2T(lpstrText),?OLE2T(lpstrCaption),?dwType);
????//卸載Shdoclc.dll并且返回
????FreeLibrary(hinstSHDOCLC);
????return?S_OK;
}
安全警告:不正確地使用LoadLibrary能載入錯誤的動態鏈接庫(DLL)來威脅你的應用程序的安全。關于該如何正確地用微軟Windows的不同版本載入DLL的信息,參照?LoadLibrary的文檔。
IDocHostShowUI::ShowHelp
這一個方法在當IE需要顯示幫助時被調用,舉例來說當?F1?鍵被按下時,而且工作方式和IDocHostShowUI::ShowMessage類似。返回S_OK覆蓋IE的幫助,或另外的HRESULT值讓IE執行自己的幫助。
控制下載和執行
瀏覽器控件給你它的下載,顯示設置和執行的控制權。?為了要得到這些控制,你實現你的宿主的IDispatch接口,使得它處理DISPID_AMBIENT_DLCONTROL。當瀏覽器控件被實例化的時候,它將會以這一個ID調用你的IDispatch::Invoke。將pvarResult設置為下列的標識的一個位與的組合,指明你的配置。
·?DLCTL_DLIMAGES?,?DLCTL_VIDEOS?和?DLCTL_BGSOUNDS:?如果這些標識被設定,圖像,視頻和背景音樂將會被從服務器下載并且顯示或播放,否則將不被下載和顯示。
·?DLCTL_NO_SCRIPTS?和?DLCTL_NO_JAVA:?腳本和Java小程序將不被運行。
·?DLCTL_NO_DLACTIVEXCTLS?和?DLCTL_NO_RUNACTIVEXCTLS:?ActiveX?控件將不被下載或者運行。
·?DLCTL_DOWNLOADONLY:?網頁只將會被下載,不顯示。
·?DLCTL_NO_FRAMEDOWNLOAD:瀏覽器控件將會下載并且解析框架集頁面,但是不會下載和解析框架集中單獨的框架。
·?DLCTL_RESYNCHRONIZE?和?DLCTL_PRAGMA_NO_CACHE:?這些標志導致Internet緩沖的刷新。通過?DLCTL_RESYNCHRONIZE,服務器將會被請求更新狀態。如果服務器指出緩存信息是最新的,將會使用?緩存文件。通過DLCTL_PRAGMA_NO_CACHE,不管文件的更新狀態如何,文件都會被從服務器重新下載。
·?DLCTL_NO_BEHAVIORS:?行為不被下載并且在文件中被禁用。
·?DLCTL_NO_METACHARSET_HTML:?忽略在META元素中指明的字符集。
·?DLCTL_URL_ENCODING_DISABLE_UTF8?和?DLCTL_URL_ENCODING_ENABLE_UTF8:?這些標志的功能類似于IDocHostUIHandler::GetHostInfo中使用的DOCHOSTUIFLAG_URL_ENCODING_DISABLE_UTF8和DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8標志。不同是只有在瀏覽器控件被初始化的時候,DOCHOSTUIFLAG標志才會被檢查。這里的環境特性變化的下載標志在每當瀏覽器控件需要運行一個下載時被檢查。
·?DLCTL_NO_CLIENTPULL:?不運行客戶端重定位頁面操作(譯者注:例如<meta?http-equiv="refresh"?content="30">?的默認行為)。
·?DLCTL_SILENT:?在下載期間沒有用戶界面顯示。
·?DLCTL_FORCEOFFLINE:?瀏覽器控件總是在脫機模式中操作。
·?DLCTL_OFFLINEIFNOTCONNECTED?和?DLCTL_OFFLINE:?這些標志是相同的。如果不連接到英特網,瀏覽器控件將會在脫機模式中操作。
DISPID_AMBIENT_DLCONTROL和標志的數值是在mshtmdid.h被定義的。
最初,當對IDispatch::Invoke調用開始的時候,?pvarResult參數指向的VARIANT是VT_EMPTY類型。?你必須為任何有效的設定設置它為VT_I4類型。你可以在VARIANT的lVal成員中存儲標志數值。
大部份標志數值有否定的效果,也就是說,他們避免行為正常地發生。舉例來說,如果你不自定義瀏覽器控件行為,那么通常腳本會被執行。?但是如果你設定DLCTL_NOSCRIPTS?標志,腳本將不會在控制的那個實例中運行。然而,三個標志—?DLCTL_DLIMAGES?,?DLCTL_VIDEOS?和?DLCTL_BGSOUNDS的作用正好相反。你必須全部設置標志,使得瀏覽器控件以它的默認行為執行關于圖像,視頻和聲音的處理。
下列示例代碼使得一個瀏覽器控件實例下載并且顯示圖像和視頻,但是不處理背景音樂,因為DLCTL_BGSOUNDS沒有被明確地設定。瀏覽器控件顯示的頁上的腳本運行被禁用。
STDMETHODIMP?CAtlBrCon::Invoke(DISPID?dispidMember,?REFIID?riid,
????LCID?lcid,?WORD?wFlags,
????DISPPARAMS*?pDispParams,
????VARIANT*?pvarResult,
????EXCEPINFO*?pExcepInfo,
????UINT*?puArgErr)
{
????switch?(dispidMember)
????{
????????case?DISPID_AMBIENT_DLCONTROL:
????????????pvarResult->vt?=?VT_I4;
????????????pvarResult->lVal?=?DLCTL_DLIMAGES?|?DLCTL_VIDEOS?|?DLCTL_NO_SCRIPTS;
????????????break;
????????default:
????????????return?DISP_E_MEMBERNOTFOUND;
????}
????return?S_OK;
}
IHostDialogHelper
IHostDialogHelper是一個你能根據你的愛好創建對話框的接口。這一個接口有一個方法,IHostDialogHelper::ShowHTMLDialog。這一個方法提供如同功能ShowHTMLDialog一般的服務,但是使用起來稍微比較容易一點。
為了要使用IHostDialogHelper,你從頭產生對話框輔助對象。在這里是你使用CoCreateInstance的方式創建它。接口和ID在?mshtmhst.h?中被定義。
例子
IHostDialogHelper*?pHDH;
IMoniker*?pUrlMoniker;
BSTR?bstrOptions?=?SysAllocString(L"dialogHeight:30;dialogWidth:40");
BSTR?bstrPath?=?SysAllocString(L"c:\dialog.htm");
CreateURLMoniker(NULL,?bstrPath,?&pUrlMoniker);
//?創建對話框輔助對象
CoCreateInstance(CLSID_HostDialogHelper,
????NULL,
????CLSCTX_INPROC,
????IID_IHostDialogHelper,
????(void**)&pHDH);
//調用ShowHTMLDialog?創建對話框
pHDH->ShowHTMLDialog(NULL,
????pUrlMoniker,
????NULL,
????bstrOptions,
????NULL,
????NULL);
//釋放資源
SysFreeString(bstrPath);
SysFreeString(bstrOptions);
pUrlMoniker->Release();
pHDH->Release();
譯者注:如果要使用對話框來獲得用戶輸入,你可能需要傳遞兩個參數到ShowHTMLDialog。關于ShowHTMLDialog參數的說明,參見Platform?SDK文檔。ShowHTMLDialog和ShowHTMLDialogEx?似乎一直是MSHTML.DLL導出的兩個函數,微軟把它封裝為接口,可能是在為未來的兼容性作準備。
控制新的窗口
控制瀏覽器控件的一個重要的方法是控制導航。你在前面已經看見如何在IDispatch::Invoke中攔截DISPID_BEFORENAVIGATE2來實現控制你的瀏覽器控件的導航位置。另外一個導航的重要的方面是要控制導航發生方式,?尤其是打開新的窗口的時候。讓我們舉例來說,?使用者右擊一個鏈接,選擇?"在新窗囗中打開"?或某一頁包含像這樣的腳本:
window.open("www.msn.com")
默認地,瀏覽器控件對這行代碼的處理是通過打開IE的一個新的實例來顯示網頁。這可能正好是你的應用程序需要的,但是也可能不是。也許你需要在當前的瀏覽器控件實例中打開所有鏈接,或者你將在你控制下的瀏覽器控件的一個新的實例——具有你的用戶界面和你的商標——打開鏈接。
你可以在你的IDispatch實現中攔截一個事件——DWebBrowserEvents2::NewWindow2——來控制它。你的控制需要連接到DWebBrowserEvents2的連接點來攔截這一個事件。
你連接到了DWebBrowserEvents2之后,實現你的IDispatch::Invoke以處理?DISPID_NEWWINDOW2。在為DISPID_NEWWINDOW2的IDispatch::Invoke函數調用中,數組pDispParams包含兩個參數。第一個,序號是零,?是一個布爾類型的數值,告訴瀏覽器控件是否取消新的窗囗。默認它是假值,而且將會打開一個新的窗囗。如果你要完全取消新窗囗的創建,?設定標志到真值。
序號為一的參數是一個IDispatch接口的指針。你可以將這一個參數設定為你已經創建的瀏覽器控件的IDispatch。當你傳回這樣一個IDispatch之后,MSHTML將會使用你給出的控件打開鏈接。
譯者注:MFC中的DHTML類和類向導默認支持這個事件。需要更多信息的話,參見MSJ1998年7月份的文章Keeping?an?Eye?on?Your?Browser?by?Monitoring?Internet?Explorer?4.0?Events,以及?微軟知識庫文章?Q184876?HOWTO:?Use?the?WebBrowser?Control?NewWindow2?Event
顯示一個正數對話框
IE6或者更高版本中,你可以在用戶瀏覽一個合法的安全超文本傳輸協議(HTTPS)站點的時候顯示證書對話框。這和用戶點擊IE中的鎖圖標效果相同。你可以通過?DWebBrowserEvents2::SetSecureLockIcon事件來顯示你自己的圖標。
#define?SHDVID_SSLSTATUS?33
IOleCommandTarget?*pct;
if?(SUCCEEDED(pWebBrowser2->QueryInterface(IID_IOleCommandTarget,?(void?**)?&pct)))
{
???pct->Exec(&CGID_ShellDocView,?SHDVID_SSLSTATUS,?0,?NULL,?NULL);
???pct->Release();
}
信息欄
Windows?XP?SP2?中的Internet?Explorer?6?引入了一個新的安全用戶界面元素,稱為信息欄。在特定操作被阻止的時候,信息欄給用戶顯示一個用戶界面元素。特別的,它會在以下操作被阻止的時候顯示。
·?彈出窗口初始化(參見?彈出窗口殺手)
·?
·?文件下載?(see?文件下載的限制)
·?
·?安裝ActiveX?控件(see?ActiveX?的限制)
·?
·?ActiveX控件安全提示的原因是用戶安全設置或者是控件未標記為腳本安全的。
·?
·?文件的擴展名和多用途因特網郵件擴展類型(MIME)不符的(參見?MIME?處理)
·?
·?網絡協議鎖死的內容(參見?協議)
·?
信息欄是Windows?XP?SP2?中的Internet?Explorer?6引入的安全特性之一。和其他安全特性控制一樣,可以通過一個注冊表鍵來控制:(FEATURE_SECURITYBAND).?默認情況下IE(iexplorer.exe)?和Windows?資源管理器(explorer.exe)?在這個安全特性控制下。下面顯示注冊表鍵和啟用過程:
HKEY_LOCAL_MACHINE?(or?HKEY_CURRENT_USER)
SOFTWARE
Microsoft
Internet?Explorer
Main
FeatureControl
FEATURE_SECURITYBAND
iexplorer.exe=?0x00000001
explorer.exe=?0x00000001
process?name.exe=0x00000001
這個FEATURE_SECURITYBAND?安全特性控制影響IE是否顯示信息欄,信息欄在一個操作被阻止的時候提示用戶。它不控制操作的阻止過程。
一個集成瀏覽器控件的程序可以通過將其進程添加到這個注冊表鍵來啟用信息欄。這可以通過調用CoInternetSetFeatureEnabled函數來在運行時執行。如果一個應用程序并未在這個安全特性控制下,那么瀏覽器控件的行為和Internet?Explorer?6?SP1b中的一樣.
沒有方法通過腳本來訪問這個特性。
在FEATURE_SECURITYBAND及相關安全特性控制下的應用程序可以使用信息欄應用程序編程接口(API)來在一個URL?操作被禁止時自定義顯示的用戶界面。為信息欄引入了很多新的OLECMDID命令。頭三個是屬于CGID_DocHostCommandHandler組。宿主應用程序應該在它們的IDocHostUIHandler實現的同一個對象中實現IOleCommandTarget?,以接受來自瀏覽器控件的IOleCommandTarget::Exec調用。
·?OLECMDID_PAGEACTIONBLOCKED
·?
·?OLECMDID_PAGEACTIONUIQUERY
·?
·?OLECMDID_FOCUSVIEWCONTROLS
·?
宿主應用程序可以使用下面兩個新的OLECMDID?命令來執行瀏覽器控件的IOleCommandTarget::Exec調用。
·?OLECMDID_FOCUSVIEWCONTROLSQUERY
·?
·?OLECMDID_SHOWPAGEACTIONMENU
·?
這個示例使用IWebBrowser2::ExecWB?來執行OLECMDID_SHOWPAGEACTIONMENU?命令。
???POINT?pt?=?{?0?};
???GetCursorPos(&pt);
???CComVariant?varHwnd((LONG)hwnd);
???CComVariant?varX(pt.x);
???CComVariant?varY(pt.y);
???SAFEARRAY*?psa?=?SafeArrayCreateVector(VT_VARIANT,?0,?3);
???LONG?lIndex?=?0;
???SafeArrayPutElement(psa,?&lIndex,?&varHwnd);
???lIndex++;
???SafeArrayPutElement(psa,?&lIndex,?&varX);
???lIndex++;
???SafeArrayPutElement(psa,?&lIndex,?&varY);
???CComVariant?varArgIn;
???V_VT(&varArgIn)?=?VT_ARRAY?|?VT_I4;
???V_ARRAY(&varArgIn)?=?psa;
???pBrowser->ExecWB(OLECMDID_SHOWPAGEACTIONMENU,?(OLECMDEXECOPT)dwPageActionFlags,?&varArgIn,?NULL);
另外,應用程序可以實現IInternetSecurityManager來重載默認的安全區域設置,參見創建一個自定義URL安全管理器以獲得更多信息.
結論
你現在有許多技術,可以根據你的處理來自定義瀏覽器控件。這個文章決不是沒有遺漏的,但是希望你現在可以自行發現超越本文的技術。檢查IE注冊表設置中那些你可以用IDocHostUIHandler::GetOptionKeyPath或IDocHostUIHandler2::GetOverrideKeyPath修改的信息。記住許多注冊表設置相互依賴。你可能必須做一些實驗來發現注冊表設置可以多么的有效地自定義;如果需要控制瀏覽器控件的拖放行為,你也可以去看看IDocHostUIHandler::GetDropTarget。
C#?WinForm?WebBrowser?(五)?討厭的問題?
WebBrowse?編輯模式?中幾個討厭的問題:
1、當設置DocumentText屬性值時會一直彈出一個“可惡的保存對話框”
現我已知的較好的策略有:
1)在設置兩個DocumentText屬性值之間使用webBrowser1.Document.OpenNew(true)方法,但這個方法會引發一些問題。詳細內容見下。
2)在設置DocumentText屬性之前將編輯模式改為瀏覽模式,設置完后再將瀏覽模式改為編輯模式。
2、監控Html內容的改變。
監控?WebBrowser?控件內容的改變
3、WebBrowse的Mouse事件。
WebBrowse本身沒有Mouse的相關事件,但是我們可以借助WebBrowse中的Body元素來模擬一些簡單的相關事件。
//在WinForm中注冊Web事件
//假設HTML源代碼如下:
<html>?
<body>?
<input?type="button"?id="btnClose"?value="關閉"?/>?
</body>?
</html>
?
//?WinForm中注冊Web事件
HtmlDocument?htmlDoc?=?webBrowser.Document;?
HtmlElement?btnElement?=?htmlDoc.All["btnClose"];?
if?(btnElement?!=?null)?
{?
????btnElement.click?+=?new?HtmlElementEventHandler(HtmlBtnClose_Click);?
}
WebBrowse中更多的實用方法參見:C#WinForm?WebBrowser?(二)?實用方法總結
4、webBrowser1.Document.OpenNew(true)的Bug
相關鏈接:
1)追蹤導致?WebBrowser控件?編輯模式下?Ctrl?+?Z?失效的原因?【求大俠佐證,這算不算是微軟的Bug呢?】
追蹤導致?WebBrowser控件?編輯模式下?Ctrl?+?Z?失效的原因?【求大俠佐證,這算不算是微軟的Bug呢?】?
導致Ctrl??+?Z失效的原因由以下2點連鎖引發而導致:
1、為了解決?WebBrowser?控件導航時彈出“保存對話框”,使用了?this.webBrowser.Document.OpenNew(true);?//?防止?彈出保存對話框,?該方法指示新的文本改變將會在新窗口中打開。
2、?由原因1導致?webBrowser?控件的編輯模式失效,?表面上看上去還是可以編輯的,但實際上新窗口內部已經不支持編輯了。
注:這里涉及到了WebBrowser控件的特殊性,它是由三層控件嵌套而成的,外面的兩層是大概負責容器、?及?響應用戶操作的,?而最內層的則是承載HTML標記,并通過渲染引擎展示HTML內容。用黑盒測試的方法推斷,當使用webBrowser.Document.OpenNew(true);??方法時,最內層控件應該是一個新的實例,?表面上看上去還是可以編輯的,但實際上內部的新窗口已經不支持編輯了,進而導致了Ctrl?+?Z的失效!
測試代碼如下:
public?partial?class?FrmTest?:?Form
{
//?界面上有一個WebBrowser?和?4個Button
private?string?strUrl?=?"http://www.cnblogs.com/08shiyan";
public?FrmTest()
{
InitializeComponent();
}
///?<summary>
///?編輯模式
///?</summary>
public?void?EditMode()
{
if?(this.webBrowser1.Document?!=?null)
{
mshtml.IHTMLDocument2?doc?=?this.webBrowser1.Document.DomDocument?as?mshtml.IHTMLDocument2;
doc.designMode?=?"on";
}
}
///?<summary>
///?啟用瀏覽模式
///?</summary>
public?void?BrowseMode()
{
if?(this.webBrowser1.Document?!=?null)
{
mshtml.IHTMLDocument2?doc?=?this.webBrowser1.Document.DomDocument?as?mshtml.IHTMLDocument2;
doc.designMode?=?"off";
}
}
//?請確保該按鈕是應用程序啟動后第一次被點擊
private?void?button1_Click(object?sender,?EventArgs?e)
{
this.webBrowser1.DocumentText?=?string.Empty;
this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>",?this.strUrl));
this.EditMode();
this.webBrowser1.Document.OpenNew(true);
this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>",?this.strUrl));
//?注意此時Ctrl?+?Z?撤銷操作將會失效
}
//?請確保該按鈕是應用程序啟動后第一次被點擊
private?void?button2_Click(object?sender,?EventArgs?e)
{
this.webBrowser1.DocumentText?=?string.Empty;
this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>",?this.strUrl));
this.EditMode();
this.webBrowser1.Document.OpenNew(true);
this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>",?this.strUrl));
this.EditMode();?//?與button1的差別是再次啟用編輯模式?//?注意此時Ctrl?+?Z?撤銷操作也會失效
}
//?請確保該按鈕是應用程序啟動后第一次被點擊
private?void?button3_Click(object?sender,?EventArgs?e)
{
this.webBrowser1.DocumentText?=?string.Empty;
this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>",?this.strUrl));
this.EditMode();
this.webBrowser1.Document.OpenNew(true);
this.webBrowser1.Document.Write(string.Format("<BODY>{0}我的誓言博客2</BODY>",?this.strUrl));
this.BrowseMode();?//?與button2?的區別是?先關閉編輯模式,再啟用編輯模式
this.EditMode();
//?此時?Ctrl?+?Z?可以使用
}
//?重啟應用程序
private?void?button4_Click(object?sender,?EventArgs?e)
{
Application.Restart();
}
}
根據以上得出結論:
在“編輯模式”下:?this.webBrowser.Document.OpenNew(true);?方法會打開一個新的“內部窗口”,而新窗口中“編輯模式”出現問題,導致Ctrl?+?Z?,Ctrl?+Y操作失效。?此時?需要先關閉?“編輯模式”?然后再打開“編輯模式”?Ctrl?+?Z,?Ctrl?+Y?才能正常使用。
求佐證。。。
原創?轉載請標明出處:?http://www.cnblogs.com/08shiyan
2)http://topic.csdn.net/u/20110318/14/e3744686-3dae-4152-a1d8-c0011e8f355c.html
更多的關于WebBrowse的問題。
轉載于:https://www.cnblogs.com/aqdaye/p/5167360.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的c#winform使用WebBrowser 大全的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS内置方法(Array)
- 下一篇: 生成JavaDoc