C# WinForm WebBrowser (一) MSDN資料 html
1、主要用途:使用戶能夠在窗體中導航網頁。程序員
2、注意:WebBrowser 控件會佔用大量資源。使用完該控件後必定要調用 Dispose 方法,以便確保及時釋放全部資源。必須在附加事件的同一線程上調用 Dispose 方法,該線程應始終是消息或用戶界面 (UI) 線程。web
3、WebBrowser 使用下面的成員能夠將控件導航到特定 URL、在導航歷史記錄列表中向後和向前移動,還能夠加載當前用戶的主頁和搜索頁:編程
1.URL屬性:可讀、可寫,用於獲取或設置當前文檔的 URL。 windows
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 屬性被設置爲當前窗體。
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 (三) 編輯模式
1、啓用編輯模式、 瀏覽模式 及 自動換行
/// <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
————————————————————————————————————————————————————————————————
因爲本人在開發中常常要在程序中嵌入瀏覽器,爲了符合本身的需求常常要對瀏覽器進行擴展和定製, 解決這些問題需在網上找資料和學習的過程,我想可能不少開發者或許會遇到一樣的問題,特寫此文,以供你們參考。
在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
---------------------------------------------------------------------------
...... 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對應。
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 ondragenter="event.returnValue = false" ondragover="event.returnValue = false" \
ondrop="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 ,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告訴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 提供一個讓你用在你本身的應用程序中實現的你本身的對象,方法和特性擴充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。
返回 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的文檔。
這一個方法在當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內容的改變。
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