C# WinForm使用WebBrowser 大全

转载:http://blog.csdn.net/xjj800211/article/details/7814196html

 

一、主要用途:使用户能够在窗体中导航网页。程序员

二、注意:WebBrowser 控件会占用大量资源。使用完该控件后必定要调用 Dispose 方法,以便确保及时释放全部资源。必须在附加事件的同一线程上调用 Dispose 方法,该线程应始终是消息或用户界面 (UI) 线程。web

三、WebBrowser 使用下面的成员能够将控件导航到特定 URL、在导航历史记录列表中向后和向前移动,还能够加载当前用户的主页和搜索页:编程

1.URL属性:可读、可写,用于获取或设置当前文档的 URL。 小程序

WebBrowser 控件维护浏览会话期间访问的全部网页的历史记录列表。设置Url属性时,WebBrowser 控件导航到指定的 URL 并将该 URL 添加到历史记录列表的末尾。windows

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 事件。


四、ObjectForScripting 属性:获取或设置一个对象,该对象可由显示在 WebBrowser 控件中的网页所包含的脚本代码访问。

使用该属性启用 WebBrowser 控件承载的网页与包含 WebBrowser 控件的应用程序之间的通讯。使用该属性能够将动态 HTML (DHTML) 代码与客户端应用程序代码集成在一块儿。为该属性指定的对象可做为 window.external 对象(用于主机访问的内置 DOM 对象)用于网页脚本。

能够将此属性设置为但愿其公共属性和方法可用于脚本代码的任何 COM 可见的对象。能够经过使用 ComVisibleAttribute 对类进行标记使其成为 COM 可见的类。

若要从客户端应用程序代码调用网页中定义的函数,请使用可从 Document 属性检索的 HtmlDocument 对象的 HtmlDocument.InvokeScript 方法。

五、AllowNavigation属性:获取或设置一个值,该值指示控件在加载其初始页以后是否能够导航到其余页。

六、AllowWebBrowserDrop属性:获取或设置一个值,该值指示 WebBrowser 控件是否导航到拖放到它上面的文档。

七、WebBrowserShortcutsEnabled属性:是否启用WebBrowser自带的快捷键。

八、ScriptErrorsSuppressed 属性:获取或设置一个值,该值指示出现脚本错误时,WebBrowser 控件是否显示错误对话框。

九、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 (三) 编辑模式 

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

 

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

---------------------------------------------------------------------------

...... 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 , 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 {

    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 编辑模式 中几个讨厌的问题:

一、当设置DocumentText属性值时会一直弹出一个“可恶的保存对话框”

现我已知的较好的策略有:
1)在设置两个DocumentText属性值之间使用webBrowser1.Document.OpenNew(true)方法,但这个方法会引起一些问题。详细内容见下。
2)在设置DocumentText属性以前将编辑模式改成浏览模式,设置完后再将浏览模式改成编辑模式。


二、监控Html内容的改变。

监控 WebBrowser 控件内容的改变


三、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 (二) 实用方法总结


四、webBrowser1.Document.OpenNew(true)的Bug

相关连接:
1)追踪致使 WebBrowser控件 编辑模式下 Ctrl + Z 失效的缘由 【求大侠佐证,这算不算是微软的Bug呢?】

追踪致使 WebBrowser控件 编辑模式下 Ctrl + Z 失效的缘由 【求大侠佐证,这算不算是微软的Bug呢?】 


致使Ctrl  + Z失效的缘由由如下2点连锁引起而致使:

一、为了解决 WebBrowser 控件导航时弹出“保存对话框”,使用了 this.webBrowser.Document.OpenNew(true); // 防止 弹出保存对话框, 该方法指示新的文本改变将会在新窗口中打开。

二、 由缘由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 才能正常使用。

求佐证。。。

相关文章
相关标签/搜索