﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-旅途-文章分类-BHO</title><link>http://www.cppblog.com/mydriverc/category/4795.html</link><description>如果想飞得高，就该把地平线忘掉</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 13:36:41 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 13:36:41 GMT</pubDate><ttl>60</ttl><item><title>ATL 实现定制的 IE 浏览器栏、工具栏和桌面工具栏 </title><link>http://www.cppblog.com/mydriverc/articles/29017.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Mon, 30 Jul 2007 08:00:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/29017.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/29017.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/29017.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/29017.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/29017.html</trackback:ping><description><![CDATA[<p align=center><strong>ATL 实现定制的 IE 浏览器栏、工具栏和桌面工具栏<br></strong><br>作者：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#121;&#97;&#110;&#103;&#102;&#101;&#110;&#103;&#64;&#102;&#116;&#114;&#46;&#99;&#111;&#109;&#46;&#99;&#110;"><font color=#0000ff><u>杨老师</u></font></a></p>
<p><a href="http://www.vckbase.com/code/downcode.asp?id=2675"><u><font color=#0000ff>下载源代码</font></u></a><br><br><strong>关键字</strong>：Band，Desk Band，Explorer Band，Tool Band，浏览器栏，工具栏，桌面工具栏<br><br><strong>一、引言</strong><br>　　最近，由于工作的要求，我需要在 IE 上做一些开发工作。于是在 MSDN 上翻阅了一些资料，根据 MSDN 上的说明我用 ATL 胜利完成了&#8220;资本家老板&#8221;分配的任务。<br>（并且在白天睡觉的过程中梦到了老板给我加工资啦......）<br>现在，我把 MSDN 上的原文资料，经过翻译整理并把一个 ATL 的实现奉贤给 VCKBASE 上的朋友们。</p>
<ul>
    <li><a href="http://www.vckbase.com/document/viewdoc/?id=1457#二、概念"><u><font color=#0000ff>概念</font></u></a>
    <li><a href="http://www.vckbase.com/document/viewdoc/?id=1457#三、原理"><u><font color=#0000ff>原理</font></u></a><br><a href="http://www.vckbase.com/document/viewdoc/?id=1457#3.1　基本_band_对象"><u><font color=#0000ff>基本band 对象</font></u></a><br><a href="http://www.vckbase.com/document/viewdoc/?id=1457#3.2　必须实现的_COM_接口"><u><font color=#0000ff>必须实现的 COM 接口</font></u></a><br>&nbsp;&nbsp;&nbsp; <a href="http://www.vckbase.com/document/viewdoc/?id=1457#IPersistStream"><u><font color=#0000ff>IPersistStream</font></u></a><br>&nbsp;&nbsp;&nbsp; <a href="http://www.vckbase.com/document/viewdoc/?id=1457#IObjectWithSite"><u><font color=#0000ff>IObjectWithSite</font></u></a><br>&nbsp;&nbsp;&nbsp; <a href="http://www.vckbase.com/document/viewdoc/?id=1457#IDeskBand"><font color=#0000ff><u>IDeskBand、IDockingWindow、IOleWindow</u></font></a><br><a href="http://www.vckbase.com/document/viewdoc/?id=1457#3.3　选择实现的_COM_接口"><font color=#0000ff><u>选择实现的 COM 接口</u></font></a><br><a href="http://www.vckbase.com/document/viewdoc/?id=1457#3.4　Band_对象注册"><u><font color=#0000ff>Band 对象注册</font></u></a>
    <li><a href="http://www.vckbase.com/document/viewdoc/?id=1457#四、_ATL_实现"><u><font color=#0000ff>ATL 实现</font></u></a> </li>
</ul>
<p><strong><a name=二、概念>二、概念</a></strong><br>　　在翻译的过程中，有两个词汇非常不好理解。第一个词是 Band 对象，词典中翻译为&#8220;镶边、裙子边、带子、乐队......&#8221;我的英文水平有限，实在不知道应该翻译为什么词汇更合适。于是我毅然决然地决定：在如下的论述中，依然使用 band 这个词！（什么？没听明白？我的意思就是说，我不翻译这个词了）但到底 Band 对象应该如何理解那？请看图一：<br><br><img height=399 alt="" src="http://www.vckbase.com/document/journal/vckbase42/images/yfbands1.jpg" width=613 border=0><br>图一<br><br>　　图一中画红圈的地方，分别称作&#8220;垂直的浏览器栏&#8221;、&#8220;水平的浏览器栏&#8221;、&#8220;工具栏&#8221;和&#8220;桌面工具栏&#8221;。这些&#8220;栏&#8221;，都可以在 IE 的&#8220;查看&#8221;菜单中或鼠标右键的上下文快捷方式菜单中显示或隐藏起来。这些界面窗口的实现，其实就是实现一种 COM 接口对象，而这个对象叫 band。这个概念实在是只能意会而无法言传的，我总不能在文章中把它翻译为&#8220;总是靠在 IE 主窗口边上的对象&#8221;吧？^_^<br>　　另外，还有一个词叫 site。这个很好翻译，叫&#8220;站点&#8221;！。呵呵，我敢打包票，如果你要能理解这个翻译在计算机类文章中的含义，那就只能恭喜你了，你的智慧太高了。（都是学计算机软件的人，做人的差距咋就这么大呢？）在本篇文章中，site 可以这样理解：IE 的主框架四周，就好比是&#8220;汽车站&#8221;，那些 band 对象，就好比是&#8220;汽车&#8221;。band 汽车总是可以停靠在&#8220;汽车站&#8221;上。所以，site 就是&#8220;站点&#8221;，它也是 COM 接口的对象（IObjectWithSite、IInputObjectSite）。<br><br><strong><a name=三、原理>三、原理</a></strong><br><br><em><a name=3.1　基本_band_对象>3.1　基本 band 对象</a></em><br>　　Band 对象，从 Shell 4.71(IE 5.0) 开始提供支持。Band 是一个 COM 对象，必须放在一个容器中去使用，当然使用它们就好象使用普通窗口是一样的。IE 就是一个容器，桌面 Shell 也是一个容器，它们提供不同的函数功能，但基本的实现是相似的。<br>　　Band 对象分三种类型，浏览器栏 band（Explorer bands）、工具栏 band（Tool Bands）和桌面工具栏(Desk bands)，而浏览器栏 band 又有两种表现形式：垂直和水平的。那么 IE 和 Shell 如何区分并加载这些 bands 对象呢？方法是：你要对不同的 band 对象，在注册表中注册不同的组件类型（CATID）。<br><br>
<table cellSpacing=1 width="83%" border=1>
    <tbody>
        <tr>
            <td width="22%">
            <p align=center><strong><span lang=en-us>Band </span>样式</strong></p>
            </td>
            <td width="25%">
            <p align=center><strong>组件类型</strong></p>
            </td>
            <td width="51%">
            <p align=center><strong>CATID</strong></p>
            </td>
        </tr>
        <tr>
            <td align=middle width="22%">垂直的浏览器栏</td>
            <td align=middle width="25%"><font face=宋体 size=3><span lang=en-us>CATID_InfoBand</span></font></td>
            <td align=middle width="51%">00021493-0000-0000-C000-000000000046</td>
        </tr>
        <tr>
            <td align=middle width="22%">水平的浏览器栏</td>
            <td align=middle width="25%"><font face=宋体 size=3><span lang=en-us>CATID_CommBand</span></font></td>
            <td align=middle width="51%">00021494-0000-0000-C000-000000000046</td>
        </tr>
        <tr>
            <td align=middle width="22%">桌面的工具栏</td>
            <td align=middle width="25%"><font face=宋体 size=3><span lang=en-us>CATID_DeskBand</span></font></td>
            <td align=middle width="51%">00021492-0000-0000-C000-000000000046</td>
        </tr>
    </tbody>
</table>
<br>　　IE 工具栏不使用组件类型注册，而是使用在注册进行 CLSID 的登记方式。详细情况见 3.3。<br>　　在例子程序中，实现了全部四个类型的 band 对象，垂直浏览器栏(CVerticalBar)显示了一个 HTML 文件，并且实现了对 IE 主窗口浏览网页的导航等功能；水平的浏览器栏(CHorizontalBar)是一个编辑窗，它同步显示当前网页的 BODY 源文件内容；IE 工具栏(CToolBar)最简单，只是添加了一个空的工具栏；桌面工具栏(CDeskBar)实现了一个单行编辑窗口，你可以在上面输入命令行或文件名称，回车后它会执行 Shell 的打开动作。<br><br><em><a name=3.2　必须实现的_COM_接口>3.2　必须实现的 COM 接口</a></em><br>　　Band 对象是 IE 或 Shell 的进程内服务器，所以它被包装在 DLL 中。而作为 COM 对象，它必须要实现 IUnknown 和 IClassFactory 接口。（大家可以不同操心，因为我们用 ATL 写程序，这两个接口是不用我们自己写代码的。）另外，Band 对象还必须实现 IDeskBand、IObjectWithSite 和 IPersistStream 三个接口：<br>　　<a name=IPersistStream>IPersistStream</a> 是持续性接口的一种。当 IE 加载 band 对象的时候，它通过这个接口的 Load 方法传递属性值给对象，让其进行初始化；而当卸载前，IE 则调用这个接口的 Save 方法保存对象的属性。用 ATL 实现这个接口很简单： </p>
<pre>class ATL_NO_VTABLE Cxxx : 	......	public IPersistStreamInitImpl, // 添加继承	......{public:	BOOL m_bRequiresSave; // IPersistStreamInitImpl 所必须的变量......BEGIN_COM_MAP(CVerticalBar)	......	COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)	COM_INTERFACE_ENTRY2(IPersistStream, IPersistStreamInit)	COM_INTERFACE_ENTRY(IPersistStreamInit)	......END_COM_MAP()BEGIN_PROP_MAP(Cxxx)...... // 添加需要持续性的属性END_PROP_MAP()		</pre>
　　上面的代码，其实实现的是 IPersistStreamInit 接口，不过没有关系，因为 IPersistStreamInit 派生自 IPersistStream，实例化了派生类，自然就实例化了基类。在例子程序中，我只在桌面工具栏对象中添加了持续性属性，用来保存和初始化&#8220;命令行&#8221;。另外 COM_INTERFACE_ENTRY2(A，B)表示的含义是：如果想查询A接口的指针，则提供B接口指针来代替。为什么可以这样那？因为B接口派生自A接口，那么B接口的前几个函数必然就是A接口的函数了，自然B接口的地址其实和A接口的地址是一样的了。<br>　　<a name=IObjectWithSite>IObjectWithSite</a> 是 IE 用来对插件进行管理和通讯用的一个接口。必须要实现这个接口的2个函数：SetSite() 和 GetSite()。当 IE 加载 band 对象和释放 band 对象的时候，都要调用 SetSite()函数，那么在这个函数里正好是写初始化和释放操作代码的地方：
<pre>STDMETHODIMP Cxxx::SetSite(IUnknown *pUnkSite){	if( NULL == pUnkSite )	// 释放 band 的时候	{		// 如果加载的时候，保存了一些接口		// 那么现在：释放它	}	else	// 加载 band 的时候	{		m_hwndParent = NULL;	// 装载 band 的父窗口(就是带有标题的那个框架窗口)		// 这个窗口的句柄，是调用 IUnknown::QueryInterface() 得到 IOleWindow		// 然后调用 IOleWindow::GetWindow() 而获得的。		CComQIPtr&lt; IOleWindow, &amp;IID_IOleWindow &gt; spOleWindow(pUnkSite);		if( spOleWindow )	spOleWindow-&gt;GetWindow(&amp;m_hwndParent);		if( !m_hwndParent )	return E_FAIL;				// 现在，正好是建立子窗口的时机。		// 注意，子窗口建立的时候，不要使用 WS_VISIBLE 属性		... ...		// 在例子程序中，用 CAxWindow 实现了一个能包容ActiveX的容器窗口(垂直浏览器栏)		// 在例子程序中，用 WIN API 函数 CreateWindow 实现了标准窗口(水平浏览器栏、工具栏)		// 在例子程序中，用 CWindowImpl 实现了一个包容窗口(桌面工具栏)		/*********************************************************/		   以下部分，根据 band 对象特有的功能，是可以选择实现的		**********************************************************/				// 如果子窗口实现了用户输入，那么必须实现 IInputObject 接口，		// 而该接口是被 IE 的 IInputObjectSite 调用的，因此在你的对象		// 中，应该保存 IInputObjectSite 的接口指针。		// 在类的头文件中，定义：		// CComQIPtr&lt; IInputObjectSite, &amp;IID_IInputObjectSite &gt; m_spSite;		m_spSite = pUnkSite;	// 保存 IInputObjectSite 指针		if( !m_spSite )		return E_FAIL;		// 你需要控制 IE 的主框架吗？		// 那么在类的头文件中，定义：		// CComQIPtr&lt; IWebBrowser2, &amp;IID_IWebBrowser2 &gt; m_spFrameWB;		// 然后，先取得 IServiceProvider,再取得 IWebBrowser2		CComQIPtr &lt; IServiceProvider, &amp;IID_IServiceProvider&gt; spSP(pUnkSite);		if( !spSP )	return E_FAIL;		spSP-&gt;QueryService( SID_SWebBrowserApp, &amp;m_spFrameWB );		if( !m_spFrameWB)	return E_FAIL;		// 如果你取得了 IE 主框架的 IWebBrowser2 指针		// 那么，当它发生了什么事情，你难道不想知道吗？		// 定义：CComPtr m_spCP;		CComQIPtr&lt; IConnectionPointContainer,			&amp;IID_IConnectionPointContainer&gt; spCPC( m_spFrameWB );		if( spCPC )		{			spCPC-&gt;FindConnectionPoint( DIID_DWebBrowserEvents2, &amp;m_spCP );			if( m_spCP )			{				m_spCP-&gt;Advise( reinterpret_cast&lt; IDispatch * &gt;( this ), &amp;m_dwCookie );			}		}		// 咳~~~ 不说了，看源码去吧。这里能干的事情太多了... ...	}	return S_OK;}		</pre>
<a name=IDeskBand>IDeskBand</a> 是一个特殊的 band 对象接口，有一个方法函数：GetBarInfo()；<br>IDockingWindow 是 IDeskBank 的基类，有3个方法函数：ShowDW()、CloseDW()、ResizeBorderDW()；<br>IOleWindow 又是 IDockingWindow 的基类，有2个方法函数：GetWindow()、ContextSensitiveHelp()； <br><br>　　首先声明 IDeskBand ,然后要实现 IDeskBand 接口的共6个函数，这些函数比较简单，不同类型的 band 对象，其实现方法也都基本一致：
<pre>class ATL_NO_VTABLE Cxxx : 	......	public IDeskBand,	......{......BEGIN_COM_MAP(Cxxx)	......	COM_INTERFACE_ENTRY_IID(IID_IDeskBand, IDeskBand)	......END_COM_MAP()// IOleWindowSTDMETHODIMP Cxxx::GetWindow(HWND * phwnd){	// 取得 band 对象的窗口句柄	// m_hWnd 是建立窗口时候保存的	*phwnd = m_hWnd;		return S_OK;}STDMETHODIMP Cxxx::ContextSensitiveHelp(BOOL fEnterMode){	// 上下文帮助，参考 IContextMenu 接口	return E_NOTIMPL;}// IDockingWindowSTDMETHODIMP CVerticalBar::ShowDW(BOOL bShow){	// 显示或隐藏 band 窗口	if( m_hWnd )		::ShowWindow( m_hWnd, bShow ? SW_SHOW : SW_HIDE);	return S_OK;}STDMETHODIMP CVerticalBar::CloseDW(DWORD dwReserved){	// 销毁 band 窗口	if( ::IsWindow( m_hWnd ) )		::DestroyWindow( m_hWnd );	m_hWnd = NULL;    return S_OK;}STDMETHODIMP CVerticalBar::ResizeBorderDW(LPCRECT prcBorder, IUnknown* punkToolbarSite, BOOL fReserved){	// 当框架窗口的边框大小改变时	return E_NOTIMPL;}// IDeskBandSTDMETHODIMP CVerticalBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode,  DESKBANDINFO* pdbi){	         // 取得 band 的基本信息，你需要填写 pdbi 参数作为返回	if( NULL == pdbi )		return E_INVALIDARG;	// 如果将来需要调用 IOleCommandTarget::Exec() 则需要保存这2个参数	m_dwBandID = dwBandID;	m_dwViewMode = dwViewMode;	if(pdbi-&gt;dwMask &amp; DBIM_MINSIZE)	{	// 最小尺寸		pdbi-&gt;ptMinSize.x = 10;		pdbi-&gt;ptMinSize.y = 10;	}	if(pdbi-&gt;dwMask &amp; DBIM_MAXSIZE)	{	// 最大尺寸 (-1 表示 4G)		pdbi-&gt;ptMaxSize.x = -1;		pdbi-&gt;ptMaxSize.y = -1;	}	if(pdbi-&gt;dwMask &amp; DBIM_INTEGRAL)	{		pdbi-&gt;ptIntegral.x = 1;		pdbi-&gt;ptIntegral.y = 1;	}	if(pdbi-&gt;dwMask &amp; DBIM_ACTUAL)	{		pdbi-&gt;ptActual.x = 0;		pdbi-&gt;ptActual.y = 0;	}	if(pdbi-&gt;dwMask &amp; DBIM_TITLE)	{	// 窗口标题		wcscpy(pdbi-&gt;wszTitle,L"窗口标题");	}	if(pdbi-&gt;dwMask &amp; DBIM_MODEFLAGS)	{		pdbi-&gt;dwModeFlags = DBIMF_VARIABLEHEIGHT;	}	if(pdbi-&gt;dwMask &amp; DBIM_BKCOLOR)	{	// 如果使用默认的背景色，则移除该标志		pdbi-&gt;dwMask &amp;= ~DBIM_BKCOLOR;	}	return S_OK;}		</pre>
<em><a name=3.3　选择实现的_COM_接口>3.3　选择实现的 COM 接口</a></em><br>　　有两个接口不是必须实现的，但也许很有用：IInputObject 和 IContextMenu。如果 band 对象需要接收用户的输入，那么必须实现 IInputObject 接口。IE 实现了 IInputObjectSite 接口，当容器中有多个输入窗口时，它调用 IInputObject 接口方法去负责管理用户的输入焦点。<br>在浏览器栏中需要实现3个函数：UIActivateIO()、HasFocusIO()、TranslateAcceleratorIO()。<br>当浏览器栏激活或失去活性的时候，IE 调用 UIActivateIO 函数，当激活的时候，浏览器栏一般调用 SetFocus 去设置它自己窗口的焦点。当 IE 需要判断哪个窗口有焦点的时候，它调用 HasFocusIO 。当浏览器栏的窗口或其子窗口有输入焦点时，则应返回 S_OK，否则返回 S_FALSE。TranslateAcceleratorIO 允许对象处理加速键，例子程序中没有实现，所以直接返回 S_FALSE。
<pre>STDMETHODIMP CExplorerBar::UIActivateIO(BOOL fActivate, LPMSG pMsg){    if(fActivate)        SetFocus(m_hWnd);    return S_OK;}STDMETHODIMP CExplorerBar::HasFocusIO(void){    if(m_bFocus)        return S_OK;    return S_FALSE;}STDMETHODIMP CExplorerBar::TranslateAcceleratorIO(LPMSG pMsg){    return S_FALSE;}      </pre>
　　Band 对象能够通过包容器的 IOleCommandTarget::Exec() 调用执行命令。而 IOleCommandTarget 接口指针，则可以通过调用包容器的 IInputOjbectSite::QueryInterface（IID_IOleCommandTarget,...） 函数得到。CGID_DeskBand 是命令组，当一个 band 对象的 GetBandInfo 被调用的时候，包容器通过 dwBandID 参数指定一个 ID 给 band 对象，对象要保存住这个ID，以便调用 IOleCommandTarget::Exec()的时候使用。ID 的命令有：
<ul>
    <li>DBID_BANDINFOCHANGED<br>Band 的信息变化。设置参数 pvaIn 为 band ID， 该 ID 就是最近一次调用 GetBandInfo 所得到的值，容器会调用 band 对象的 GetBandInfo 函数来更新请求信息。
    <li>DBID_MAXIMIZEBAND <br>最大化 band。设置参数 pvaIn 为 band ID，该 ID 就是最近一次调用 ?GetBandInfo ?所得到的值。
    <li>DBID_SHOWONLY <br>打开或关闭容器中其它的 bands。 设置参数 pvaIn 为VT_UNKNOWN 类型，它可以是如下的值：<br>　
    <table class=clsStd width=727 border=1>
        <tbody>
            <tr>
                <th width=62><font face=宋体 size=3>值</font></th>
                <th width=650><font face=宋体 size=3>描述</font></th>
            </tr>
            <tr>
                <td align=middle width=62><font face=宋体 size=3>pUnk</font></td>
                <td width=650><font face=宋体 size=3><span lang=en-us>band </span>对象的 <strong>IUnknown</strong> 指针，其它的桌面<span lang=en-us> bands </span>将被隐藏</font></td>
            </tr>
            <tr>
                <td align=middle width=62><font face=宋体 size=3>0</font></td>
                <td width=650><font face=宋体 size=3>隐藏所有的桌面<span lang=en-us> bands</span></font></td>
            </tr>
            <tr>
                <td align=middle width=62><font face=宋体 size=3>1</font></td>
                <td width=650><font face=宋体 size=3>显示所有的桌面 <span lang=en-us>bands</span></font></td>
            </tr>
        </tbody>
    </table>
    <br>
    <li>DBID_PUSHCHEVRON<br>在菜单项左边显示&#8220;v&#8221;的选择标志。容器发送一个 RB_PUSHCHEVRON 消息，当 band 对象接收到通知消息 RBN_CHEVRONPUSHED 提示它显示一个"v"的标志。设置 IOleCommandTarget::Exec 函数中 nCmdExecOpt 参数为 band ID，该 ID 是最近一次调用 GetBandInfo ?所得到的值，设置 IOleCommandTarget::Exec 函数中 pvaIn 参数为 VT_I4 类型，这是应用程序定义的一个值，它通过通知消息 RBN_CHEVRONPUSHED 中lAppValue 回传给 band 对象。 </li>
</ul>
<p><em><a name=3.4　Band_对象注册>3.4　Band 对象注册</a></em><br>　　Band 对象必须注册为一个 OLE 进程内的服务器，并且支持 apartment 线程公寓。注册表中默认键的值是表示菜单的文字。对于浏览器栏，它加到 IE 菜单的&#8220;查看\浏览器栏&#8221;中；对于工具栏 band ，它加到 IE 菜单的&#8220;查看\工具栏&#8221;中；对于桌面 band， 它加到系统任务栏的快捷菜单中。在菜单资源中，可以使用&#8220;&amp;&#8221;指明加速键。<br><br>通常，一个基本的 band 对象的注册表项目是：<br><br><strong>HKEY_CLASSES_ROOT <br>CLSID <br><em>{你的 band 对象的 CLSID}</em></strong> <br>　　(Default) = 菜单的文字 <br>　　InProcServer32 <br>　　　(Default) = DLL 的全路径文件名 <br>　　　ThreadingModel= Apartment<br><br>工具栏 bands 还必须把它们的 CLSID 注册到 IE 的注册表中。<br><br>在 <strong>HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Toolbar</strong> 下给出 CLSID 作为键名，而其键值是被忽略的。<br><br><strong>HKEY_LOCAL_MACHINE <br>Software <br>Microsoft <br>Internet Explorer <br>Toolbar </strong><br>　　{你的 band 对象的 CLSID}<br><br>　　还有几个可选的注册表项目(例子程序并不是这样实现的)。比如，你想让浏览器栏显示 HTML 的话，必须要如下设置注册表： <br><br><strong>HKEY_CLASSES_ROOT <br>CLSID <br><em>{你的 Band 对象的 CLSID} </em><br>Instance <br>CLSID <br>　　</strong>(Default) = {4D5C8C2A-D075-11D0-B416-00C04FB90376}<br><br>同时，如果要指定一个本地的 HTML 文件，那么要如下设置： <br><br><strong>HKEY_CLASSES_ROOT <br>CLSID <br><em>{你的 Band 对象的 CLSID}</em> <br>Instance <br>InitPropertyBag <br>　　</strong>Url<br><br>　　另外，还可以指定浏览器栏的宽和高，当然，它是依赖于这个栏是纵向还是横向的。其实这个项目无所谓，因为当用户调整了浏览器栏的大小后，会自动保存在注册表中的。<br><br><strong>HKEY_CURRENT_USER <br>Software <br>Microsoft <br>Internet Explorer <br>Explorer Bars <br>{你的 Band 对象的 CLSID} <br>　　</strong>BarSize<br><br>　　BarSize 键的类型必须是 REG_BINARY 类型，它有8个字节。左起前4个字节，是用16进制表示的像素宽度或高度，后4个字节保留，你应该设置为0。下面是一个可以在浏览器栏上显示 HTML 文件的全部注册表项目的例子，默认宽度为291（0x123）个像素点： <br><br><strong>HKEY_CLASSES_ROOT <br>CLSID <br><em>{你的 Band 对象的 CLSID} </em></strong><br>　(Default) = 菜单文字 <br>　<strong>InProcServer32 </strong><br>　　(Default) = DLL 的全路径文件名 <br>　　ThreadingModel= Apartment<br><strong>Instance <br>CLSID </strong><br>　　(Default) = {4D5C8C2A-D075-11D0-B416-00C04FB90376}<br><strong>InitPropertyBag</strong> <br>　　Url= 你的 HTML 文件名<br><br><strong>HKEY_CURRENT_USER <br>Software <br>Microsoft <br>Internet Explorer <br>Explorer Bars <br>{你的 Band 对象的 CLSID} </strong><br>　　BarSize= 23 01 00 00 00 00 00 00<br><br>　　对于注册表的设置，用 ATL 实现其实是异常简单的。打开工程的 xxx.rgs 文件，并手工编辑一下就可以了。 下面这个文件源码，是例子程序中 IE 工具栏的注册表样式，HKLM 是需要手工添加的，因为它不使用组件类型方式注册。而对于其它类型的 band 对象只要在类声明中添加： </p>
<pre>BEGIN_CATEGORY_MAP(Cxxx)			// 向注册表中注册 COM 类型	IMPLEMENTED_CATEGORY(CATID_InfoBand)	// 垂直样式的浏览器栏END_CATEGORY_MAP()		</pre>
IE 工具栏类型 band 对象的&#8220;.rgs&#8221;文件
<pre>HKCR	// 这个项目是 ATL 帮你生成的，你只要手工修改&#8220;菜单上的文字&#8221;就可以了{	Bands.ToolBar.1 = s ''ToolBar Class''	{		CLSID = s ''{ 你的 CLSID }''	}	Bands.ToolBar = s ''ToolBar Class''	{		CLSID = s ''{ 你的 CLSID }''		CurVer = s ''Bands.ToolBar.1''	}	NoRemove CLSID	{		ForceRemove { 你的 CLSID } = s ''用在菜单上的文字(&amp;T)''		{			ProgID = s ''Bands.ToolBar.1''			VersionIndependentProgID = s ''Bands.ToolBar''			ForceRemove ''Programmable''			InprocServer32 = s ''%MODULE%''			{				val ThreadingModel = s ''Apartment''			}			''TypeLib'' = s ''{xxxx-xxxx-xxxxxxxxxxxxxxx}''		}	}}HKLM	// 这个项目是手工添加的IE工具栏所特有的{	Software	{		Microsoft		{			''Internet Explorer''			{				NoRemove Toolbar				{					ForceRemove val { 你的 CLSID } = s ''随便给个说明性文字串''				}			}		}	}}		</pre>
<strong><a name=四、_ATL_实现>四、 ATL 实现</a></strong><br>　　下载代码后(VC 6.0 工程)，请参照前面的说明仔细阅读，代码中也有一些关键点的注释。如果想运行，则可以用 regsvr32.exe 进行注册，然后打开 IE 浏览器或资源浏览器就可以看到效果了。如果想自己实践一下，可以按照如下的步骤构造工程：<br><br>4.1　建立一个 ATL DLL 工程<br>4.2　添加 New ATL Object...，选择 Internet Explorer Object，选这个类型的目的是让向导给我们添加 IObjectWithSite 的支持。如果你使用的是 .net 环境，则不要忘记选择支持这个接口。<br><br><img height=257 alt="" src="http://www.vckbase.com/document/journal/vckbase42/images/yfbands2.jpg" width=413 border=0><br><br>4.3　输入对象名称，比如我想建立一个垂直的浏览器栏，不妨叫它 VerBar<br><br><img height=260 alt="" src="http://www.vckbase.com/document/journal/vckbase42/images/yfbands3.jpg" width=421 border=0><br><br>4.4　线程模型必须选择 Apartment，接口类型的选择无所谓，看你想不想支持 IDispatch 接口功能了。在例子程序中的垂直浏览器栏中，由于想更简单的操纵 IE 和从 IE 中接受事件（连接点），选择 Dual 是必要的。聚合选项，你只要别选择 Only 就可以了。<br><br><img height=260 alt="" src="http://www.vckbase.com/document/journal/vckbase42/images/yfbands4.jpg" width=421 border=0><br><br>4.5　展现你无穷的智慧，开始输入程序吧。如果是 Debug 方式编译，可能会出现一个连接错误，报告找不到_AtlAxCreateControl，那么你要在菜单 Project\Settings...\Link 中增加对 Atl.lib 的连接。或者使用 #pragma comment ( lib, "atl" )加入连接库。<br>4.6　如果想调试代码，在菜单 Project\Settings...\Debug 中输入 IE 的路径名称，比如：&#8220;C:\Program Files\Internet Explorer\IEXPLORE.EXE&#8221;，然后就可以跟踪断点调试了。 编译和调试桌面工具栏的 band 对象，是非常麻烦的，因为计算机启动时自动运行 Shell，而 Shell 就会加载活动的桌面对象。<br><br><strong>五、结束语</strong><br>好了，到这里，就到这里了。祝大家学习快乐^_^&nbsp;<br>
<img src ="http://www.cppblog.com/mydriverc/aggbug/29017.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-30 16:00 <a href="http://www.cppblog.com/mydriverc/articles/29017.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer 编程简述（十一）实现完美的Inplace Drag &amp; Drop——“超级拖放”</title><link>http://www.cppblog.com/mydriverc/articles/28966.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:40:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28966.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28966.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28966.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28966.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28966.html</trackback:ping><description><![CDATA[<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><font size="3"><strong> 关键字</strong> ：超级拖放，GetDropTarget，ondragover，IHTMLDataTransfer</font></font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">1、概述</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">许多多窗口浏览器都提供了一种被称为&#8220;超级拖放&#8221;（或&#8220;超级拖拽&#8221;、&#8220;随心拖放&#8221;等等，不一而足）的功能。作为对IE拖拽行为对扩展，&#8220;超级拖放&#8221;实现了一些非常实用的功能：</font></div>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">拖放网页链接：通常是在新窗口中打开 </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">拖放选中的文字：保存文字、作为关键字通过搜索引擎搜索网络、作为Url打开等 </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">拖放图片：通常是保存图片到指定文件夹 </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">当然，还有很关键的一点：拖动对象时鼠标指针反馈不同的拖拽效果 </font></li>
</ul>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">在《</font><a target="_blank" href="http://blog.csdn.net/CathyEagle/archive/2006/03/11/621961.aspx"><font face="Courier New">Internet Explorer 编程简述（十）响应来自HTML Element的事件通知——几个好用的类</font></a><font face="Courier New">》中曾提到，尽管许多浏览器都提供了超级拖放的功能，但与IE的缺省实现相比，除了具备鼠标指针拖拽效果外，还没有哪个浏览器的实现能够实现：</font></div>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">文字在页面内与输入框之间的交互拖放（这一点最为重要） </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">来自外部的文字与网页输入框之间的交互拖放 </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">拖拽时滚动页面（这一点是被忽略了） </font></li>
</ul>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">本文的目的，一是介绍实现超级拖放的两种方法，二是说明如何实现&#8220;完美&#8221;的拖放——即扩展IE拖拽行为的同时，保留IE默认的拖拽行为。三是给出一个最为直接和简洁的实现，至于拖放不同的对象以实现不同的功能，不在本文讨论的范围，略去。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">2、标准的实现方法</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">标准方法即通过IDocHostUIHandler的GetDropTarget成员函数来实现，在MSDN这样说到：</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">IDocHostUIHandler::GetDropTarget Method——Called by MSHTML when it is used as a drop target. This method enables the host to supply an alternative IDropTarget interface.</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">即
在适当的时候，MSHTML引擎会调用IDocHostUIHandler的GetDropTarget方法，为应用程序提供一个机会来替换MSHTML
缺省的DropTarget实现。我们就可以通过这个自定义的DropTarget实现来完成上述的&#8220;超级拖放&#8221;功能。方法示例如下,其中略去的部分可参
考MFC中CHtmlControlSite和CHtmlView的源代码：</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">STDMETHODIMP CHtmlControlSite::XDocHostUIHandler::GetDropTarget(</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">METHOD_PROLOGUE_EX_(CHtmlControlSite, DocHostUIHandler)</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">*ppDropTarget = g_pDropTarget;//<font style="background-color: #808080;" color="#ffffff">将自定义的实现告知MSHTML引擎</font></font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  S_OK;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">其
中g_pDropTarget指向某个全局的IDropTarget接口的实现，我们假定为CIEDropTarget，CIEDropTarget实现
了IDropTarget的几个成员函数DragEnter、DragOver、DragLeave和Drop。在DragEnter中可以决定是否接受
一个Drop以及如果接受这个Drop的话该提供怎样的鼠标拖拽反馈，在持续触发的DragOver中同样可以设定鼠标拖拽反馈，从而实现在拖放不同的对
象（文字、链接、图像等）时提供不同的拖拽视觉效果，实现相当简单，此处不再赘述。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">但
上面的实现存在一些问题。首先是选中的文字在页面内与输入框之间交互的拖放没有了。这是自然的，既然我们用自定义的DropTarget替换掉了IE的缺
省实现，那这种交互的拖放理应由我们自己实现。难处并非在于不能实现，而是在于实现起来比较麻烦——光是得到鼠标下的HTML
Element就够我们烦了；当输入框中有文字的时候，光标还应该随着鼠标的移动而移动——所以这个费力还不一定讨好的功能似乎没有哪个浏览器去做。其
次，作为输入框文字拖放的衍生物，拖拽滚动没有了。当鼠标向某个方向拖拽时，网页应该随着将不可见的部分滚动出来，比如某个输入框，让我们有机会将文字拖
拽过去。这个Feature的实现并不困难，不过一来是被忽略了（注意到拖拽滚动的人并不多），二来主要Feature都没有实现，这个滚动也意义不大
了。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">3、打入MSHTML内部</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">既然从GetDropTarget提供外部实现难以得到与输入框的交互式拖放，那就换个角度来考虑问题，让我们打入MSHTML的内部。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">着
手点是IHTMLDocumentX接口——操纵IE的DOM的法宝。我们注意到IHTMLDocument2有个ondragstart事件，进而想到
应该也有诸如ondragenter、ondragover、ondrop之类的事件（事实上也是有的），如果响应这些事件，处理同输入框的交互式拖放应
该就能够解决。因为这些拖放在MSHTML的缺省DropTarget实现中发生，因而当鼠标拖拽到某个输入框上时，肯定会触发一个ondragover
事件，而在IHTMLEventObj的辅助下我们能轻松得到相关的HTML
Element，其它的操作就容易进行了。再细心一点，我们还发现IHTMLEventObj2接口有个dataTransfer属性——可以得到一个
IHTMLDataTransfer的指针，而IHTMLDataTransfer接口正是浏览器内部用于数据交换的重要手段之一（看看它的属性就知道会
很有用了）：</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier New">IHTMLDataTransfer Members</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">clearData——Removes one or more data formats from the clipboard through dataTransfer or clipboardData object.</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">dropEffect——Sets or retrieves the type of drag-and-drop operation and the type of cursor to display.</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">effectAllowed——Sets or retrieves, on the source element, which data transfer operations are allowed for the object.</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">getData——Retrieves the data in the specified format from the clipboard through the dataTransfer or clipboardData objects.</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">setData——Assigns data in a specified format to the dataTransfer or clipboardData object.</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">更进一步，从IHTMLDataTransfer接口还可以访问到IDataObject接口，在进行Ole拖放时，数据就是通过IDataObject接口来传递的。具体用法稍后讨论。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">4、打入MSHTML内部——思路</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">提
供鼠标反馈效果与实现GetDropTarget的方法类似，有了IHTMLDataTransfer接口，便可在ondragstart及
ondragover事件触发时通过dropEffect属性设置拖拽的效果（可根据需要自行设定，不设置的话使用默认的效果）。再者，&#8220;拖&#8221;和&#8220;放&#8221;都
在MSHTML的缺省实现中发生，我们从IHTMLEventObj的SrcElement即可得知鼠标所位置的HTML Element是否是输入框。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">5、打入MSHTML内部——实现</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">要接收到ondragstart之类的事件，可以采用《</font><a target="_blank" href="http://blog.csdn.net/CathyEagle/archive/2006/03/11/621961.aspx"><font face="Courier New">Internet Explorer 编程简述（十）响应来自HTML Element的事件通知——几个好用的类</font></a><font face="Courier New">》中提到的CHtmlObj类和CHtmlElements类，并在适当的地方连接到Document，示例代码如下所示：</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">HRESULT CHtmlDocument2::OnInvoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">DISPPARAMS * pdispparams, VARIANT * pvarResult,EXCEPINFO * pexcepinfo, </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">UINT * puArgErr)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">......</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//如果只是要设置鼠标拖拽效果的话，这个事件可以不处理</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> case</strong>  DISPID_HTMLELEMENTEVENTS_ONDRAGSTART :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">OnDragStart();</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> break</strong> ;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//重点在这里</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> case</strong>  DISPID_HTMLELEMENTEVENTS_ONDRAGOVER :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">OnDragOver();</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> break</strong> ;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> case</strong>  DISPID_HTMLELEMENTEVENTS_ONDROP :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">OnDrop();</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> break</strong> ;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">......</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> void</strong>  CHtmlDocument2::OnDragOver(<strong> void</strong> )</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">SetDragEffect();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">设置鼠标拖拽效果</font></font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> void</strong>  CHtmlDocument2::SetDragEffect(<strong> void</strong> )</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLWindow2&gt;&nbsp; </font><font face="Courier New">pWindow;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLEventObj&gt;&nbsp; </font><font face="Courier New">pEventObj;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLEventObj2&gt;&nbsp; </font><font face="Courier New">pEventObj2;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLElement&gt;&nbsp; </font><font face="Courier New">pElement;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">HRESULT hr = m_spHtmlObj-&gt;get_parentWindow( &amp;pWindow );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">hr = pWindow-&gt;get_event( &amp;pEventObj );</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//ondragover发生时IE的默认行为是&#8220;没有鼠标拖拽效果&#8221;。</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//将IHTMLEventObj的返回值设为false即可取消该事件的默认行为，所以执行完下面这句话，拖拽效果就出现了。</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">AllowDisplayDragCursor(pEventObj, FALSE);&nbsp;&nbsp; </font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComBSTR bstrTagName;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">pEventObj-&gt;get_srcElement(&amp;pElement);&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">获得当前HTML Element</font></font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">pElement-&gt;get_tagName(&amp;bstrTagName);&nbsp;&nbsp;&nbsp;&nbsp; </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( IsEditArea(bstrTagName) )&nbsp;<font style="background-color: #808080;" color="#ffffff">//根据Tag Name判断是否鼠标位于输入框，以便设置焦点使得光标随鼠标移动</font></font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLElement2&gt;&nbsp; </font><font face="Courier New">pElement2;</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( SUCCEEDED(pElement-&gt;QueryInterface(IID_IHTMLElement2, (void **) &amp;pElement2 )) </font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">&amp;&amp; pElement2 )</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">pElement2-&gt;focus();</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//默认情况下，当拖拽文档到输入框时，鼠标会变成拖拽的光标，所以这里使用IE的默认行为。</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">AllowDisplayDragCursor(pEventObj, TRUE);</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">BOOL CHtmlDocument2::IsEditArea(CComBSTR bstrTagName)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  bstrTagName == "INPUT" || bstrTagName == "TEXTAREA";</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> void</strong>  CHtmlDocument2::AllowDisplayDragCursor(CComQIPtr&lt;IHTMLEventObj&gt; pEventObj, BOOL bAllow)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">VARIANT v;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">v.vt = VT_BOOL;</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">v.boolVal = !bAllow ? VARIANT_FALSE : VARIANT_TRUE;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">pEventObj-&gt;put_returnValue(v);</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> void</strong>  CHtmlDocument2::OnDrop(<strong> void</strong> )</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLWindow2&gt;&nbsp; </font><font face="Courier New">pWindow;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLEventObj&gt;&nbsp; </font><font face="Courier New">pEventObj;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLEventObj2&gt;&nbsp; </font><font face="Courier New">pEventObj2;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLElement&gt;&nbsp; </font><font face="Courier New">pElement;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IHTMLDataTransfer&gt;&nbsp;&nbsp; pdt;&nbsp;<font style="background-color: #808080;" color="#ffffff">//此处演示如何使用IHTMLDataTransfer</font></font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">HRESULT hr = m_spHtmlObj-&gt;get_parentWindow( &amp;pWindow );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">hr = pWindow-&gt;get_event( &amp;pEventObj );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">hr = pEventObj-&gt;QueryInterface(IID_IHTMLEventObj2, (void **) &amp;pEventObj2 );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">hr = pEventObj2-&gt;get_dataTransfer(&amp;pdt);</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComBSTR bstrFormat = "URL";&nbsp;<font style="background-color: #808080;" color="#ffffff">//首先尝试获取URL</font></font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">VARIANT Data;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">hr = pdt-&gt;getData(bstrFormat, &amp;Data);</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( Data.vt != VT_NULL )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">获取成功，拖放的对象是Url</font></font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">DoOpenUrl(CString(Data.bstrVal));</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">else</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">否则尝试获取选中的文本</font></font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">bstrFormat = "Text";</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">hr = pdt-&gt;getData(bstrFormat, &amp;Data); </font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( Data.vt != VT_NULL )</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">{&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">获取成功，拖放的内容是文本</font></font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">CComBSTR bstrTagName;</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">pEventObj-&gt;get_srcElement(&amp;pElement);</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">pElement-&gt;get_tagName(&amp;bstrTagName);</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( IsEditArea(bstrTagName) )</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 2in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//Drop target是输入框，不做任何操作，由IE进行默认处理</font></div>
<div style="margin: 0in 0in 0in 2in; font-size: 10pt;"><font face="Courier New"><strong> return</strong> ;</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New"><strong> else</strong> </font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">{&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">否则我们自己处理文本，或保存，或检测是否链接后打开，等等</font></font></div>
<div style="margin: 0in 0in 0in 2in; font-size: 10pt;"><font face="Courier New">DoProcessText(CString(Data.bstrVal));</font></div>
<div style="margin: 0in 0in 0in 2.5in; font-size: 10pt;"><font face="Courier New">//Process the text</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> else</strong> </font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">{&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #808080;" color="#ffffff">//</font><font style="background-color: #808080;" color="#ffffff">既不是链接，也不是文本，可认为是来自外部（如Windows Shell）的文件拖放</font></font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New">DoOnDropFiles(pdt);</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//演示如何从IHTMLDataTransfer得到IDataObject</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><strong> void</strong>  CHtmlDocument2::DoOnDropFiles(CComQIPtr&lt;IHTMLDataTransfer&gt; pDataTransfer)</p>
</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IServiceProvider&gt;&nbsp; </font><font face="Courier New">psp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CComQIPtr&lt;IDataObject&gt;&nbsp; </font><font face="Courier New">pdo;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( FAILED(pDataTransfer-&gt;QueryInterface(IID_IServiceProvider, (void **) &amp;psp)) )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> return</strong> ;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( FAILED(psp-&gt;QueryService(IID_IDataObject, IID_IDataObject, (void **) &amp;pdo)) )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> return</strong> ;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">COleDataObject DataObject;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">DataObject.Attach(pdo);</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">......</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">6、再次回到标准方法</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">上
述通过Event
Sink响应网页拖拽的方法已经能够很好地工作，可说&#8220;趋于完美&#8221;了，但仍有两个&#8220;小&#8221;问题：第一，必须与document建立连接才能工作，而建立连接
的时机不容易掌握（MSDN中推荐的位置是DocumentComplete，但在NavigateComplete中也可，或者是检测到
WebBrowser的readystate变为READYSTATE_INTERACTIVE时进行连接）。第二，实现方法还是略显复杂。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">有没有更简单的方法呢？我决定再次对GetDropTarget进行&#8220;调研&#8221;。所谓&#8220;踏破铁鞋无觅处，得来全不费功夫&#8221;，晃了一眼GetDropTarget方法的声明后，灵机一动，我忽然想到了办法。事实证明，这是完美的解决办法。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">让
我们再来看看GetDropTarget的声明，其中第一个参数指向MSHTML提供的缺省DropTarget实现，而第二个参数用以返回应用程序的自
定义DropTarget实现，如果在GetDropTarget中返回S_OK，MSHTML将以应用程序提供的自定义DropTarget替换缺省的
DropTarget实现。</font></div>
<div style="margin: 0in; font-size: 10pt;"><blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin: 0in; font-size: 10pt;">HRESULT<font face="Courier New">&nbsp;GetDropTarget( IDropTarget&nbsp;*pDropTarget, IDropTarget&nbsp;**ppDropTarget);</font></p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt; font-family: 宋体;"><font face="Courier New">参数说明</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><strong> pDropTarget</strong>  </p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">[in]<font face="Courier New">&nbsp;Pointer to an IDropTarget interface for the current drop target object supplied by MSHTML. </font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><strong> ppDropTarget</strong>  </p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">[out]<font face="Courier New">&nbsp;Address of a pointer variable that receives an IDropTarget interface pointer for the alternative drop target object supplied by the host.</font></p>
</blockquote></div>
<div style="margin: 0in; font-size: 10pt;">
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">想到了吗？解决问题的关键就在于第一个参数pDropTarget。相信很多浏览器在处理的时候都忽略掉了第一个参数而只是将自己的实现通过第二个参数告知MSHTML，因而丢失了IE缺省的行为。既然如此，将缺省的IDropTarget接口的指针保存下来，在适当的时候调用，不就能够保留IE的原始拖放行为了吗？</font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New"></font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New"></font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New"></font></p>
<p style="margin: 0in; font-size: 10pt;"></p>
</div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">7、完美实现</font></div>
</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">完整的代码就不再给出，我们只列出关键的部分作为示例。假设我们用来实现IDropTarget接口的类叫做CBrowserDropTarget：</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//构造函数，传入参数即是从GetDropTarget得到的那个pDropTarget，它是MSHTML的缺省实现</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">CBrowserDropTarget::CBrowserDropTarget(IDropTarget *pOrginalDropTarget)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">:&nbsp; </font><font face="Courier New">m_bDragTextToInputBox(FALSE)</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//这个布尔变量用来判断是否正在向InputBox拖拽文字</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">,&nbsp; </font><font face="Courier New">m_pOrginalDropTarget(pOrginalDropTarget)</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//m_pOrginalDropTarget用来保存MSHTML的缺省实现</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">STDMETHODIMP CBrowserDropTarget::DragEnter(/* [unique][in] */IDataObject __RPC_FAR *pDataObj,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [in] */ DWORD grfKeyState,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [in] */ POINTL pt,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [out][in] */ DWORD __RPC_FAR *pdwEffect)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//调用缺省的行为</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  m_pOrginalDropTarget-&gt;DragEnter(pDataObj, grfKeyState, pt, pdwEffect);</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">STDMETHODIMP CBrowserDropTarget::DragOver(/* [in] */ DWORD grfKeyState,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [in] */ POINTL pt,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [out][in] */ DWORD __RPC_FAR *pdwEffect)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//在网页内拖拽文字时这个值是DROPEFFECT_COPY（拖拽的文字不属于输入框中）</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//或DROPEFFECT_COPY | DROPEFFECT_MOVE（拖拽的文字是输入框中的文字）</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">DWORD dwTempEffect = *pdwEffect;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//接下来调用IE的缺省行为</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">HRESULT hr = m_pOrginalDropTarget-&gt;DragOver(grfKeyState, pt, pdwEffect);</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//判断是否是往输入框拖拽文字</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">m_bDragTextToInputBox = IsDragTextToInputBox(dwOldEffect, *pdwEffect);</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( !m_bDragTextToInputBox )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//不是往输入框拖拽文字，则使用原始的拖拽效果。否则和IE的缺省效果一样——也就是没有效果</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">*pdwEffect = dwTempEffect;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  S_OK;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//根据调用缺省行为前后的Effect值判断是否是往输入框拖拽文字</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">BOOL CBrowserDropTarget::IsDragTextToInputBox(DWORD dwOldEffect, DWORD dwNewEffect)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//如果是把非输入框中文字往输入框拖动，则dwOldEffect与dwNewEffect相等，都是DROPEFFECT_COPY</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">BOOL bTextSelectionToInputBox = ( dwOldEffect == DROPEFFECT_COPY )</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">&amp;&amp; ( dwOldEffect == dwNewEffect );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//如果是把文字从一个输入框拖到另一个输入框，则dwOldEffect为DROPEFFECT_COPY | DROPEFFECT_MOVE，</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//而dwNewEffect的值可能为DROPEFFECT_MOVE（默认情况），也可能为DROPEFFECT_COPY（按下Ctrl键时）</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">BOOL bInputBoxToInputBox = ( dwOldEffect == (DROPEFFECT_COPY | DROPEFFECT_MOVE) )</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">&amp;&amp; ( dwNewEffect == DROPEFFECT_MOVE || dwNewEffect == DROPEFFECT_COPY );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//来自Microsoft Word的拖拽特殊一些，dwOldEffect是所有效果的组合值</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">BOOL bMSWordToInputBox = </font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">( dwOldEffect == (DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK) )</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">&amp;&amp; ( dwNewEffect == DROPEFFECT_MOVE || dwNewEffect == DROPEFFECT_COPY );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//来自Edit Plus的拖拽过也特殊一些，dwOldEffect是个负数（怀疑是Edit Plus的拖拽实现有问题）</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">BOOL bEditPlusToInputBox = ( dwOldEffect &lt; 0 )</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">&amp;&amp; ( dwNewEffect == DROPEFFECT_MOVE || dwNewEffect == DROPEFFECT_COPY );</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//也许还有些例外，可再添加</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">......</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  bTextSelectionToInputBox || bInputBoxToInputBox || bMSWordToInputBox || bEditPlusToInputBox;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">STDMETHODIMP CBrowserDropTarget::DragLeave()</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//调用缺省的行为</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  m_pOrginalDropTarget-&gt;DragLeave();</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">STDMETHODIMP CBrowserDropTarget::Drop(/* [unique][in] */ IDataObject __RPC_FAR *pDataObj,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [in] */ DWORD grfKeyState,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [in] */ POINTL pt,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">/* [out][in] */ DWORD __RPC_FAR *pdwEffect)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> if</strong>  ( m_bDragTextToInputBox )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//是文字拖放，调用IE的缺省行为</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">return m_pOrginalDropTarget-&gt;Drop(pDataObj, grfKeyState, pt, pdwEffect);</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//否则是拖放链接、图片、文件等，按常规的IDataObject处理方式</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">......</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong>  S_OK;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">至此，我们就得到了一个完美的&#8220;超级拖放&#8221;的基本框架，它在扩展的同时保留了IE的默认行为：</font></div>
<ol style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="1">
    <li value="1" style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">文字在页面内与输入框之间能够交互拖放。 </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;" value="2"><font face="Courier New">来自外部的文字与网页输入框之间也能交互拖放 </font></li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;" value="3"><font face="Courier New">拖拽时能够自动滚动页面 </font></li>
</ol>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">其余的功能，如向不同的方向拖拽以完成不同的工作，左键右键拖放执行不同的功能，按住Alt保存文字等等，可根据需要自行实现，不再讨论。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">8、修正</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">今天和<a target="_blank" href="http://blog.csdn.net/stanley_xu/">Stanley Xu</a>聊了几个钟头，受益匪浅。根据Stanley的提议，毋须再作是否往输入框拖拽文字的判断，因为我们需要的只是在IE的缺省行为没有鼠标拖拽效果的时候让它有拖拽效果，因此只需要简单地判断调用IE缺省行为后的Effect值是否为0即可，如下：</font></div>
<div style="margin: 0in; font-size: 10pt;">
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font style="background-color: #808080;" color="#ffffff" face="Courier New">//判断是否是往输入框拖拽文字</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">m_bDragTextToInputBox = *pdwEffect != 0;</font></div>
</div>
<div style="margin: 0in; font-size: 10pt;">简单而直接，当然更重要的是：可用。</div>
&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier new" size="3">9、参考资料</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">MSDN: <a target="_blank" href="http://search.msdn.microsoft.com/search/Redirect.aspx?title=IHTMLEventObj+Interface+%28Internet+Explorer+-+DHTML%29&amp;url=http://msdn.microsoft.com/workshop/browser/mshtml/reference/ifaces/eventobj/eventobj.asp">IHTMLEventObj Interface</a></font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">MSDN: <a target="_blank" href="http://search.msdn.microsoft.com/search/Redirect.aspx?title=IHTMLDataTransfer+Interface+%28Internet+Explorer+-+DHTML%29&amp;url=http://msdn.microsoft.com/workshop/browser/mshtml/reference/ifaces/datatransfer/datatransfer.asp">IHTMLDataTransfer Interface</a></font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">《</font><a target="_blank" href="http://blog.csdn.net/CathyEagle/archive/2006/03/11/621961.aspx"><font face="Courier New">Internet Explorer 编程简述（十）响应来自HTML Element的事件通知——几个好用的类</font></a><font face="Courier New">》</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<br><br>
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=677425</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28966.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:40 <a href="http://www.cppblog.com/mydriverc/articles/28966.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer 编程简述（十）响应来自HTML Element的事件通知——几个好用的类</title><link>http://www.cppblog.com/mydriverc/articles/28965.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:37:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28965.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28965.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28965.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28965.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28965.html</trackback:ping><description><![CDATA[<font face="Courier New">键字：HTML Element, Sink</font>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier New">1、概述</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">实现了对Webbrowser的resuing之后我们便会发现有时候我们还需要处理浏览器中的元素（HTML Element）。这种处理包括主动和被动两个方面，像《<a target="_blank" href="http://blog.csdn.net/CathyEagle/archive/2004/11/05/167884.aspx">FAQ：如何访问WebBrowser的滚动条</a>》</font><font face="Courier New">、《<a target="_blank" href="http://blog.csdn.net/CathyEagle/archive/2004/10/15/138029.aspx">FAQ：操纵下拉列表</a>》</font><font face="Courier New">、《<a target="_blank" href="http://blog.csdn.net/CathyEagle/archive/2004/09/30/121540.aspx">FAQ：两种方法访问多层嵌套的frame</a>》</font><font face="Courier New">等
文章所演示的就是主动的处理。通常我们从Webbrowser获得一个Web文档接口（IHTMLDocumentx），从它出发便可访问到浏览器所包含
的一切HTML元素。而被动的处理则是在COM技术中称为Sink的技术，我更喜欢的说法是事件通知。当文档的下载进度发生变化时，我们可以获得
ProgressChange通知，当Webbrowser下载完HTML文档时，我们可以获得DocumentComplete的通知，而当链接被点
击，或图片被拖动时，我们如何获得通知呢？本文希望能够给出部分的答案。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier New">2、HtmlObj Template</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">如何Sink一个HTML Element并不是本文的重点，其理论我不是太了解，也懒得去搞透彻，所以使用现成的库来实现。CodeProject上的一篇文章《<a target="_blank" href="http://www.codeproject.com/shell/ieHtmlObj.asp">CHtmlObj Template</a>》给出的一个模板类CHtmlObj就非常好用。下面的例子是针对Html Anchor Element的一个实例化。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">#include "HtmlObj.h"</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">class CHtmlAnchorElement : public CHtmlObj&lt;IHTMLAnchorElement, </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&amp;DIID_HTMLAnchorEvents&gt;&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">public:</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">CHtmlAnchorElement(CHtmlDocument2* pParentDoc2);</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">virtual ~CHtmlAnchorElement();</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">virtual HRESULT OnInvoke(DISPID dispidMember,REFIID riid, </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">LCID lcid, WORD wFlags,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">DISPPARAMS * pdispparams, VARIANT * pvarResult, </font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">EXCEPINFO * pexcepinfo, </font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">UINT * puArgErr);</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">};</font></div>
<div style="margin: 0in; font-size: 10pt;"><br><font face="Courier New">HRESULT CHtmlAnchorElement::OnInvoke(DISPID dispidMember, </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">REFIID riid, LCID lcid, WORD wFlags,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">DISPPARAMS * pdispparams, VARIANT * pvarResult,</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">EXCEPINFO * pexcepinfo, </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">UINT * puArgErr)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">HRESULT hr = E_NOTIMPL;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">switch(dispidMember)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">case DISPID_HTMLELEMENTEVENTS_ONMOUSEOVER :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{<font style="background-color: #808080;" color="#ffffff">//当鼠标经过链接时，我们在这里获得通知</font></font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">hr = S_OK;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // TODO: add code to handle on mouse over events</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">break;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">case DISPID_HTMLELEMENTEVENTS_ONMOUSEOUT :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{<font style="background-color: #808080;" color="#ffffff">//当鼠标从链接上移开时，我们在这里获得通知，其它的Dispatch ID可根据需要添加</font></font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">hr = S_OK;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // TODO: add code to handle on mouse out events</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">break;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">default: </font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">break;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">return hr;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">当
我们得到某个链接的HTML接口指针，便可调用CHtmlAnchorElement继承自CHtmlObj的SetSite(IUnknown
*pUnkSite)成员函数传入该接口指针。在CHtmlObj类内部用一个智能指针m_spHtmlObj来保存相应的HTML
Element接口指针，所以当上面的ONMOUSEHOVER和ONMOUSEOUT两个事件通知到达时，从m_spHtmlObj就可以访问
IHTMLAnchorElement的所有成员，如从href获得链接的Url等，此处不再赘述。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier New">3、CHtmlElements类</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">有
了CHtmlObj之后我们又会发现实践中常常会需要多个相同类型的CHtmlObj。比如包含Frame的网页中每个Frame的HTML
Document都需要一个CHtmlObj来Sink其事件。所以我们还需要有效地管理这些相同类型的CHtmlObj。下面是我写的一个简单的模板类
CHtmlElements，它通过CMap来管理多个CHtmlObj对象。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">template&lt;class THtmlElement&gt; <strong> class</strong>  CHtmlElements</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> typedef</strong>  CMap&lt;LPDISPATCH, LPDISPATCH, THtmlElement*, THtmlElement*&gt; CMapDispToHtmlElement;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CMapDispToHtmlElement m_htmlElements;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">BOOL IsSiteConnected( LPDISPATCH pDisp )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">THtmlElement *pElement;</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New"><strong> return </strong> m_htmlElements.Lookup( pDisp, pElement );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> public</strong> :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CHtmlElements(<strong> void</strong> )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">~CHtmlElements(<strong> void</strong> )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> public</strong> :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New"><strong> void</strong>  SetSite( LPDISPATCH pDisp )</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">if ( IsSiteConnected( pDisp ) ) </font><font style="background-color: #808080;" color="#ffffff" face="Courier New">//检查以避免多余的Sink</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font face="Courier New"><strong> return</strong> ;</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">THtmlElement *pElement = <strong> new</strong>  THtmlElement;&nbsp;<font style="background-color: #808080;" color="#ffffff">//通过模板类型创建相应的类的实例进行连接</font></font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">pElement-&gt;SetSite( pDisp );</font></div>
<div style="margin: 0in 0in 0in 1in; font-size: 10pt;"><font face="Courier New">m_htmlElements.SetAt( pDisp, pElement );</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">}</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font color="#ffffff"><font style="background-color: #808080;">//在合适的地方调用Clear释放所管理的内存</font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">void Clear(void)</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">POSITION pos = m_htmlElements.GetStartPosition();</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">THtmlElement *pElement = NULL;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">LPDISPATCH pDisp = NULL;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">while (pos != NULL)</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">m_htmlElements.GetNextAssoc( pos, pDisp, pElement );</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">m_htmlElements.RemoveKey( pDisp );</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">delete pElement;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
};</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">假设我们有一个象CHtmlAnchorElement那样派生自CHtmlObj的类CHtmlDocument2，使用CHtmlElements时这样声明：</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> typedef</strong>  CHtmlElements&lt;CHtmlDocument2&gt; CHtmlDocuments;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> typedef</strong>  CHtmlElements&lt;CHtmlAnchorElement&gt; CHtmlAnchors;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> class</strong>  CMyView : <strong> public</strong>  CHtmlView</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> private</strong> :</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CHtmlDocuments m_htmlDocs;</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">CHtmlAnchors m_htmlAnchors;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">在DocumentComplete时就可以这样连接到浏览器的文档对象：<br></font></div>
<blockquote dir="ltr" style="margin-right: 0px;">
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><strong> void</strong>  CMyView ::OnDocumentComplete(LPDISPATCH pDisp, LPCTSTR lpszURL)</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">{</font></div>
<div style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font face="Courier New">m_htmlDocs.SetSite(pDisp);</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">}</font></div>
</blockquote>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">如
果想一次性连接上文档中所有的Anchor
Element，可以通过IHTMLDocument2::get_anchors获得包含所有IHTMLAnchorElement接口指针的
IHTMLElementCollection，再遍历其中的每个元素，分别调用m_htmlAnchors.SetSite即可。当然，一次性的
Sink全部链接可能并不是个好注意，我更愿意在CHtmlDocument2中响应事件再通过其它手段来访问当前位置的HTML Element。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier New">4、结论</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">响
应HTML
Element的事件通知对于浏览器编程来说是一个非常强大的手段，它可以更深入细化地控制浏览器中的文档及其HTML元素，实现更为高级的功能，比如所
谓的&#8220;超级拖放&#8221;（许多多窗口浏览器都提供了该功能，但实际上没有哪个浏览器完美地实现了对URL、文字及图片的拖放）。</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-weight: bold; font-size: 10pt;"><font face="Courier New">5、参考资料</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New"><a target="_blank" href="http://www.codeproject.com/shell/ieHtmlObj.asp">Codeproject: HtmlObj Template</a></font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">&nbsp;</font></div>
<div style="margin: 0in; font-size: 10pt;"><font face="Courier New">引用地址：《<a href="http://blog.csdn.net/CathyEagle/archive/2006/03/11/621961.aspx">Internet Explorer 编程简述（十）响应来自HTML Element的事件通知——几个好用的类</a>》</font></div>
<br><br>
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=621961</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28965.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:37 <a href="http://www.cppblog.com/mydriverc/articles/28965.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer 编程简述（九）在自己的浏览器中嵌入Google工具条</title><link>http://www.cppblog.com/mydriverc/articles/28964.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:25:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28964.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28964.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28964.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28964.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28964.html</trackback:ping><description><![CDATA[<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">关键字：Google Toolbar, Explorer Bars, ToolBands, IObjectWithSite, IDeskBand</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">1<font face="Courier New">、概述</font></p>
<p style="margin: 0in; font-size: 10pt;">　　Internet Explorer<font face="Courier New">强大而方便的可编程能力和可扩展能力为其抢占浏览器市场可谓是立下了汗马功劳。可编程主要体现两方面：</font></p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">实现浏览功能的部分被包装成一个控件——WebBrowser Control，开发人员可以在自己的应用程序中嵌入它从而使程序具有访问Internet上网页的能力，同时WebBrowser Control也能够被灵活地自定义以满足不同的需要。</font> </li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">可对Microsoft Internet Explorer应用程序本身嵌入的浏览器控件编程，一般通过BHO（Browser Helper Object）来实现。</font> </li>
</ul>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: 宋体;"><font face="Courier New">　　可扩展能力则体现在几个方面：</font></p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">嵌入式面板型扩展，包括Explorer Bars（如收藏夹、搜索、历史等嵌入IE主窗口的大型面板）, Tool Bands（如Google Toolbar、MSN Toolbar等嵌入IE的工具条）, 和Desk Bands（如快速启动这类嵌入任务栏的面板，实际上是Explorer外壳的扩展）。这几种面板的编写方法相差无几，不同之处主要在于向系统注册方式的不同。</font> </li>
</ul>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;courier new&quot;;" align="center"><img alt="Explorer bars" src="http://blog.csdn.net/images/blog_csdn_net/cathyeagle/32965/o_ExplorerBar.jpg"></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in;" align="center"><font face="Courier new" size="2"><img alt="Tool bands" src="http://blog.csdn.net/images/blog_csdn_net/cathyeagle/32965/o_ToolBand.jpg"></font></p>
<p style="margin: 0in;" align="center"><font face="Courier new" size="2"><img alt="Desk band" src="http://blog.csdn.net/images/blog_csdn_net/cathyeagle/32965/o_DeskBand.jpg"></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">是参数型扩展，包括为浏览器增加上下文菜单项（调用脚本）、为浏览器主菜单增加菜单项、为浏览器&#8220;标准按钮&#8221;工具条添加按钮等。</font> </li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;"><font face="Courier New">其他扩展，如文件下载的扩展（Custom Download Manager）、地址栏扩展（搜索扩展）等。</font> </li>
</ul>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　随着IE的发展，各种类型的扩展遍地开花，其中最为引人注目的，当属地址栏扩展和工具条扩展（几乎成了兵家必争之地）。本文讨论的主题，正是工具条的扩展。</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">2<font face="Courier New">、问题的提出</font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　两个原因促成了Google Toolbar的流行，一是广告窗口的泛滥、二是Google Search。Google&#8220;简单&#8221;（实则一点都不简单，没有搜索引擎的强力支持，Toolbar的用途就大受限制）地抓住了这两点，迅速占领了市场。</font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　插件的一大好处在于可以不修改主程序，只需换一个样子差不多但功能更强的东西就可以使得整个应用程序功能增强，所以IE不升级大家也觉得Google Toobar越来越好用。于是利用WebBroser Control编写浏览器的开发人员就想，如果能像IE一样支持上述这些扩展，不就能把Google Toolbar拿过来用了吗？其他的事交给Google去做就行了。这就是我们要讨论的问题，&#8220;如何在自己的浏览器中嵌入Google Toolbar&#8221;。</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">3<font face="Courier New">、分析</font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　微软并未在MSDN中说明如何将Google Toolbar这类IE的工具条插件嵌入自己的应用程序，但其基于COM的设计方法实际上给予了我们这个能力（创建嵌入式的工具条的方法并不是本文的重点，此处略去，有兴趣的朋友可以参考MSDN）。我们知道，除了IUnknown接口外，Bands和Bars（以下简称Band对象）还需要实现三个接口：IObjectWithSite，IPersistStream和IDeskBand。当用户选择工具条或面板时，其容器（如IE的外壳框架）就会调用Band对象的IObjectWithSite::SetSite方法（该方法仅需要一个IUnknown类型的指针），将自己实现的IUnknown指针传递给Band对象。这就是整个插件开始真正激活的入口，也是我们的着手点。</font></p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;">　　MSDN<font face="Courier New">中说到，一般来说，Band对象对于SetSite方法的实现需要完成以下几件事：</font></p>
<ol>
    <li><font face="Courier New"><font size="2">如果当前Band对象持有另外的Site指针，则首先释放该指针。</font></font> </li>
    <li>
    <div><font face="Courier New"><font size="2">如果容器向SetSite方法传入的是一个空指针，则表示要删除该Band对象，此时SetSite返回S_OK即可。</font></font></div>
    </li>
    <li>
    <div><font face="Courier New"><font size="2">如果容器传入的不是空指针，则需要设置新的Site： </font></font></div>
    <ol style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="1">
        <li value="1" style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle; font-family: &quot;Courier New&quot;;"><font face="Courier New">对此IUnknown指针所指的新Site调用QueryInterface查询得到其IOleWindow接口。</font> </li>
        <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle; font-family: &quot;courier new&quot;;" value="2"><font face="Courier New">调用得到的IOleWindow接口的GetWindow方法获取父窗口的句柄（此窗口即是Band对象的栖身之处）并保存下来。如果以后不会再用到IOleWindow接口的话就对其调用Release。</font> </li>
        <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle; font-family: &quot;courier new&quot;;" value="3"><font face="Courier New">现在可以创建Band对象的窗口了，当然，要以第2步得到的窗口为父窗口来创建，并且该窗口目前只能以不可见状态存在。</font> </li>
        <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle; font-family: &quot;courier new&quot;;" value="4"><font face="Courier New">如果Band对象实现了IInputObject接口，即需要接收键盘输入，则还需要向容器传来的Site查询（QueryInterface）IInputObjectSite接口，此接口指针也需要保存下来。</font> </li>
        <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle; font-family: &quot;courier new&quot;;" value="5"><font face="Courier New">上述步骤完成后即可返回S_OK，否则应返回OLE-defined的error code告知容器什么地方出了错。</font> </li>
    </ol>
    </li>
</ol>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　显然，就我们要讨论的问题而言，只需换个角度（编写IE外壳的的角度）来考虑即可。首先，我们需要一个IUnknown接口（即Band对象所需的Site），其次需要一个IInputObjectSite接口，用以和Band对象的IInputObject接口交互，处理输入焦点转移的情况。接下来就可以通过Band对象的IDeskBand接口来显示、隐藏Band对象了（注意IDeskBand接口派生自IDockingWindow接口,后者又派生自IOleWindow接口）。</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">4<font face="Courier New">、实现</font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　实现分为两个部分，其一是一个简单的类，用以模拟IE外壳，我取名为CIESimulator。其二是一个管理IE扩展的类CIEBandPlugInManager，用以管理Band对象的方方面面。</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">class CIESimulator : public IInputObjectSite</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">private:</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IWebBrowser2 *m_pwb; <font color="#ffffff"><font style="background-color: #999999;">//<font face="Courier New">保存WebBrowser Control的接口指针</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">public:</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">CIESimulator(void){};</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">~CIESimulator(void){};</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">void SetIWebBrowser2(IWebBrowser2* pwb);</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #999999;" color="#ffffff">//IUnknown methods</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">STDMETHODIMP QueryInterface(REFIID, void **);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">STDMETHODIMP_(ULONG) AddRef(void);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">STDMETHODIMP_(ULONG) Release(void);</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #999999;" color="#ffffff">//IInputObjectSite specific methods</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">STDMETHOD(OnFocusChangeIS)(THIS_ IUnknown* punkObj, BOOL fSetFocus);</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">};</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">//IUnknown methods</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">STDMETHODIMP CIESimulator::QueryInterface( REFIID riid, void **ppv )</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">if ( riid == IID_IInputObjectSite )&nbsp; <font style="background-color: #999999;" color="#ffffff">//</font><font style="background-color: #999999;" color="#ffffff" face="Courier New">这个接口需要自己处理</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">*ppv = static_cast&lt;IInputObjectSite*&gt;(this);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">else if ( m_pwb )&nbsp; <font style="background-color: #999999;" color="#ffffff">//</font><font color="#ffffff"><font style="background-color: #999999;"><font face="Courier New">其它的交给WebBrowser Control去处理</font></font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">m_pwb-&gt;QueryInterface( riid, ppv );</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return S_OK;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">//IInputObjectSite specific methods</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">STDMETHODIMP CIESimulator::OnFocusChangeIS(IUnknown* punkObj, BOOL fSetFocus)</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">return S_OK;&nbsp; <font style="background-color: #999999;" color="#ffffff">//</font><font style="background-color: #999999;" color="#ffffff" face="Courier New">此处我们简单地返回</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">void CIESimulator::SetIWebBrowser2(IWebBrowser2* pwb)</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">m_pwb = pwb;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
</blockquote>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　注意这里我们并没有实现IOleWindow接口来向Band对象传递父窗口对象（窗口的宿主可以更改，所以Band对象创建的窗口的父窗口我们并不关心，Band对象查询IOleWindow接口的动作实际上是向WebBrowser Control查询），而是在稍后的CIEBandPlugInManager类中通过调用IDeskBand的GetWindow方法获得Band对象的窗口句柄，再手动将其嵌入我们指定的窗口中。</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　首先我们定义一个结构用以保存Band的信息：</font></p>
<blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">enum eBANDTYPE</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">btVertical = 0,</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">btHorizontal = 1</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">};</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">enum eBANDSTATE</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">bsUnInitialized = -1,</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">bsVisible = 0,</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">bsInVisible = 1,</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">bsUnLoaded = 2</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">};</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">typedef struct tagIEBANDINFO</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">char</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">szCLSID[39];</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">char</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">szName[MAX_PATH];</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IUnknown</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">*puk;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">HWND</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">hBand;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">UINT</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">uMinHeight;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">UINT</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">uBandID;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">eBANDTYPE</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">eBandType;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">eBANDSTATE</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">eBandState;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">} IEBANDINFO, *LPIEBANDINFO;</p>
</blockquote>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　再用一个函数来获取所有Band的信息（以下代码为示例，具体实现是可从注册表把所有Band的信息一一读出）</font></p>
<blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">void CIEBandPlugInManager::GetAllBandCLSID(void)</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">LPIEBANDINFO pIEBandInfo;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pIEBandInfo = new IEBANDINFO();</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">strcpy( pIEBandInfo-&gt;szCLSID, "{2318C2B1-4965-11d4-9B18-009027A5CD4F}\0");&nbsp; <font style="background-color: #999999;" color="#ffffff">//Google Toolbar的CLSID</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">strcpy( pIEBandInfo-&gt;szName, GetDisplayName(pIEBandInfo-&gt;szCLSID) );</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pIEBandInfo-&gt;uMinHeight = 22;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pIEBandInfo-&gt;uBandID = m_BandCtrlID++;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pIEBandInfo-&gt;eBandType = btHorizontal;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pIEBandInfo-&gt;eBandState = bsUnInitialized;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">m_oaBand.Add( (CObject*)pIEBandInfo );<font color="#ffffff"><font style="background-color: #999999;">//m_oaBand<font face="Courier New">是一个CObArray</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: 宋体;"><font face="Courier New">｝</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font color="#ffffff"><font style="background-color: #999999;">//根据CLSID<font face="Courier New">从注册表获取Band的名称</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">CString CIEBandPlugInManager::GetDisplayName(CString strCLSID)</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">TCHAR</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">sz[MAX_PATH];</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">HKEY</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">hKey;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">DWORD</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">dwSize;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">strCLSID = "CLSID\\" + strCLSID;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if(RegOpenKey(HKEY_CLASSES_ROOT, strCLSID, &amp;hKey) != ERROR_SUCCESS)</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return _T("");</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">RegQueryValueEx(hKey, NULL, NULL, NULL, (LPBYTE)sz, &amp;dwSize);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">RegCloseKey(hKey);</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return sz;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font color="#ffffff"><font style="background-color: #999999;">//<font face="Courier New">通过Band的CLSID激活Band</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">bool CIEBandPlugInManager::ActivateBand( CString strCLSID )</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">LPIEBANDINFO pIEBandInfo = GetBandInfo( strCLSID ); <font style="background-color: #999999;" color="#ffffff">//</font><font color="#ffffff"><font style="background-color: #999999;"><font face="Courier New">从m_oaBand中找到符合条件的Band的信息</font></font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if ( !pIEBandInfo )</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return false;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">WCHAR wsz[MAX_PATH];&nbsp; </p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">::MultiByteToWideChar(CP_ACP, 0, strCLSID, -1, wsz, MAX_PATH);</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">CLSID</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">clsid;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">HRESULT hr2 = CLSIDFromString( wsz, &amp;clsid);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if ( hr2 != NOERROR )</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return false;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">HRESULT hr = ::CoCreateInstance(clsid, NULL, LSCTX_INPROC_SERVER, IID_IUnknown, (void**)&amp;pIEBandInfo-&gt;puk); <font color="#ffffff"><font style="background-color: #999999;">//<font face="Courier New">创建Band对象的实例</font></font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IUnknown* puk = pIEBandInfo-&gt;puk;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if (FAILED(hr))</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return false;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">DoQueryBandInfo( pIEBandInfo );&nbsp; <font style="background-color: #999999;" color="#ffffff">//</font><font color="#ffffff"><font style="background-color: #999999;"><font face="Courier New">查询Band对象实例的信息</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">switch( pIEBandInfo-&gt;eBandType ) </p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">case btVertical:</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">break;</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font color="#ffffff"><font style="background-color: #999999;">//我们<font face="Courier New">不处理Vertical的面板</font></font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">case btHorizontal:</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt;">g_pMainFrame-&gt;m_wndReBar.AddBar2( pIEBandInfo-&gt;hBand, pIEBandInfo-&gt;uBandID, pIEBandInfo-&gt;uMinHeight, 0, 0); <font style="background-color: #999999;" color="#ffffff">//</font><font color="#ffffff"><font style="background-color: #999999;"><font face="Courier New">将Band嵌入主窗口的ReBar中</font></font></font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">REBARBANDINFO rbbi;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">rbbi.cbSize = sizeof(rbbi);</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">rbbi.cxMinChild = 0;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">rbbi.cyMinChild = pIEBandInfo-&gt;uMinHeight;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">rbbi.cx = rbbi.cxIdeal = 250;</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">UINT nCount = g_pMainFrame-&gt;m_wndReBar.GetReBarCtrl().GetBandCount();</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">g_pMainFrame-&gt;m_wndReBar.GetReBarCtrl().SetBandInfo(nCount-1, &amp;rbbi); </p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">break;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">default:</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">break;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">} </p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pIEBandInfo-&gt;eBandState = bsVisible;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return true;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font color="#ffffff"><font style="background-color: #999999;">//<font face="Courier New">查询Band对象实例的信息</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">void CIEBandPlugInManager::DoQueryBandInfo(LPIEBANDINFO pIEBandInfo)</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IObjectWithSite *pOWS;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;"><font color="#ffffff"><font style="background-color: #999999;">//<font face="Courier New">查询IObjectWithSite接口</font></font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">HRESULT hr = pIEBandInfo-&gt;puk-&gt;QueryInterface(IID_IObjectWithSite, (void**)&amp;pOWS);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if (SUCCEEDED(hr))</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">{&nbsp;&nbsp;&nbsp;&nbsp; //<font face="Courier New">设置Site</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt;">pOWS-&gt;SetSite( (IUnknown *)&amp;m_IESimulator ); <font style="background-color: #999999;" color="#ffffff">//</font><font color="#ffffff"><font style="background-color: #999999;">m_IESimulator<font face="Courier New">是CIESimulator的一个实例对象，对Band对象而言，它就像IE</font></font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IDeskBand *pdb;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">hr = pIEBandInfo-&gt;puk-&gt;QueryInterface(IID_IDeskBand, (void**)&amp;pdb);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if (SUCCEEDED(hr))</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">{&nbsp;&nbsp;&nbsp;&nbsp; <font style="background-color: #999999;" color="#ffffff">//</font><font color="#ffffff"><font style="background-color: #999999;"><font face="Courier New">查询得到Band对象窗口的句柄，稍候通过该句柄将Band对象的窗口嵌入我们指定的窗口</font></font></font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pdb-&gt;GetWindow(&amp;pIEBandInfo-&gt;hBand);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt;">ShowBand(pIEBandInfo, TRUE);<font color="#ffffff"><font style="background-color: #999999;">//<font face="Courier New">显示Band</font></font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">bool CIEBandPlugInManager::ShowBand(LPIEBANDINFO pIEBandInfo, bool bShow)</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IDockingWindow *pdw;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">HRESULT hr = pIEBandInfo-&gt;puk-&gt;QueryInterface(IID_IDockingWindow, (void**)&amp;pdw);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">if (SUCCEEDED(hr))</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">pdw-&gt;ShowDW(bShow);</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">else</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">{</p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return false;</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">return true;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">}</p>
</blockquote>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　下面是我测试的一个截图，Google的搜索、广告窗口拦截均可正常工作。</font></p>
<p style="margin: 0in;" align="center"><font face="Courier new" size="2"><img alt="嵌入Google toolbar演示" src="http://blog.csdn.net/images/blog_csdn_net/cathyeagle/32965/o_EmbedGoogleToolbarSnap.jpg"></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">5<font face="Courier New">、总结</font></p>
<p style="margin: 0in; font-size: 10pt;"><font face="Courier New">　　上述的原理看来很简单，但具体实现的时候仍然需要作较多的测试和考虑，Band对象的管理和缓存、接口的AddRef和Release等。时间有限，代码也很乱，不过只要原理交待清楚，相信会对有兴趣的朋友有所帮助。</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">6<font face="Courier New">、参考资料</font></p>
<p style="margin: 0in; font-size: 10pt;">MSDN:<font face="Courier New">《<font face="Courier New">Creating Custom Explorer Bars, Tool Bands, and Desk Bands</font>》</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">引用地址：《<a href="http://blog.csdn.net/CathyEagle/archive/2005/12/12/550698.aspx">Internet Explorer编程简述（九）在自己的浏览器中嵌入Google工具条</a>》</p>
<br><br>
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=550698</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28964.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:25 <a href="http://www.cppblog.com/mydriverc/articles/28964.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer 编程简述（八）实现浏览历史菜单</title><link>http://www.cppblog.com/mydriverc/articles/28963.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:22:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28963.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28963.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28963.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28963.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28963.html</trackback:ping><description><![CDATA[<p style="margin: 0in; font-size: 10pt;">关键字：ITravelLogStg, IEnumTravelLogEntry, ITravelLogEntry</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">1、概述</p>
<p style="margin: 0in; font-size: 10pt;">Internet Explorer的浏览历史菜单在4.0版本开始出现，但直到5.5之前，微软都未公布用于访问浏览历史的COM接口，如今已是IE6.0大行其道的年代，用于访问浏览历史的接口也早已公布多时，本文的目的则是试图抛砖引玉，简单介绍用于访问浏览历史的Travel Log接口，并用一个小小的类CIETravelLog来实现对Travel Log的封装。</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">2、IOmHistory接口</p>
<p style="margin: 0in; font-size: 10pt;">在早些时候的MSDN中，我们能够查阅到关于浏览历史的接口仅有IOmHistory，而该接口实际上对应的是浏览器中可以通过脚本访问的&#8220;history&#8221;对象。对于&#8220;history&#8221;对象，MSDN中是这样说的：</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">For security reasons,
the history object does not expose the actual URLs in the browser
history. It does allow navigation through the browser history by
exposing the back, forward, and go methods.
A particular document in the browser history can be identified as an
index relative to the current page. For example, specifying -1 as a
parameter for the go method is the equivalent of clicking the Back
button.</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">This object is available in script as of Microsoft Internet Explorer 3.0.</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;">即为了安全的原因，IOmHistory接口仅提供了有限的几个方法来完成在浏览器中前进、后退等操作，并没有提供访问历史列表Url的能力。这也难怪，该接口在IE 3.0时代已经存在，而当时IE并不成熟，可编程能力也不甚强大。一直到IE 4.0通过与Windows 98捆绑销售一统天下之后，相关的文档才逐渐丰富，多窗口浏览器等基于Internet Explorer/WebBrowser Control的应用软件也才铺天盖地开来。但在IE 5.5接口公布之前，要完全模拟IE的Travel Log行为，并不是一件容易的事。最容易想到的方法就是在BeforeNavigate、DocumentComplete等事件发生之时记录/修改Url并加以保存（我在早些时候也这样做过），但是效果不甚理想，尤其是浏览包含Frame的网页时，处理更是麻烦。当然，要完全模拟亦非难事，只不过开发人员都知道微软公布接口是早晚的事，所以也没有人花大力气在模拟IE的Travel Log行为上。</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">3、Travel Log简介</p>
<p style="margin: 0in; font-size: 10pt;">Internet Explorer 5.5推出以后，Travel Log接口也就开始出现在MSDN中，它是专门为OLE嵌入WebBrowser Control的应用程序设计的，其目的是&#8220;提高和加强用户的访问日志体验&#8221;（improve and enhance the user's travel log experience）。事实上，稍后我会提到，Travel Log接口正日益成为应用程序中的重要接口之一。</p>
<p style="margin: 0in; font-size: 10pt;">微软公布的Travel Log共包含三个接口：ITravelLogStg, IEnumTravelLogEntry和ITravelLogEntry。</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">ITravelLogStg——该接口提供了用于在Travel Log中添加、删除、枚举日志（浏览历史）的方法，本文需要用到的几个方法列举如下： </li>
</ul>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: 宋体;">方法名&nbsp;&nbsp;&nbsp;&nbsp; 用途</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">EnumEntries 为访问日志项创建枚举器（IEnumTravelLogEntry接口指针）</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">GetRelativeEntry 返回一个日志项 </p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">TravelTo 访问一个日志项</p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">IEnumTravelLogEntry——该接口提供用于枚举日志项所必需的方法，本文只用到一个方法： </li>
</ul>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: 宋体;">方法名&nbsp;&nbsp;&nbsp;&nbsp; 用途</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">Next&nbsp;&nbsp; 枚举下一个日志项（返回ITravelLogEntry接口指针）</p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">ITravelLogEntry——该接口只有两个方法，分别用于返回日志项的Title和Url： </li>
</ul>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: 宋体;">方法名&nbsp;&nbsp;&nbsp;&nbsp; 用途</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">GetTitle 返回日志项的Title</p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">GetURL 返回日志项的Url</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: 宋体;">接口准备好了，我们也就很容易得知它们之间的关系：</p>
<ul style="margin-top: 0in; margin-bottom: 0in; margin-left: 0.5in; direction: ltr; unicode-bidi: embed;" type="disc">
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">要得到相对于当前页面的日志项列表，首先应通过ITravelLogStg接口创建一个枚举器（IEnumTravelLogEntry接口）。 </li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">通过IEnumTravelLogEntry枚举器的Next方法枚举出一个个的日志项（ITravelLogEntry接口）。 </li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">由ITravelLogEntry接口获取日志项所代表的网页的Title和Url并加以处理。 </li>
    <li style="margin-top: 0px; font-size: 10pt; margin-bottom: 0px; vertical-align: middle;">访问相对于当前页面的某个日志项时，首先由ITravelLogStg的GetRelativeEntry方法根据与当前页的距离得到ITravelLogEntry接口，再将后者传入ITravelLogStg的TravelTo方法以达到访问日志项的目的（如前进和后退）。 </li>
</ul>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;">也许不是太恰当，我对UML也不熟悉，借用一个伪UML序列图表示其关系如下：</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;<img alt="Travel log" src="http://blog.csdn.net/images/blog_csdn_net/cathyeagle/32965/o_TravelLog.jpg"></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">4、封装Travel Log</p>
<p style="margin: 0in; font-size: 10pt;">接下来，我们就用一个简单的类来完成对Travel Log的封装。如下所示，tlogstg.h包含了Travel Log的相关接口声明，该头文件可以在Platform SDK中找到。</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">#include "tlogstg.h"</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">class CIETravelLog</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">private:</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">ITravelLogStg *m_pTravelLogStg;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">IEnumTravelLogEntry *m_pEnumLogEntry;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">ITravelLogEntry *m_pTravalLogEntry;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">IWebBrowser2* m_pWebBrowser;</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">public:</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">CIETravelLog(void);</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">~CIETravelLog(void);</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void SetWebBrowser(IWebBrowser2* pWebBrowser);</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void BuildHistoryMenu(CMenu * pMenu, unsigned char nCount, bool bForward);</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void TravelTo(int nPosition);</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">};</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">CIETravelLog::CIETravelLog(void)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">: m_pTravelLogStg(NULL), m_pEnumLogEntry(NULL), m_pTravalLogEntry(NULL), m_pWebBrowser(NULL)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">CIETravelLog::~CIETravelLog(void)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pTravelLogStg != NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pTravelLogStg-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pEnumLogEntry != NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pEnumLogEntry-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pTravalLogEntry != NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pTravalLogEntry-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pWebBrowser != NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pWebBrowser-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-style: italic;"><font style="background-color: #808080;"><font color="#ffffff">//将浏览器的IWebBrowser2接口指针赋予CIETravelLog的实例</font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void CIETravelLog::SetWebBrowser(IWebBrowser2* pWebBrowser)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;"><font color="#000000">if ( (m_pWebBrowser == pWebBrowser) || (m_pWebBrowser == NULL) )</font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">return;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pWebBrowser != NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pWebBrowser-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pWebBrowser = pWebBrowser;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">IServiceProvider *pSP;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">HRESULT hr = m_pWebBrowser-&gt;QueryInterface(IID_IServiceProvider, (LPVOID*)&amp;pSP);</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pWebBrowser-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if (SUCCEEDED(hr))</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">hr = pSP-&gt;QueryService(SID_STravelLogCursor, IID_ITravelLogStg, (LPVOID*)&amp;m_pTravelLogStg);</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">pSP-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-style: italic;"><font style="background-color: #808080;"><font color="#ffffff">//创建浏览历史菜单，bForward指明是前进还是后退菜单</font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void CIETravelLog::BuildHistoryMenu(CMenu * pMenu, unsigned char nCount, bool bForward)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pTravelLogStg == NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">return;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">TLENUMF eFlag = bForward ? TLEF_RELATIVE_FORE : TLEF_RELATIVE_BACK;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( FAILED(m_pTravelLogStg-&gt;EnumEntries( eFlag, &amp;m_pEnumLogEntry ) ) )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">return;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">ULONG uFetched;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">int i=0;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pEnumLogEntry !=NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">while ( SUCCEEDED( m_pEnumLogEntry-&gt;Next( 1, &amp;m_pTravalLogEntry, &amp;uFetched ) ) &amp;&amp; </font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt;"><font style="background-color: #cccccc;"><font color="#000000">m_pTravalLogEntry &amp;&amp; i&lt;10 )</font><font color="#ffffff"><font style="background-color: #808080;">//我们最多只需要10条历史菜单，可根据实际情况修改</font></font></font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">LPOLESTR pszTitle;</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pTravalLogEntry-&gt;GetTitle( &amp;pszTitle );</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">CString strTitle = pszTitle;</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( bForward )</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 2in; font-size: 10pt; font-style: italic;"><font style="background-color: #808080;"><font color="#ffffff">//ID_IEHISTORY_MIDDLE是预定义的某个菜单项ID，从该ID开始前后可以创建10个菜单项，参见下一节</font></font></p>
<p style="margin: 0in 0in 0in 2in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">pMenu-&gt;InsertMenu( 0, MF_STRING, ID_IEHISTORY_MIDDLE + ++i, strTitle );</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">else</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 2in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">pMenu-&gt;InsertMenu( 0, MF_STRING, ID_IEHISTORY_MIDDLE - ++i, strTitle );</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">CoTaskMemFree( pszTitle );</font></p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pTravalLogEntry-&gt;Release();</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-style: italic;"><font style="background-color: #808080;"><font color="#ffffff">//根据与当前页面的相对距离来访问历史网页</font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void CIETravelLog::TravelTo(int nPosition)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if ( m_pTravelLogStg == NULL )</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">return;</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">if SUCCEEDED(m_pTravelLogStg-&gt;GetRelativeEntry( nPosition, &amp;m_pTravalLogEntry ))</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 1in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">m_pTravelLogStg-&gt;TravelTo( m_pTravalLogEntry );</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
</blockquote>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-weight: bold; font-size: 10pt;">5、使用CIETravelLog</p>
<p style="margin: 0in; font-size: 10pt;">假设是在我们自己编写的多窗口浏览器中使用Travel Log。为简单起见，我们声明一个CIETravelLog的全局对象g_IETravelLog，以便在任何地方调用。然后在适当的地方，如CMainFrame的TBN_DROPDOWN消息（工具条菜单下拉消息）处理函数OnDropDown中，添加下面的代码，用以创建浏览历史菜单：</p>
<p style="margin: 0in; font-size: 10pt; font-style: italic;">//GetActiveWebBrowserPtr返回活动的浏览器IWebBrowser2接口指针</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IETravelLog.SetWebBrowser( GetActiveWebBrowserPtr );</p>
<p style="margin: 0in; font-size: 10pt; font-style: italic;">//bForward为true则创建&#8220;前进&#8221;菜单，否则创建&#8220;后退&#8221;菜单</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">IETravelLog.BuildHistoryMenu( &amp;Menu, 10, bForward);</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;">以下定义为菜单项ID的范围，前后共可以容纳10个菜单项，可根据实际情况修改。</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">#define ID_IEHISTORY_FIRST&nbsp; 60200</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">#define ID_IEHISTORY_MIDDLE 60210</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">#define ID_IEHISTORY_LAST&nbsp;&nbsp; 60220</p>
<p style="margin: 0in 0in 0in 1.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;">添加命令处理函数OnTravelHistoryUrl用以响应从ID_IEHISTORY_FIRST到ID_IEHISTORY_LAST的菜单命令。</p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<blockquote dir="ltr" style="margin-right: 0px;">
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">ON_COMMAND_RANGE(ID_IEHISTORY_FIRST, ID_IEHISTORY_LAST, OnTravelHistoryUrl)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">&nbsp;</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">void CMainFrame::OnTravelHistoryUrl(UINT nID /* Command ID */)</font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">{</font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-style: italic;"><font color="#ffffff"><font style="background-color: #808080;">//nID - ID_IEHISTORY_MIDDLE即为要访问的浏览历史到当前页面的距离</font></font></p>
<p style="margin: 0in 0in 0in 0.5in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;"><font color="#000000">g_IETravelLog.TravelTo( nID - ID_IEHISTORY_MIDDLE );</font></font></p>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;"><font style="background-color: #cccccc;" color="#000000">}</font></p>
</blockquote>
<p style="margin: 0in; font-size: 10pt; font-family: &quot;Courier New&quot;;">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;">6、再谈Travel Log</p>
<p style="margin: 0in; font-size: 10pt;">前面我提到&#8220;Travel Log接口正日益成为应用程序中的重要接口之一&#8221;，此处加以说明。从微软平台的开发模式及导向来看，基于Internet Explorer/WebBrowser Control的应用势必会成为主流。在下一代的操作系统Longhorn中，应用程序界面的描述将完全由XML的一个特化——XAML来完成，而XAML的解析将由浏览器完成。微软说未来应用程序的部署将会十分容易，本地应用和基于浏览器的应用之间的差异将会被逐渐淡化，而实现这一目标的一个重要表现就是，在将来的操作系统平台上，应用程序实际上时刻都将运行在Internet Explorer中，Internet Explorer在某种程度上来说变成了一个容器。</p>
<p style="margin: 0in; font-size: 10pt;">于是，扎根于Internet Explorer的Travel Log自然而然地就被整合到了我们的应用程序中。君不见，我们每天在资源管理器和浏览器上完成的工作，不就是在Travel Log中来来回回地跑吗？如果所有的应用程序都嵌入到Internet Explorer中运行，那么我们在应用程序中所作的操作便自然得到了记录，&#8220;前进&#8221;和&#8220;后退&#8221;也就很Easy了。</p>
<p style="margin: 0in; font-size: 10pt;">很多软件都已经或多或少地开始采用基于Internet Explorer的模式，如Microsoft Money、Microsoft Encarta、Visual Studio.net的安装程序等等，都是很好的范例。所以，就目前来说，将我们的应用程序按这种模式编写（可参考《<a href="http://blog.csdn.net/cathyeagle/archive/2004/08/09/69607.aspx">利用浏览器实现程序界面与实现的分离</a>》），不是可以早一点获得&#8220;访问日志的体验&#8221;吗？</p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;">何乐而不为。</p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;"></p>
<font size="3">
<p style="margin: 0in; font-size: 10pt; font-family: 'Courier New';">&nbsp;</p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;"></p>
<p style="margin: 0in; font-size: 10pt;"></p>
</font><font size="3">引用地址</font><font size="2">：《<a href="http://blog.csdn.net/cathyeagle/archive/2005/03/03/308620.aspx">Internet Explorer 编程简述（八）实现浏览历史菜单</a>》</font><br><br>
<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=308620</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:22 <a href="http://www.cppblog.com/mydriverc/articles/28963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(七)</title><link>http://www.cppblog.com/mydriverc/articles/28962.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:18:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28962.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28962.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28962.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28962.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28962.html</trackback:ping><description><![CDATA[<div id="BodyLabel" class="content" style="padding: 0px 10px; display: block;">
<strong> 1、概述</strong>
<p>Internet Explorer有实在太多没有公布的东西。上一篇文章《Internet Explorer
编程简述（六）自定义浏览器上下文菜单》提到的获取&#8220;编码&#8221;菜单的方法就是利用了浏览器的上层窗口&#8220;Shell DocObject
View&#8221;的未公布的命令ID。本文将要介绍的是如何用这个ID把&#8220;编码&#8221;菜单放到我们自己的菜单中来（如工具条上的&#8220;编码&#8221;按钮的下拉菜单）。</p>
<p>#define SHDVID_GETMIMECSETMENU 27<br>......<br>CComPtr<iolecommandtarget> spCT;</iolecommandtarget></p>
<p>hr = pcmdTarget-&gt;QueryInterface(IID_IOleCommandTarget, (void**)&amp;spCT);<br>......<br>// Get the language submenu<br>hr = spCT-&gt;Exec(&amp;CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &amp;var);<br></p>
<p><strong> 2、原理</strong> </p>
<p>上面指向IOleCommandTarget接口的智能指针spCT是从IDocHostUIHandler::ShowContextMenu的参数pcmdTarget得到的，它其实也可以从HTML文档接口得到，这就是实现的关键。</p>
<p><strong> 3、实现</strong> </p>
<p style="margin: 0in; font-size: 10pt; font-family: SimSun;">下面的代码演示了如何将&#8220;编码&#8221;菜单放置到我们自己的编码菜单上去。</p>
<p>void CMainFrame::OnDropDown( NMHDR* pNotifyStruct, LRESULT* pResult )</p>
<p>{</p>
<p>　const UINT CmdID_GetMimeSubMenu = 27;</p>
<p>　<font style="background-color: #808080;" color="#ffffff">// Command ID for getting the Encoding submenu</font></p>
<p>　NMTOOLBAR* pNMToolBar = ( NMTOOLBAR* )pNotifyStruct;</p>
<p>　CMenu menu;</p>
<p>　CMenu* pPopup = 0;</p>
<p>　CMyHtmlView *pView = NULL;</p>
<p>　m_bIsEncodMenuPopup = false;<font style="background-color: #808080;"><font color="#ffffff">//标志变量，用以在WM_INITMENUPOPUP消息处理函数中检查&#8220;编码&#8221;菜单</font></font></p>
<p><font face="宋体">　</font>switch ( pNMToolBar-&gt;iItem )</p>
<p>　{</p>
<p>　......<br>　case ID_VIEW_ENCODE:<font style="background-color: #808080;"><font color="#ffffff">//按下&#8220;编码&#8221;按钮</font></font></p>
<p>　{</p>
<p>　　m_bIsEncodMenuPopup = true;</p>
<p>　　VERIFY( menu.LoadMenu( IDR_ENCODE ) <font style="background-color: #808080;" color="#ffffff">);</font><font style="background-color: #808080;"><font color="#ffffff">//IDR_ENCODE是预置的&#8220;编码&#8221;菜单资源，内含任意一项占位用的菜单</font></font></p>
<p>　　CMyHtmlView = GetActiveMyHtmlView();<font style="background-color: #808080;"><font color="#ffffff">//检查当前是否存在活动的浏览器视图窗口</font></font></p>
<p>　　if ( pView != NULL )</p>
<p>　　{</p>
<p>　　　LPDISPATCH lpDispatch =pView-&gt;GetHtmlDocument();<font style="background-color: #808080;"><font color="#ffffff">//获得文档指针</font></font></p>
<p>　　　if ( lpDispatch != NULL )</p>
<p>　　　{</p>
<p>　　　　<font style="background-color: #808080;" color="#ffffff">// Get an IDispatch pointer for the IOleCommandTarget interface.</font></p>
<p>　　　　IOleCommandTarget * pCmdTarget = NULL;</p>
<p>　　　　HRESULT hr = lpDispatch-&gt;QueryInterface(IID_IOleCommandTarget, (void**)&amp;pCmdTarget);</p>
<p>　　　　if ( SUCCEEDED( hr ) )</p>
<p>　　　　{</p>
<p>　　　　　VARIANT varEncSubMenu;</p>
<p>　　　　　::VariantInit( &amp;varEncSubMenu );</p>
<p>　　　　　hr = pCmdTarget-&gt;Exec( &amp;::CGID_ShellDocView,
CmdID_GetMimeSubMenu, OLECMDEXECOPT_DODEFAULT, NULL, &amp;varEncSubMenu
);</p>
<p>　　　　　if ( SUCCEEDED( hr ) )</p>
<p>　　　　　{</p>
<p>　　　　　　<font style="background-color: #808080;"><font color="#ffffff">// 添加&#8220;编码&#8221;菜单</font></font></p>
<p>　　　　　　MENUITEMINFO miiEncoding;</p>
<p>　　　　　　::memset( &amp;miiEncoding, 0, sizeof(MENUITEMINFO) );</p>
<p>　　　　　　miiEncoding.cbSize = sizeof(MENUITEMINFO);</p>
<p>　　　　　　miiEncoding.fMask = MIIM_SUBMENU;</p>
<p>　　　　　　miiEncoding.hSubMenu = reinterpret_cast&lt; HMENU &gt; (varEncSubMenu.byref);</p>
<p>　　　　　　menu.SetMenuItemInfo(0, &amp;miiEncoding, TRUE);<font style="background-color: #808080;"><font color="#ffffff">//丢掉设计时占位用的菜单，替换为&#8220;编码&#8221;菜单</font></font></p>
<p><font face="宋体">　　　　　 </font>}</p>
<p>　　　　}</p>
<p>　　　}</p>
<p>　　}</p>
<p>　　pPopup = menu.GetSubMenu( 0 );</p>
<p>　　break;</p>
<p>　}</p>
<p>　......</p>
<p>　}</p>
<p>　</p>
<p>　if ( pPopup != 0 )</p>
<p>　{</p>
<p>　　CRect rc;</p>
<p>　　::SendMessage( pNMToolBar-&gt;hdr.hwndFrom, TB_GETRECT, pNMToolBar-&gt;iItem, ( LPARAM )&amp;rc );</p>
<p>　　rc.top = rc.bottom;</p>
<p>　　::ClientToScreen( pNMToolBar-&gt;hdr.hwndFrom, &amp;rc.TopLeft() );</p>
<p>　　long lResult = pPopup-&gt;TrackPopupMenu( TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, rc.left, rc.top, this );</p>
<p>　　m_bIsEncodMenuPopup = false;</p>
<p>　　if ( pNMToolBar-&gt;iItem == ID_VIEW_ENCODE )</p>
<p>　　{</p>
<p>　　　<font style="background-color: #808080;"><font color="#ffffff">//其余的事教给浏览器去做，参考《<a id="CategoryEntryList.ascx_EntryStoryList_Entries__ctl1_TitleUrl" href="http://blog.csdn.net/cathyeagle/archive/2004/09/16/106013.aspx"><font color="#000080">Internet Explorer 编程简述（五）调用IE隐藏的命令（中文版）</font></a>》</font></font></p>
<p><font face="宋体">　　　 </font>CFindIEWnd FindIEWnd( pView-&gt;m_wndBrowser.m_hWnd, "Internet Explorer_Server");</p>
<p>　　　::SendMessage( FindIEWnd.m_hWnd, WM_COMMAND, MAKEWPARAM(LOWORD(lResult), 0x0), 0 );</p>
<p>　　}</p>
<p>　　else</p>
<p>　　{</p>
<p>　　　SendMessage( WM_COMMAND, MAKEWPARAM(LOWORD(lResult), 0x0), 0 );</p>
<p>　　}</p>
<p>　}</p>
<p>　*pResult = TBDDRET_DEFAULT;</p>
<p>}</p>
<p><br></p>
<p>void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)</p>
<p>{</p>
<p>　CMDIFrameWndEx::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);</p>
<p>　if ( m_bIsEncodMenuPopup )</p>
<p>　{</p>
<p>　　//默认情况下&#8220;编码&#8221;的所有菜单项都是Disabled的，在此修改其状态为Enabled</p>
<p>　　for ( UINT i=0; i
<ppopupmenu->GetMenuItemCount(); i++ )</ppopupmenu->
</p>
<p>　　{</p>
<p>　　　pPopupMenu-&gt;EnableMenuItem( pPopupMenu-&gt;GetMenuItemID( i ), MF_ENABLED | MF_BYCOMMAND );</p>
<p>　　}</p>
<p>　}</p>
<p>}</p>
<p>这样一来，原本只在浏览器上下文菜单中出现的&#8220;编码&#8221;菜单就出现在了我们自己的工具条按钮下拉菜单上，无需更多的处理，菜单状态的改变，编码的设置等，一切都教给浏览器自己去完成了。</p>
</div>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28962.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:18 <a href="http://www.cppblog.com/mydriverc/articles/28962.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(六)</title><link>http://www.cppblog.com/mydriverc/articles/28961.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:17:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28961.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28961.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28961.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28961.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28961.html</trackback:ping><description><![CDATA[<div id="BodyLabel" class="content" style="padding: 0px 10px; display: block;">
<p><strong> 1、概述<br></strong> Internet Explorer提供了非常开发的接口，使开发人员不仅可以把其浏览器核心嵌入应用程序，还可以通过各种接口以实现更深层的控制。本文就将介绍对浏览器进行高级控制的话题之一——自定义上下文菜单。</p>
<p><strong> 2、最简单的情况</strong> <br>自定义的IE及WebBrowser的上下文菜单，最简单的方式就是在注册表的HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt下添加自定义的键值，步骤如下：<br><strong> 1)</strong> 添加一个新的键，其名称即为将来显示在上下文菜单中的菜单项名称，如：<br>　HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\&amp;Google Search<br><strong> 2)</strong> 将新增的键的默认值设置为一个包含脚本的网页的URL（或文件路径全名），该网页中的脚本将在用户点击上下文菜单中的&#8220;Google Search&#8221;后被浏览器执行。</p>
<p><img alt="" src="http://www.webjx.com/img/aspnet09257-1.jpg" align="bottom" border="0" hspace="0"></p>
<p>3)在新增的键下还可以新建一个二进制值Contexts，用以指定我们新增的菜单项针对特定的网页对象是否出现，其取值可以是如下值的组合（逻辑或）</p>
<p>Context　　　　 Value<br>Default　　　　 0x1<br>Images　　　　　0x2<br>Controls　　　　0x4<br>Tables　　　　　0x8<br>Text selection　0x10<br>Anchor　　　　　0x20 </p>
<p><strong> 4)</strong> 还可以建立一个DWORD类型的Flags项并将其值设置为0x01，这将使得前述脚本在一个模态窗口中执行，就好像是通过window.showModalDialog调用的，但不同的是在脚本中仍然可以访问window对象。<br><strong> 5)</strong> 实例脚本如下：</p>
<p>
<script language="JavaScript" defer="defer"><br>&nbsp; open(external.menuArguments.location.href);<br></script>
</p>
<p>通过修改注册表自定义菜单的方法适用于Internet Explorer和WebBrowser，也具有良好的扩展性。但我们如果希望执行的是不仅仅是脚本，二是自己的程序中代码，这种方法就不适用了。</p>
<p><strong> 3、使用完全自定义的菜单<br>1)</strong> IDocHostUIhandler接口提供了一个ShowContextMenu方法，在需要显示上下文菜单之前，MSHTML引擎就会调用实现了IDocHostUIHandler接口的<br>宿主程序的ShowContextMenu方法。</p>
<p>HRESULTIDocHostUIHandler::ShowContextMenu(<br>　　DWORD dwID,<br>　　POINT *ppt,<br>　　IUnknown *pcmdtReserved,<br>　　IDispatch *pdispReserved<br>);</p>
<p>dwID参数的意义与Contexts的组合类似；ppt为菜单的弹出点屏幕坐标；pcmdtReserved接口指向
IOleCommandTarget接口，可用于检测网页对象的状态和执行命令等操作。pdispReserved在IE5以上版本中指向的是网页对象的
IDispatch接口，用以区分不同对象，比如我们可以这样来获得网页对象的指针：</p>
<p>IHTMLElement *pElem;<br>HRESULT hr;<br>hr = pdispReserved-&gt;QueryInterface(IID_IHTMLElement, (void**)pElem);<br>if(SUCCEEDED (hr)) {<br>　　BSTR bstr;<br>　　pElem-&gt;get_tagName(bstr);<br>　　USES_CONVERSION;<br>　　ATLTRACE("TagName:%s\n", OLE2T(bstr));<br>　　SysFreeString(bstr);<br>　　pElem-&gt;Release();<br>}</p>
<p>如果我们在该方法中返回S_OK，则告诉MSHTML我们将使用自己的菜单（界面），如果是S_FALSE，则弹出默认的菜单。</p>
<p><strong> 2)</strong> 实现<br>原理清楚之后，实现起来非常简单，和弹出一般的菜单没什么两样，举例如下，显示主框架的&#8220;文件菜单&#8221;：</p>
<p>HRESULT CMyHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt, IUnknown * pcmdtReserved, IDispatch *)<br>{<br>　// 载入主菜单<br>　HMENU hMenuParent = ::LoadMenu( ::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME) );<br>　if (hMenuParent)<br>　{<br>　　HMENU hMenu = ::GetSubMenu( hMenuParent, 0 ); // 取得&#8220;文件&#8221;子菜单<br>　　if (hMenu)<br>　　{<br>　　 // 显示菜单<br>　　　TrackPopupMenuEx( hMenu, <br>　　　　TPM_LEFTALIGN | TPM_TOPALIGN,<br>　　　　ppt-&gt;x,<br>　　　　ppt-&gt;y,<br>　　　　::AfxGetMainWnd()-&gt;m_hWnd,<br>　　　　NULL );<br>　　}<br>　　::DestroyMenu( hMenuParent );<br>　}<br>　return S_OK;<br>}</p>
<p><strong> 4、自定义标准上下文菜单<br>1)</strong> 原理<br>更多的时候我们希望能在浏览器原来菜单的基础上作一些修改，如删掉&#8220;查看源文件&#8221;，添加自己的菜单项，等等，而不是完全不要原始的菜单，怎么办呢？借助MSDN提供的例子，我们来看看：</p>
<p>HRESULT CBrowserHost::ShowContextMenu(DWORD dwID, POINT *ppt,IUnknown *pcmdTarget,IDispatch *pdispObject) <br>{<br>　#define IDR_BROWSE_CONTEXT_MENU 24641<br>　#define IDR_FORM_CONTEXT_MENU 24640<br>　#define SHDVID_GETMIMECSETMENU 27<br>　#define SHDVID_ADDMENUEXTENSIONS 53<br><br>　HRESULT hr;<br>　HINSTANCE hinstSHDOCLC;<br>　HWND hwnd;<br>　HMENU hMenu;<br><br>　CComPtr<iolecommandtarget> spCT;<br>　CComPtr<iolewindow> spWnd;<br><br>　MENUITEMINFO mii = {0};<br>　CComVariant var, var1, var2;<br>　hr = pcmdTarget-&gt;QueryInterface(IID_IOleCommandTarget, (void**)&amp;spCT);<br>　hr = pcmdTarget-&gt;QueryInterface(IID_IOleWindow, (void**)&amp;spWnd);<br>　hr = spWnd-&gt;GetWindow(&amp;hwnd);//取得浏览器窗口句柄<br>　hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));<br>　if (hinstSHDOCLC == NULL)<br>　{<br>　　// Error loading module -- fail as securely as possible<br>　　return;<br>　}<br>　hMenu = LoadMenu(hinstSHDOCLC, MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));<br>　hMenu = GetSubMenu(hMenu, dwID);<br>　<font style="background-color: #808080;"><font color="#ffffff">// Get the language submenu<br>　hr = spCT-&gt;Exec(&amp;CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &amp;var);<br>　mii.cbSize = sizeof(mii);<br>　mii.fMask = MIIM_SUBMENU;<br>　mii.hSubMenu = (HMENU) var.byref;<br>　// Add language submenu to Encoding context item<br>　SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &amp;mii);<br>　// Insert Shortcut Menu Extensions from registry<br>　V_VT(&amp;var1) = VT_INT_PTR;<br>　V_BYREF(&amp;var1) = hMenu;<br>　V_VT(&amp;var2) = VT_I4;<br>　V_I4(&amp;var2) = dwID;<br>　hr = spCT-&gt;Exec(&amp;CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &amp;var1, &amp;var2);<br>　// Remove View Source<br>　DeleteMenu(hMenu, IDM_VIEWSOURCE, MF_BYCOMMAND);//删除&#8220;查看源文件&#8221;菜单项<br>　// Show shortcut menu<br>　int iSelection = ::TrackPopupMenu(hMenu,<br>　　TPM_LEFTALIGN | TPM_RIGHTBUTTON | <strong> <font style="background-color: #000000;" color="#ffffff">TPM_RETURNCMD</font></strong> <font style="background-color: #000000;" color="#ffffff">,//返回用户选择的菜单命令ID<br>　　ppt-&gt;x,<br>　　ppt-&gt;y,<br>　　0,<br>　　hwnd,<br>　　(RECT*)NULL);<br>　// Send selected shortcut menu item command to shell<br>　LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);<font color="#ffffff"><font style="background-color: #808080;">//发送到Internet Explorer_Server进行内部处理。<br>　FreeLibrary(hinstSHDOCLC);<br>　return S_OK;<br>}</font></font></font></font></font></iolewindow></iolecommandtarget></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">从
上面的例子我们看出，基本的方法就是根据&#8220;shdoclc.dll&#8221;文件中的菜单资源建立菜单，再通过来自pcmdTarget的
IOlcCommandTarget接口获得&#8220;编码&#8221;菜单以及HKEY_CURRENT_USER\Software\Microsoft\
Internet
Explorer\MenuExt下的定义扩展菜单，然后以TPM_RETURNCMD标志调用TrackPopupMenu或
TrackPopupMenuEx弹出菜单，并将返回的菜单命令ID教给浏览器窗口进行处理。这种方法可以调用许多通过浏览器无法直接调用的命令和对话框
（参阅：《<a href="http://blog.csdn.net/cathyeagle/archive/2004/09/16/106013.aspx"><font color="#000000">Internet Explorer 编程简述（五）调用IE隐藏的命令</font></a>》）。</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">所以，我们只需要在弹出菜单之前做一些自定义操作即可达到修改默认菜单的目的，如上面代码中就用删除了&#8220;查看源文件&#8221;菜单项。</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;"><strong> 2)</strong> 问题<br>如果我们不仅仅是删除默认的菜单项或是修改了默认的菜单项，还添加了自己的菜单项，会出现什么情况呢？由于使用了类似于MFC中UpdateUI的机制，遇到不认识的CommandID，浏览器就会将其状态设置为Disabled，所以我们自己添加的菜单是无法被选择的。<br>可
以想到，如果把菜单状态设置为Enabled，并通过TPM_RETURNCMD标志调用TrackPopupMenu或
TrackPopupMenuEx，再把返回的命令ID教给合适的窗口（如主框架窗口）去处理不就行了。关键点就在于如何把菜单状态设置为
Enabled。</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;"><strong> 3)</strong> 实现<br>解决的办法是截获<strong> WM_INITMENUPOPUP</strong> 消息，在菜单创建以后，尚未显示之前修改菜单项状态，那浏览器就没有办法了。截获WM_INITMENUPOPUP消息则可使用子类化（subclass）的技术，前面通过IOleWindow接口我们得到了浏览器窗口的句柄hwnd，则可以这样做：</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">HMENU g_hPubMenu = NULL;<br>WNDPROC g_lpPrevWndProc = NULL;</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">LRESULT CALLBACK CustomMenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)<br>{<br>　if (uMsg == WM_INITMENUPOPUP)<br>　{<br>　　if (wParam == (WPARAM) g_hPubMenu)<br>　　{<br>　　　::EnableMenuItem( 自定义的菜单命令ID, MF_ENABLED | MF_BYCOMMAND );<br>　　　::CheckMenuItem( 自定义的菜单命令ID, MF_BYCOMMAND);<br>　　　return 0;<br>　　}<br>　}<br>　return CallWindowProc(g_lpPrevWndProc, hwnd, uMsg, wParam, lParam);<br>}</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">HRESULT CMyHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt,<br>LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved)<br>{<br>//浏览器菜单句柄保存在g_hPubMenu中<br>......<br>// subclass浏览器窗口<br>g_lpPrevWndProc = (WNDPROC)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)CustomMenuWndProc);<br>//m_SubclassWnd.SubclassWindow( hwnd );//MFC中用此方法更简便</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">// Show shortcut menu<br>int iSelection = ::TrackPopupMenu(hSubMenu,<br>　TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,<br>　ppt-&gt;x,<br>　ppt-&gt;y,<br>　0,<br>　hwnd,<br>　(RECT*)NULL);<br>// Unsubclass浏览器窗口<br>::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)g_lpPrevWndProc);<br>g_lpPrevWndProc = NULL;<br>//m_SubclassWnd.UnsubclassWindow();</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">if (iSelection == 自定义的菜单命令ID )<br>{<br>　::SendMessage( ::AfxGetMainWnd()-&gt;m_hWnd, WM_COMMAND, MAKEWPARAM(LOWORD(lResult), 0x0), 0 );<br>}<br>else<br>{<br>　LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);<br>}<br>......<br>}</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">在MFC
中则更为方便，从CWnd继承一个窗口类，假设为CWebBrowserSubclassWnd，为CMyHtmlView添加一个
CWebBrowserSubclassWnd类型的成员变量m_SubclassWnd，在子类化和去除子类化时调用
m_SubclassWnd.SubclassWindow( hwnd
)和m_SubclassWnd.UnsubclassWindow()即可。相应的WM_INITMENUPOPUP消息处理函数如下所示：</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">void CWebBrowserSubclassWnd::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) <br>{<br>　CWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">　pPopupMenu-&gt;EnableMenuItem( 自定义的菜单命令ID, MF_ENABLED | MF_BYCOMMAND );<br>　pPopupMenu-&gt;CheckMenuItem( 自定义的菜单命令ID, MF_BYCOMMAND);<br>}</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">下面的图片显示了将&#8220;文字大小&#8221;菜单项添加到&#8220;编码&#8221;菜单项的下面的效果。</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;"><img alt="" src="http://www.webjx.com/img/aspnet09257-2.jpg" align="bottom" border="0" hspace="0"></font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;"><strong> 5、新的问题<br></strong> 看完上面的代码，我们又自然地想到浏览器编程中的另一个问题，那就是&#8220;编码&#8221;菜单。我们指定，手动建立一个&#8220;编码&#8221;菜单是比较麻烦的事，而且很难做到与浏览器上下文菜单上的&#8220;编码&#8221;菜单一样的效果。何不使用上述的方法让浏览器自己建立&#8220;编码&#8221;菜单和处理相应的命令呢？</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;">具体实现请看下一篇文章《Internet Explorer 编程简述（七）完美的&#8220;编码&#8221;菜单》</font></font></font></font></font></p>
<p><font style="background-color: #808080;"><font color="#ffffff"><font style="background-color: #000000;" color="#ffffff"><font color="#ffffff"><font style="background-color: #808080;"><br><strong> 参考资料</strong> ：<br>MSDN:<a href="http://msdn.microsoft.com/library/default.aspurl=/workshop/browser/ext/tutorials/context.asp"><font color="#000000">Adding Entries to the Standard Context Menu</font></a><br>MSDN:<a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;177241"><font color="#000000">How To Adding to the Standard Context Menus of the WebBrowser Control</font></a> <br>MSDN:<a href="http://msdn.microsoft.com/library/default.aspurl=/workshop/browser/hosting/reference/ifaces/idochostuihandler/showcontextmenu.asp"><font color="#000000">IDocHostUIHandler::ShowContextMenu Method</font></a> <br>BeginThread.com:<a href="http://www.beginthread.com/Article/Ehsan/Custom%20WebBrowser%20Context%20Menus/"><font color="#000000">Custom WebBrowser Context Menus</font></a></font></font></font></font></font></p>
</div>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28961.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:17 <a href="http://www.cppblog.com/mydriverc/articles/28961.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(五)</title><link>http://www.cppblog.com/mydriverc/articles/28959.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:14:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28959.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28959.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28959.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28959.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28959.html</trackback:ping><description><![CDATA[1、概述<br><br>　　除了&#8220;整理收藏夹&#8221;和&#8220;添加到收藏夹&#8221;对话框外，还有其它一些对话框是我们希望直接通过WebBrowser调用的，
比如&#8220;导入/导出&#8221;对话框，用一般的方法很难调用。IShellUIHelper尽管提供了ImportExportFavorites方法，但结果只是
显示一个选择文件的对话框，且只能导入/导出收藏夹而不能对Cookies操作。<br>
<p align="center"><img src="http://www.webjx.com/img/aspnet09256-1.gif"></p>
<p>　　2、契机<br><br>　　MSDN中有一篇叫&#8220;WebBrowser Customization&#8221;的文章，其中介绍了通过IDocHostUIHandler.ShowContextMenu方法自定义WebBrowser上下文菜单的方法。<br><br>其
原理是从&#8220;shdoclc.dll&#8221;的资源中创建菜单，作一些修改之后用TrackPopupMenu函数（注意在标志中包含
TPM_RETURNCMD）将菜单弹出，然后把返回的Command ID发送给&#8220;Internet Explorer_Server&#8221;窗口进行处理。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　......<br>　　// 显示菜单<br>　　int iSelection = ::TrackPopupMenu(hMenu,<br>　　TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,<br>　　ppt-&gt;x,<br>　　ppt-&gt;y,<br>　　0,<br>　　hwnd,<br>　　(RECT*)NULL);<br>　　// 发送Command ID到外壳窗口<br>　　LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);<br>　　......</td>
        </tr>
    </tbody>
</table>
<br>
<p>　　好，如果找到所有上下文菜单的Command ID，不就可以随时调用了？确实是这样的。<br><br>　　3、实现<br>　　用eXeScope之类应用程序资源探索器打开&#8220;shdoclc.dll&#8221;便可以在菜单资源下找到上下文菜单的设计，如下图：<br></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09256-2.gif"></p>
<p>　　我们要做的，就是将这些ID发送到&#8220;Internet
Explorer_Server&#8221;窗口进行处理。问题是WebBrowser其实是一个OLE容器，我们使用的CHtmlView又是更外层的封装，他们
的m_hWnd成员变量并不是IE窗口的句柄，如何找到我们需要的句柄呢？请看下面的图：</p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09256-3.gif"></p>
<p><br>　　根据图中显示的从属关系，顺藤摸瓜，最内层的窗口&#8220;Internet Explorer_Server&#8221;的句柄就是我们需要的东西。为了简化问题，我这里使用了来自MSDN Magazine资深专栏撰稿人Paul Dilascia的CFindWnd类，非常好用。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　////////////////////////////////////////////////////////////////<br>　　// MSDN Magazine -- August 2003<br>　　// If this code works, it was written by Paul DiLascia.<br>　　// If not, I don't know who wrote it.<br>　　// Compiles with Visual Studio .NET on Windows XP. Tab size=3.<br>　　//<br>　　// ---<br>　　// This class encapsulates the process of finding a window with a given class name<br>　　// as a descendant of a given window. To use it, instantiate like so:<br>　　//<br>　　// CFindWnd fw(hwndParent,classname);<br>　　//<br>　　// fw.m_hWnd will be the HWND of the desired window, if found.<br>　　//<br>　　class CFindWnd {<br>　　private:<br>　　//////////////////<br>　　// This private function is used with EnumChildWindows to find the child<br>　　// with a given class name. Returns FALSE if found (to stop enumerating).<br>　　//<br>　　static BOOL CALLBACK FindChildClassHwnd(HWND hwndParent, LPARAM lParam) {<br>　　CFindWnd *pfw = (CFindWnd*)lParam;<br>　　HWND hwnd = FindWindowEx(hwndParent, NULL, pfw-&gt;m_classname, NULL);<br>　　if (hwnd) {<br>　　pfw-&gt;m_hWnd = hwnd; // found: save it<br>　　return FALSE; // stop enumerating<br>　　}<br>　　EnumChildWindows(hwndParent, FindChildClassHwnd, lParam); // recurse<br>　　return TRUE; // keep looking<br>　　}<br>　　public:<br>　　LPCSTR m_classname; // class name to look for<br>　　HWND m_hWnd; // HWND if found<br>　　// ctor does the work--just instantiate and go<br>　　CFindWnd(HWND hwndParent, LPCSTR classname)<br>　　: m_hWnd(NULL), m_classname(classname)<br>　　{<br>　　FindChildClassHwnd(hwndParent, (LPARAM)this);<br>　　}<br>　　};</td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>　　再写一个函数InvokeIEServerCommand，调用就很方便了，《Internet Explorer 编程简述（四）&#8220;添加到收藏夹&#8221;对话框》中最后给出的方法就是从这里来的。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　void CMyHtmlView::InvokeIEServerCommand(int nID)<br>　　{<br>　　CFindWnd FindIEWnd( m_wndBrowser.m_hWnd, "Internet Explorer_Server");<br>　　::SendMessage( FindIEWnd.m_hWnd, WM_COMMAND, MAKEWPARAM(LOWORD(nID), 0x0), 0 );<br>　　}</td>
        </tr>
    </tbody>
</table>
void CMyHtmlView::OnFavAddtofav()<br>　　{<br>　　InvokeIEServerCommand(ID_IE_CONTEXTMENU_ADDFAV);//调用&#8220;添加到收藏夹&#8221;对话框<br>　　}
<p><br><br>　　4、Command IDs<br>　　对所有的Command ID逐一尝试后我们发现：<br>　　1)不是所有的Command ID都可以用上面的方法调用；<br>　　2)不是所有的Command ID都是由&#8220;Internet Explorer_Server&#8221;窗口处理；<br>　　3)有一些Command ID是由上一级窗口&#8220;Shell DocObject View&#8221;处理。<br>　　所以我们还需要写一个函数。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　void CMyHtmlView::InvokeShellDocObjCommand(int nID)<br>　　{<br>　　CFindWnd FindIEWnd( m_wndBrowser.m_hWnd, "Shell DocObject View");<br>　　::SendMessage( FindIEWnd.m_hWnd, WM_COMMAND, MAKEWPARAM(LOWORD(nID), 0x0), 0 );<br>　　}</td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>　　调用文章开头提到的&#8220;导入/导出&#8221;对话框可以这样来做：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　void CDemoView::OnImportExport()<br>　　{<br>　　InvokeShellDocObjCommand(ID_IE_FILE_IMPORTEXPORT);//调用&#8220;导入/导出&#8221;对话框<br>　　}</p>
            <p>　　由"Internet Explorer_Server"窗口处理的Command ID:<br>　　#define ID_IE_CONTEXTMENU_ADDFAV 2261<br>　　#define ID_IE_CONTEXTMENU_VIEWSOURCE 2139<br>　　#define ID_IE_CONTEXTMENU_REFRESH 6042</p>
            <p>　　由"Shell DocObject View"窗口处理的Command ID:<br>　　#define ID_IE_FILE_SAVEAS 258<br>　　#define ID_IE_FILE_PAGESETUP 259<br>　　#define ID_IE_FILE_PRINT 260<br>　　#define ID_IE_FILE_NEWWINDOW 275<br>　　#define ID_IE_FILE_PRINTPREVIEW 277<br>　　#define ID_IE_FILE_NEWMAIL 279<br>　　#define ID_IE_FILE_SENDDESKTOPSHORTCUT 284<br>　　#define ID_IE_HELP_ABOUTIE 336<br>　　#define ID_IE_HELP_HELPINDEX 337<br>　　#define ID_IE_HELP_WEBTUTORIAL 338<br>　　#define ID_IE_HELP_FREESTUFF 341<br>　　#define ID_IE_HELP_PRODUCTUPDATE 342<br>　　#define ID_IE_HELP_FAQ 343<br>　　#define ID_IE_HELP_ONLINESUPPORT 344<br>　　#define ID_IE_HELP_FEEDBACK 345<br>　　#define ID_IE_HELP_BESTPAGE 346<br>　　#define ID_IE_HELP_SEARCHWEB 347<br>　　#define ID_IE_HELP_MSHOME 348<br>　　#define ID_IE_HELP_VISITINTERNET 349<br>　　#define ID_IE_HELP_STARTPAGE 350<br>　　#define ID_IE_FILE_IMPORTEXPORT 374<br>　　#define ID_IE_FILE_ADDTRUST 376<br>　　#define ID_IE_FILE_ADDLOCAL 377<br>　　#define ID_IE_FILE_NEWPUBLISHINFO 387<br>　　#define ID_IE_FILE_NEWCORRESPONDENT 390<br>　　#define ID_IE_FILE_NEWCALL 395<br>　　#define ID_IE_HELP_NETSCAPEUSER 351<br>　　#define ID_IE_HELP_ENHANCEDSECURITY 375</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　5、Refresh<br><br>　　熟悉TEmbeddedWB的读者可能注意到了ID_IE_CONTEXTMENU_REFRESH(6042)这个ID，在TEmbeddedWB中给出了一个当网页刷新时触发的OnRefresh事件，其中的关键代码如下：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　......<br>　　if Assigned(FOnRefresh) and ((nCmdID = 6041 { F5}) or (nCmdID = 6042 { ContextMenu}) or (nCmdID = 2300)) then<br>　　begin<br>　　FCancel := False;<br>　　FOnRefresh(self, nCmdID, FCancel);<br>　　if FCancel then Result := S_OK;<br>　　end;<br>　　......</td>
        </tr>
    </tbody>
</table>
<br><br>
其中的6402就是我们这里的ID_IE_CONTEXTMENU_REFRESH，2300是内置的刷新命令，那6041呢。见下图，还是
&#8220;shdoclc.dll&#8221;，6041原来是IE&#8220;查看&#8221;菜单下&#8220;刷新&#8221;菜单的命令ID。实际开发中我们发现直接调用WebBrowser的
Refresh命令有时候会导致一些错误，可以用这里的方法替换一下。<br>
<p align="center"><img src="http://www.webjx.com/img/aspnet09256-4.gif"></p>
<p><br><br>　　6、需要注意的问题<br><br>　　1)用InvokeIEServerCommand
(ID_IE_CONTEXTMENU_ADDFAV)调用&#8220;添加到收藏夹&#8221;对话框时需要注意的是，IE接收到
ID_IE_CONTEXTMENU_ADDFAV命令时是对网页中当前被选中的链接来执行&#8220;添加到收藏夹&#8221;操作的，如果没有选中的链接，才是将当前网页
添加到收藏夹。<br><br>　　2)新建IE窗口。这是浏览器编程中的难题之一，即从当前窗口新建一个Internet
Explorer窗口，完全复制当前页的内容（包括&#8220;前进&#8221;、&#8220;后退&#8221;的状态），这可以通过InvokeShellDocObjCommand
(ID_IE_FILE_NEWWINDOW)来实现。<br><br>　　3)显示IE的版本信息。调用InvokeShellDocObjCommand(ID_IE_HELP_ABOUTIE)，如下：<br></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09256-5.gif"></p>
<p>　　4)InvokeShellDocObjCommand(ID_IE_FILE_PRINT)调出的&#8220;打印&#8221;对话框是非模态的（我们不太清楚
Microsoft的设计意图，我认为&#8220;打印&#8221;对话框应该是模态的），显示模态窗口的方法请参考我的另一篇文章《利用WH_CBT
Hook将非模态对话框显示为模态对话框》</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28959.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:14 <a href="http://www.cppblog.com/mydriverc/articles/28959.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(四)</title><link>http://www.cppblog.com/mydriverc/articles/28958.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:12:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28958.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28958.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28958.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28958.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28958.html</trackback:ping><description><![CDATA[　1、概述
<p>　　调用&#8220;添加到收藏夹&#8221;对话框（如下）与调用&#8220;整理收藏夹&#8221;对话框有不同之处，前者所做的工作比后者要来得复杂。将链接添加到收藏夹除了将链接保存之外，还可能会有脱机访问的设置，从IE 4.0到IE 5.0，处理的方式也发生了一些变化。<br></p>
<br><br clear="all"><br>
<p align="center"><img src="http://www.webjx.com/img/aspnet09255-1.gif"></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09255-2.gif"></p>
<p>　　2、IShellUIHelper接口</p>
<p>　　微软专门提供了一个接口IShellUIHelper来实现对Windows Shell API一些功能的访问，将链接添加到收藏夹也是其中之一，就是下面的AddFavorite函数。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　HRESULT IShellUIHelper::AddFavorite(BSTR URL, VARIANT *Title);</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　实例代码如下：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　void CMyHtmlView::OnAddToFavorites()<br>　　{<br>　　IShellUIHelper* pShellUIHelper;<br>　　HRESULT hr = CoCreateInstance(CLSID_ShellUIHelper, NULL, <br>　　CLSCTX_INPROC_SERVER, IID_IShellUIHelper,(LPVOID*)&amp;pShellUIHelper);</p>
            <p>　　if (SUCCEEDED(hr))<br>　　{<br>　　_variant_t vtTitle(GetTitle().AllocSysString());<br>　　CString strURL = m_webBrowser.GetLocationURL();</p>
            <p>　　pShellUIHelper-&gt;AddFavorite(strURL.AllocSysString(), &amp;vtTitle);<br>　　pShellUIHelper-&gt;Release();<br>　　}<br>　　}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　我们注意到这里的&#8220;AddFavorite&#8221;函数并没有像&#8220;DoOrganizeFavDlg&#8221;那样需要一个父窗口句柄。这也导致与在IE中打
开不同，通过IShellUIHelper接口显示出来的&#8220;添加到收藏夹&#8221;对话框是&#8220;非模态&#8221;的，有一个独立于我们应用程序的任务栏按钮，这使我们的浏览
器显得非常不专业（我是个追求完美的人，这也是我的浏览器迟迟不能发布的原因之一）。<br><br>　　于是我们很自然地想到&#8220;shdocvw.dll&#8221;中除了&#8220;DoOrganizeFavDlg&#8221;外，应该还有一个类似的函数，可以传入一个父窗口句柄用以显示模态窗口，也许就像这样：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　typedef UINT (CALLBACK* LPFNADDFAV)(HWND, LPTSTR, LPTSTR);</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　事实上，这样的函数确实存在于&#8220;shdocvw.dll&#8221;中，那就是&#8220;DoAddToFavDlg&#8221;。</p>
<p><br>　　3、DoAddToFavDlg函数</p>
<p>　　&#8220;DoAddToFavDlg&#8221;函数也是&#8220;shdocvw.dll&#8221;暴露出来的函数之一，其原型如下：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　typedef BOOL (CALLBACK* LPFNADDFAV)(HWND, TCHAR*, UINT, TCHAR*, UINT,LPITEMIDLIST);</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　第一个参数正是我们想要的父窗口句柄，第二和第四个参数分别是初始目录（一般来说就是收藏夹目录）和要添加的链接的名字（比如网页的
Title），第三和第五个参数分别是第二和第四两个缓冲区的长度，而最后一个参数则是指向与第二个参数目录相关的item identifier
list的指针(PIDL)。但最奇怪的是这里并没有像&#8220;AddFavorite&#8221;函数一样的链接URL，那链接是怎样添加的呢？答案是&#8220;手动创建&#8221;。<br><br>
第二个参数在函数调用返回后会包含用户在&#8220;添加到收藏夹&#8221;对话框中选择或创建的完整链接路径名（如&#8220;X:\XXX\mylink.url&#8221;），我们就根
据这个路径和网页的URL来创建链接，代码如下（为简化，此处省去检查"shdocvw.dll"是否已在内存中的代码，参见《Internet
Explorer 编程简述（三）&#8220;整理收藏夹&#8221;对话框》）：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　void CMyHtmlView::OnFavAddtofav()<br>　　{<br>　　typedef BOOL (CALLBACK* LPFNADDFAV)(HWND, TCHAR*, UINT, TCHAR*, UINT,LPITEMIDLIST);</p>
            <p>　　HMODULE hMod = (HMODULE)LoadLibrary("shdocvw.dll");<br>　　if (hMod)<br>　　{<br>　　LPFNADDFAV lpfnDoAddToFavDlg = (LPFNADDFAV)GetProcAddress( hMod, "DoAddToFavDlg");<br>　　if (lpfnDoAddToFavDlg)<br>　　{<br>　　TCHAR szPath[MAX_PATH];<br>　　LPITEMIDLIST pidlFavorites;</p>
            <p>　　if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_FAVORITES, TRUE) &amp;&amp;<br>　　(SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &amp;pidlFavorites))))<br>　　{<br>　　TCHAR szTitle[MAX_PATH];<br>　　strcpy(szTitle, GetLocationName());</p>
            <p>　　TCHAR szURL[MAX_PATH];<br>　　strcpy(szURL, GetLocationURL());</p>
            <p>　　BOOL bOK = lpfnDoAddToFavDlg(m_hWnd, szPath,<br>　　sizeof(szPath)/sizeof(szPath[0]), szTitle,<br>　　sizeof(szTitle)/sizeof(szTitle[0]), pidlFavorites);<br>　　CoTaskMemFree(pidlFavorites);</p>
            <p>　　if (bOK)<br>　　CreateInternetShortcut( szURL, szPath, "");&nbsp; //创建Internet快捷方式<br>　　}<br>　　}<br>　　FreeLibrary(hMod);<br>　　}<br>　　return;<br>　　}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　实现CreateInternetShortcut函数创建Internet快捷方式，可以用读写INI文件的方法，但更好的则是利用IUniformResourceLocator接口。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　HRESULT CMyHtmlView::CreateInternetShortcut(LPCSTR pszURL, LPCSTR pszURLfilename,<br>　　LPCSTR szDescription,LPCTSTR szIconFile,int nIndex)<br>　　{<br>　　HRESULT hres;</p>
            <p>　　CoInitialize(NULL); </p>
            <p>　　IUniformResourceLocator *pHook;</p>
            <p>　　hres = CoCreateInstance (CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,<br>　　IID_IUniformResourceLocator, (void **)&amp;pHook);</p>
            <p>　　if (SUCCEEDED (hres))<br>　　{<br>　　IPersistFile *ppf;<br>　　IShellLink *psl;</p>
            <p>　　// Query IShellLink for the IPersistFile interface for <br>　　hres = pHook-&gt;QueryInterface (IID_IPersistFile, (void **)&amp;ppf);<br>　　hres = pHook-&gt;QueryInterface (IID_IShellLink, (void **)&amp;psl);</p>
            <p>　　if (SUCCEEDED (hres))<br>　　{ <br>　　WORD wsz [MAX_PATH]; // buffer for Unicode string</p>
            <p>　　// Set the path to the shortcut target.<br>　　pHook-&gt;SetURL(pszURL,0);</p>
            <p>　　hres = psl-&gt;SetIconLocation(szIconFile,nIndex);</p>
            <p>　　if (SUCCEEDED (hres))<br>　　{<br>　　// Set the description of the shortcut.<br>　　hres = psl-&gt;SetDescription (szDescription);</p>
            <p>　　if (SUCCEEDED (hres))<br>　　{<br>　　// Ensure that the string consists of ANSI characters.<br>　　MultiByteToWideChar (CP_ACP, 0, pszURLfilename, -1, wsz, MAX_PATH);</p>
            <p>　　// Save the shortcut via the IPersistFile::Save member function.<br>　　hres = ppf-&gt;Save (wsz, TRUE);<br>　　}<br>　　}</p>
            <p>　　// Release the pointer to IPersistFile.<br>　　ppf-&gt;Release ();<br>　　psl-&gt;Release ();<br>　　}</p>
            <p>　　// Release the pointer to IShellLink.<br>　　pHook-&gt;Release ();</p>
            <p>　　}<br>　　return hres;<br>　　} </p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　好，上面的方法虽然麻烦一点，但总算解决了&#8220;模态窗口&#8221;的问题，使得我们的程序不至于让用户鄙视。但是问题又来了，我们发现&#8220;允许脱机使用&#8221;是Disabled的，那&#8220;自定义&#8221;也就无从谈起了，尽管90%的人都没有使用过IE提供的脱机浏览。<br></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09255-3.gif"></p>
<p>　　难道我们的希望要破灭吗？我们一方面想像调用&#8220;AddFavorite&#8221;函数一样的不必手动创建链接，一方面又要模态显示窗口，就像IE那样，还能自定义脱机浏览。</p>
<p>　　3、脚本方式</p>
<p>　　许多网页上都会有一个按钮或链接&#8220;添加本页到收藏夹&#8221;，实际上通过下面的脚本显示模态的&#8220;添加到收藏夹&#8221;对话框将网页加入到收藏夹。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　window.external.AddFavorite(location.href, document.title);</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　这里的external对象是WebBrowser内置的COM自动化对象，以实现对文档对象模型（DOM）的扩展（我们也可以通过
IDocHostUIHandler实现自己的扩展）.查阅MSDN可以得知external对象的的方法与IShellUIHelper接口提供的方法
是一样的。我们有理由相信，IShellUIHelper提供了对WebBrowser内置的external对象的访问，如果在适当的地方创建
IShellUIHelper接口的实例，也许调用&#8220;AddFavorite&#8221;函数显示出来的就是模态对话框了。问题是我们还没有找到这样的地方。</p>
<p>　　从上面的脚本，我们很自然地又想到另一个方法。如果能够让网页来执行上面的脚本，岂不是问题就解决了？说做就做，如下：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　void CMyHtmlView::OnFavAddtofav()<br>　　{<br>　　CString strUrl = GetLocationURL();<br>　　CString strTitle = GetLocationName();<br>　　CString strjs = "javascript:window.external.AddFavorite('" + strUrl + "'," + "'" + strTitle + "');";<br>　　ExecScript(strjs);<br>　　}</p>
            <p>　　void CMIEView::ExecScript(CString strjs)<br>　　{<br>　　CComQIPtr&lt;IHTMLDocument2&gt;&nbsp;&nbsp; pHTMLDoc = (IHTMLDocument2*)GetHtmlDocument();<br>　　if ( pHTMLDoc != NULL&nbsp; )<br>　　{<br>　　CComQIPtr&lt;IHTMLWindow2&gt;&nbsp;&nbsp; pHTMLWnd;<br>　　pHTMLDoc-&gt;get_parentWindow( &amp;pHTMLWnd );</p>
            <p>　　if ( pHTMLWnd != NULL&nbsp; )<br>　　{<br>　　CComBSTR bstrjs = strjs.AllocSysString();<br>　　CComBSTR bstrlan = SysAllocString(L"javascript");<br>　　VARIANT varRet;<br>　　pHTMLWnd-&gt;execScript(bstrjs, bstrlan, &amp;varRet);<br>　　}<br>　　}<br>　　}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　先从CHtmlView获得文档的父窗口window对象的指针，再调用其方法execScript来执行脚本（事实上可以执行任意的脚本）。
试验发现，这个方法非常有效，不仅窗口是模态的，而且不需要手动创建链接，更重要的是&#8220;允许脱机使用&#8221;和&#8220;自定义&#8221;按钮也可以用了。</p>
<p>　　4、问题仍旧没有解决</p>
<p>　　执行脚本的方式看起来有效，可一旦我们的程序实现了IDocHostUIHandler接口对WebBrowser进行高级控制，就会发现一旦
执行的脚本包含有对&#8220;external&#8221;对象的调用，就会出现&#8220;找不到对象&#8221;的脚本错误。原因是当MSHTML解析引擎（并非WebBrowser）检查
到宿主实现了IDocHostUIHandler接口，就会调用其GetExternal方法以获得一个用以扩展DOM的自动化接口的引用。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　HRESULT IDocHostUIHandler::GetExternal(IDispatch **ppDispatch）</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　但有时候我们并没有想要扩展DOM，同时我们还希望WebBrowser使用它自己的DOM扩展。糟糕的是GetExternal方法的文档中
说这种情况下必须把ppDispatch设置为NULL，换句话说，WebBrowser连它内置的external对象也不用了，那我们的
window.external.AddFavorite就变得无处为家了。</p>
<p>　　我曾多方尝试将WebBrowser内置的external对象找出来，虽然都没有成功，但是解决问题的方法却被我找到了。</p>
<p>　　5、完美的方案</p>
<p>　　WebBrowser内置的external对象我们虽然找不到，但它肯定存在，我们只要想办法让WebBrowser自己完成对其调用即可。
实现非常简单，找到WebBrowser中包含的&#8220;Internet
Explorer_Server&#8221;窗口的句柄，发一个消息就完成了。下面的代码中假设m_hWndIE就是&#8220;Internet
Explorer_Server&#8221;窗口的句柄。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　#define ID_IE_ID_ADDFAV 2261<br>　　::SendMessage( m_hWndIE, WM_COMMAND, MAKEWPARAM(LOWORD(ID_IE_ID_ADDFAV), 0x0), 0 );</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　试一试成果，是不是和在Internet Explorer中选择&#8220;添加到收藏夹&#8221;的效果一模一样。</p>
<p>　　至于为什么这样做，后续文章再说。</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28958.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:12 <a href="http://www.cppblog.com/mydriverc/articles/28958.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(三)</title><link>http://www.cppblog.com/mydriverc/articles/28956.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:10:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28956.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28956.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28956.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28956.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28956.html</trackback:ping><description><![CDATA[关于Internet Explorer的收藏夹，比较常见的两个问题就是调用&#8220;整理收藏夹&#8221;对话框和&#8220;添加到收藏夹&#8221;对话框。调用的方法有多种，但其中还是有些值得讨论的地方。
<p>　　关键字：添加到收藏夹，整理收藏夹，DoAddToFavDlg， DoOrganizeFavDlg</p>
<p>　　<strong> 1、整理收藏夹</strong> </p>
<p>　　调用&#8220;整理收藏夹&#8221;对话框（如下），基本上来说都用的是同一个方法，即调用&#8220;shdocvw.dll&#8221;中的&#8220;DoOrganizeFavDlg&#8221;函数，把父窗口句柄和收藏夹路径作为参数传入即可。</p>
<p><br>&nbsp;</p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09254-1.gif"></p>
<p>　　<strong> 2、代码</strong> </p>
<p>　　代码实例如下所示，值得注意的是对&#8220;shdocvw.dll&#8221;的处理，为避免重复调用，应该先检查其是否已经在内存中。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　void CMyHtmlView::OnFavOrganizefav()<br>　　{<br>　　typedef UINT (CALLBACK* LPFNORGFAV)(HWND, LPTSTR);</p>
            <p>　　bool bResult = false;</p>
            <p>　　HMODULE hMod = ::GetModuleHandle( _T("shdocvw.dll") );</p>
            <p>　　if (hMod == NULL)//如果"shdocvw.dll"尚未载入则载入之<br>　　{<br>　　hMod = ::LoadLibrary( _T("shdocvw.dll") );</p>
            <p>　　if (hMod == NULL)<br>　　{</p>
            </td>
        </tr>
    </tbody>
</table>
<br>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　MessageBox( _T("The dynamic link library ShDocVw.DLL cannot be found."),<br>　　_T("Error"), MB_OK | MB_ICONSTOP );<br>　　return;<br>　　}<br>　　LPFNORGFAV lpfnDoOrganizeFavDlg = (LPFNORGFAV)<br>　　::GetProcAddress( hMod, "DoOrganizeFavDlg" );</p>
            <p>　　if (lpfnDoOrganizeFavDlg == NULL)<br>　　{<br>　　MessageBox( _T("The entry point DoOrganizeFavDlg cannot be found\n")<br>　　_T("in the dynamic link library ShDocVw.DLL."),<br>　　_T("Error"), MB_OK | MB_ICONSTOP );<br>　　return;<br>　　}</p>
            <p>　　TCHAR szPath [ MAX_PATH ];<br>　　HRESULT hr;</p>
            <p>　　hr = ::SHGetSpecialFolderPath( m_hWnd, szPath, CSIDL_FAVORITES, TRUE );<br>　　if (FAILED(hr))<br>　　{<br>　　MessageBox( _T("The path of the Favorites folder cannot be found."),<br>　　_T("Error"), MB_OK | MB_ICONSTOP );<br>　　return;<br>　　}</p>
            <p>　　bResult = (*lpfnDoOrganizeFavDlg) ( m_hWnd, szPath ) ? true : false;</p>
            <p>　　::FreeLibrary( hMod );<br>　　}<br>　　else//如果"shdocvw.dll"已经在调用者进程的地址空间中则直接使用。<br>　　{<br>　　LPFNORGFAV lpfnDoOrganizeFavDlg = (LPFNORGFAV)<br>　　::GetProcAddress( hMod, "DoOrganizeFavDlg" );</p>
            <p>　　if (lpfnDoOrganizeFavDlg == NULL)<br>　　{<br>　　MessageBox( _T("The entry point DoOrganizeFavDlg cannot be found\n")<br>　　_T("in the dynamic link library ShDocVw.DLL."),<br>　　_T("Error"), MB_OK | MB_ICONSTOP );<br>　　return;<br>　　}</p>
            <p>　　TCHAR szPath [ MAX_PATH ];<br>　　HRESULT hr;</p>
            <p>　　hr = ::SHGetSpecialFolderPath( m_hWnd, szPath, CSIDL_FAVORITES, TRUE );<br>　　if (FAILED(hr))<br>　　{<br>　　MessageBox( _T("The path of the Favorites folder cannot be found."),<br>　　_T("Error"), MB_OK | MB_ICONSTOP );<br>　　return;<br>　　}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　bResult = (*lpfnDoOrganizeFavDlg) ( m_hWnd, szPath ) ? true : false;<br>　　}</p>
            <p>　　return;</p>
            <p><br>　　}</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　<strong> 3、讨论</strong> </p>
<p>　　实际上，从&#8220;DoOrganizeFavDlg&#8221;函数的原型声明我们可以看到，由于需要一个路径，所以&#8220;整理收藏夹&#8221;对话框其实不仅可以用来整
理收藏夹，还可以整理磁盘上的目录。而且所谓的整理也不过是提供了一个对话框使用户用起来比较方便而已，和直接在资源管理器中整理没有实质性的差别。因此
调用&#8220;整理收藏夹&#8221;对话框的方法从IE4.0开始就没有变过，除了对话框的布局有所改变。</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　typedef UINT (CALLBACK* LPFNORGFAV)(HWND, LPTSTR);</td>
        </tr>
    </tbody>
</table>
<p align="center"><img src="http://www.webjx.com/img/aspnet09254-2.gif"></p>
<p>　　IE 4.0的&#8220;整理收藏夹&#8221;对话框<br></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09254-3.gif"></p>
<p>　　IE 4.0的&#8220;整理收藏夹&#8221;对话框（原本的设计）</p>
<p>　　&#8220;添加到收藏夹&#8221;就不同了，&#8220;DoAddToFavDlg&#8221;函数不再像&#8220;DoOrganizeFavDlg&#8221;函数一样对所有IE的版本都适用。</p>
<p>　　<strong> 参考资料：</strong> </p>
<p>　　MSDN: Adding Internet Explorer Favorites to Your Application </p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28956.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:10 <a href="http://www.cppblog.com/mydriverc/articles/28956.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(二)</title><link>http://www.cppblog.com/mydriverc/articles/28955.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:09:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28955.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28955.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28955.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28955.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28955.html</trackback:ping><description><![CDATA[ternet Explorer 编程简述（二）在IE中编辑OLE嵌入文档
<p>　　除了打开Internet上的网页，Internet
Explorer还能够浏览本地文件夹及文件。如果浏览的是PDF文档或Office文档，有时候你会发现当调用Navigate("xxx.doc")
的时候，Adobe Reader/Acrobat或Office等Document
Servers会在IE中嵌入自己的一个实例以打开相应的文件，当然有时候也会在独立的Acrobat或Office窗口中打开文件。 </p>
<p>　　在Adobe Reader/Acrobat的属性设置窗口中，我们可以找到&#8220;Display PDF in
browser&#8221;的选项，如果勾上，则Navigate("xxx.pdf")将会以嵌入的方式在IE中浏览PDF文件，否则在独立的Adobe
Reader/Acrobat窗口中浏览。但在Office的&#8220;选项&#8221;对话框中我们找不到这样的设置。</p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09253-1.gif"></p>
<p>　　问题：如何在自己的浏览器中控制Office这类Ole Servers的打开方式？<br><br>　　答案：修改文件夹选项，或修改注册表。<br><br>　　方法1、如下所示，从控制面板中打开&#8220;文件夹&#8221;选项，在&#8220;文件类型&#8221;属性页上找到相应的文件后缀名，如&#8220;DOC&#8221;，点击&#8220;高级&#8221;按钮，在弹出的&#8220;编辑文件类型&#8221;对话框中有&#8220;在同一窗口中浏览&#8221;的选项，如果勾上，则以嵌入IE的方式打开文档，否则在独立窗口中打开。<br>
</p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09253-2.gif"><img src="http://www.webjx.com/img/aspnet09253-3.gif"><img src="http://www.webjx.com/img/aspnet09253-4.gif"><br></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09253-5.gif"></p>
<p><br>　　方法2、直接修改注册表。<br><br>　　在&#8220;HKEY_LOCAL_MACHINE\SOFTWARE\Classes&#8221;键值下，保存了各种文件类型的注册信息，以Office文档为例，与文档相关键值如下。<br><br>　　文档类型 键值<br><br>　　Microsoft Excel 7.0 worksheet Excel.Sheet.5<br><br>　　Microsoft Excel 97 worksheet Excel.Sheet.8<br><br>　　Microsoft Excel 2000 worksheet Excel.Sheet.8<br><br>　　Microsoft Word 7.0 document Word.Document.6<br><br>　　Microsoft Word 97 document Word.Document.8<br><br>　　Microsoft Word 2000 document Word.Document.8<br><br>　　Microsoft Project 98 project MSProject.Project.8<br><br>　　Microsoft PowerPoint 2000 document PowerPoint.Show.8<br><br>
如果我们要修改Word文档的打开方式，，则在&#8220;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\
Word.Document.8&#8221;下新建一个名为&#8220;BrowserFlags&#8221;，类型为&#8220;REG_DWORD&#8221;的子键值，如果设置其值为&#8220;8&#8221;，则在独立
的窗口中打开Word文档，否则在嵌入IE的Word窗口中打开文档。<br><br>　　注：Microsoft Excel 7.0 worksheet稍有不同，应设置BrowserFlags的值为&#8220;9&#8221;方可在独立的窗口中打开文档。<br></p>
<p align="center"><img src="http://www.webjx.com/img/aspnet09253-6.gif"></p>
参考资料：<br><br>　　MSDN：259970：In-Place Activating Document Servers in Internet Explorer<br><br>
MSDN：162059：How to configure Internet Explorer to open Office
documents in the appropriate Office program instead of in Internet
Explorer
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28955.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:09 <a href="http://www.cppblog.com/mydriverc/articles/28955.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Internet Explorer编程简述(一)</title><link>http://www.cppblog.com/mydriverc/articles/28954.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 07:08:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28954.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28954.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28954.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28954.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28954.html</trackback:ping><description><![CDATA[　一直对Microsoft Internet Explorer编程非常感兴趣，曾花了不少时间琢磨，也与众多网友讨论过问题，2000年将心得写成一篇《<a href="http://blog.csdn.net/cathyeagle/archive/2004/09/03/93984.aspx" target="_blank"><font color="#000000">TWebBrowser编程简述</font></a>》，发表在自己的个人主页&#8220;<a href="http://eagleboost.myrice.com/issues/Materials/Articles/TWebBrowser.htm" target="_blank"><font color="#000000">阿甘的家</font></a>&#8221;上，得到了不少网友的回应，也被许多网站转载。此后相当长的时间内不断回答网友的提问，收获良多。
<p>　　其间正是多窗口浏览器全面开花的日子，无奈手头事情太多，我的作品Multiple
iExplorer也一直未能问世，至今遗憾。后来常与GoSurf的作者交流学习，替他解决了不少问题，也从他那里学到许多。如今GoSurf有了比较
固定的用户群，有我一份功劳，算是一种安慰吧，他也一度在GoSurf官方主页上将我列为核心技术支持，但我后来因为工作和学习的关系很少再和他联系，实
在惭愧。</p>
<p>　　回头再看当时的文章，错误实在不少，认识也比较浅薄，有些问题更是一直没有得到解决，所以我觉得有必要在前文的基础上，花点时间将我积累的关于Internet Explorer编程的问题比较完整地写出来，希望对自己有个交代，对大家有一些帮助。</p>
<p>　　是为序。</p>
<p>　　<strong> Internet Explorer编程简述（一）WebBrowser还是WebBrowser_V1</strong> </p>
<p>　　你的机器上总是存在着&#8220;两&#8221;个WebBrowser，一个叫WebBrowser，另一个叫WebBrowser_V1，其CLASSID如下：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　CLASS_WebBrowser: TGUID = '{8856F961-340A-11D0-A96B-00C04FD705A2}';<br>　　CLASS_WebBrowser_V1: TGUID = '{EAB22AC3-30C1-11CF-A7EB-0000C05BAE0B}';</td>
        </tr>
    </tbody>
</table>
<p>　　它们分别对应的接口是IWebBrowser2和IWebBrowser。问题是我们该用哪一个呢？<br>　　按照微软的推荐，应该尽量使用
前者，因为后者是为兼容Internet Explorer 3.x而保留的（尽管它能够响应来自Internet Explorer
3.x、4.x、5.x、6.x的事件），相应的IWebBrowser和IWebBrowserApp接口也应抛弃。</p>
<p>　　由于Internet Explorer
3.x年代久远，导致WebBrowser_V1提供的事件少得可怜，但值得一提的是它提供的两个事件OnNewWindow和
OnFrameBeforeNavigate有着与OnBeforeNavigate几乎相同的参数：</p>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>　　OnBeforeNavigate(<br>　　BSTR URL, <br>　　long Flags, <br>　　BSTR TargetFrameName, <br>　　VARIANT* PostData, <br>　　BSTR Headers, <br>　　BOOL FAR* Cancel)</td>
        </tr>
    </tbody>
</table>
<p>
</p>
<table align="center" bgcolor="#ddedfb" border="1" bordercolor="#55aaff" cellpadding="0" cellspacing="0" rules="none" width="500">
    <tbody>
        <tr>
            <td width="10"><br></td>
            <td>
            <p>　　OnNewWindow(<br>　　BSTR URL, <br>　　long Flags, <br>　　BSTR TargetFrameName, <br>　　VARIANT* PostData, <br>　　BSTR Headers, <br>　　BOOL FAR* Processed)</p>
            <p>　　OnFrameBeforeNavigate(<br>　　BSTR URL, <br>　　long Flags, <br>　　BSTR TargetFrameName, <br>　　VARIANT* PostData, <br>　　BSTR Headers, <br>　　BOOL FAR* Cancel)</p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　所以使用WebBrowser_V1使得我们的浏览器在有新窗口打开时能够轻易捕捉到其URL及相关的数据，如果将Processed设置为TRUE，则可取消新窗口的弹出。同样，处理Frame也比在WebBrowser中来得容易。</p>
<p>　　但WebBrowser_V1的致命弱点是它不支持高级接口，如IDocHostUIHandler，即便我们实现了
IDocHostUIHandler接口，也不会被WebBrowser_V1调用。所以希望在自己的浏览器中实现XP的界面主题、扩展IE的DOM
（Document Object Model）等高级控制的话，就肯定不能选择WebBrowser_V1了。</p>
<p>　　处理新窗口实在是很麻烦的一件事，不知道微软为什么在新版本的OnNewWindow2事件中去掉了URL这样的参数，而且OnNewWindow2事件不能完全捕捉到所有的新窗口打开。但如果安装了Windows XP SP2的话，好处又回来了。</p>
<p>　　Windows XP SP2对Internet Explorer
6作了升级，并且提供了一个新的事件OnNewWindow3，它在OnNewWindow2事件之前发生，也包含了让我们能够加以过滤处理的新窗口的
URL等参数，再加上INewWindowManager接口，就是实现Windows XP SP2中过滤广告窗口功能的基础。</p>
<br><img src ="http://www.cppblog.com/mydriverc/aggbug/28954.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 15:08 <a href="http://www.cppblog.com/mydriverc/articles/28954.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BHO开发基础</title><link>http://www.cppblog.com/mydriverc/articles/28951.html</link><dc:creator>旅途</dc:creator><author>旅途</author><pubDate>Sun, 29 Jul 2007 06:59:00 GMT</pubDate><guid>http://www.cppblog.com/mydriverc/articles/28951.html</guid><wfw:comment>http://www.cppblog.com/mydriverc/comments/28951.html</wfw:comment><comments>http://www.cppblog.com/mydriverc/articles/28951.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mydriverc/comments/commentRss/28951.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mydriverc/services/trackbacks/28951.html</trackback:ping><description><![CDATA[在Windows操作系统上，我们最常见的浏览器有两种：<span style="font-weight: bold;">文件浏览器（exploer.exe，应用于文件系统）和Internet浏览器
（iexplore.exe，应用于互联网资源）。</span>由于这两个浏览器功能强大，而且又与Windows操作系统捆绑销售，最终也就成为了浏览器的标准。但
有时候，为了给浏览器加入一些新的特性，我们往往会重新设计一个自己的浏览器。新的浏览器模仿标准浏览器的大部分功能，同时加入新特性。这种做法最直观，
但实际上也是相对于微软的重复劳动，且工作量比较大。其实，使用BHO插件，一切都变得很简单。<br>
<br>
BHO（Browser Help
Objects），是实现了特定接口的COM组件。开发好的BHO插件在注册表特定的位置注册好后，每当微软的浏览器启动，BHO实例就会被创建。在浏览
器工作的工程中，BHO会接收到很多事件，比如浏览器浏览新的地址、前进或后退、生成新的窗口、浏览器退出等等；BHO可以在这些事件的响应中实现与浏览
器的交互。<br>
<br>
下面，我们首先来介绍一下BHO的工作原理。上面我们已经提到，BHO是COM组件，而且一定实现了IObjectWithSite接口。这些组件除了在
注册表中注册为COM
Server外，还必须将它们的CLSID在HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows
CurrentVersionExplorerBrowser Helper
Objects下注册为子键。微软在设计浏览器的时候，已经给这些组件预留了空间。每当浏览器启动时，浏览器会首先在上述注册表位置查看是否有注册的
BHO
CLSID；如果有则分别创建一个实例，并对BHO实例进行初始化，建立交互连接。（注：BHO实例只有在创建它的浏览器窗口销毁时才被释放。）下图演示
了BHO的创建过程：<br>
<img alt="说明 createbho.jpg" src="http://www.vchelp.net/ASP/ibr_upload/504.jpg"><br>
<br>
成功创建的BHO，不仅可以得到各种标准的浏览器操作事件，并做出响应；还可以定制浏览器的菜单、工具条等界面元素；更或者可以安装钩子函数，监视浏览器
的一举一动。值得注意的是，使用BHO插件，Internet浏览器要求在4.0以上版本；如果是文件浏览器，操作系统要求是Windows
95/98/2000或Window NT 4.0以上版本，并且Shell的版本在4.71以上。下面是支持BHO特性的系统一览表：<br>
<br>
Shell版本 操作系统版本 支持BHO <br>
4.00 Windows 95 and Windows NT 4.0（IE版本为 4.0） 仅IE4.0 <br>
4.71 Windows 95 and Windows NT 4.0（IE版本为 4.0） IE和文件浏览器 <br>
4.72 Windows 98 IE和文件浏览器 <br>
5.00&nbsp;&nbsp; Windows 2000 IE和文件浏览器 <br>
<br>
接下去，笔者就来介绍一下如何开发BHO插件，开发环境为VC6.0（使用ATL），安装Platform SDK中的Internet
Development SDK。首先，启动VC的ATL COM
AppWizard，生成一个项目名为BhoPlugin，其余均采用默认设置。接着，我们就来分步详细阐述。<br>
第一步，增加一个ATL Object到该项目中。VC菜单Insert-&gt;New ATL
Object&#8230;，在弹出的对话框中选择&#8220;<span style="font-weight: bold;">Internet Explorer Objec</span>t&#8221;，输入COM类名（在Short
Name后输入EyeOnIE，其它各项会自动生成）。完成后，我们可以看到CEyeOnIE类有一个基类IObjectWithSiteImpl，这个
就是实现IObjectWithSite接口的模版类。<br>
第二步，实现IObjectWithSite的接口方法。在这之前，我们要先定义几个成员变量：CComQIPtr&lt;IWebBrowser2,
&amp;IID_IWebBrowser2&gt; mWebBrowser2，（需要加入#include
"ExDisp.h"），用以保存浏览器组件的指针；DWORD
mCookie，用以保存与浏览器的连接ID。IObjectWithSite有两个接口方法：SetSite和GetSite。我们只需重载
SetSite就行了。在EyeOnIE.h中增加函数声明STDMETHOD(SetSite)(IUnknown
*pUnkSite)，在EyeOnIE.cpp实现如下：<br>
STDMETHODIMP CEyeOnIE::SetSite(IUnknown *pUnkSite)<br>
{<br>
USES_CONVERSION;<br>
<br>
if (pUnkSite)<br>
{<br>
mWebBrowser2 = pUnkSite;<br>
if (mWebBrowser2)<br>
{<br>
return RegisterEventHandler(TRUE);<br>
}<br>
}<br>
return E_FAIL;<br>
}<br>
<br>
HRESULT CEyeOnIE::RegisterEventHandler(BOOL inAdvise)<br>
{<br>
CComPtr&lt;IConnectionPoint&gt; spCP;<br>
// Receives the connection point for WebBrowser events<br>
CComQIPtr&lt;IConnectionPointContainer, &amp;IID_IConnectionPointContainer&gt; spCPC(mWebBrowser2);<br>
HRESULT hr = spCPC-&gt;FindConnectionPoint(DIID_DWebBrowserEvents2, &amp;spCP);<br>
if (FAILED(hr))<br>
return hr;<br>
<br>
if (inAdvise)<br>
{<br>
// Pass the event handlers to the container<br>
hr = spCP-&gt;Advise(reinterpret_cast&lt;IDispatch*&gt;(this), &amp;mCookie);<br>
}<br>
else<br>
{<br>
spCP-&gt;Unadvise(mCookie);<br>
}<br>
return hr; <br>
}<br>
我们可以看到，SetSite的参数实际上指向的是浏览器组件。在SetSite实现中，我们首先保存浏览器组件指针，然后将该BHO向浏览器注册为事件处理器。<br>
第三步，实现IDispatch接口方法。事件处理也就在IDispatch::Invoke中实现（各个事件的ID在ExDispID.h中定义）。
BHO可能会接收到很多事件，但我们只需要响应我们感兴趣的那一部分。首先在EyeOnIE.h中增加该函数的声明，在EyeOnIE.cpp的实现中，
笔者试着响应浏览器浏览一个地址之前发出的事件<a name="baidusnap1"></a>
STDMETHODIMP CEyeOnIE::Invoke(DISPID dispidMember,REFIID riid, LCID lcid, <br>
WORD wFlags, DISPPARAMS * pDispParams, <br>
VARIANT * pvarResult,EXCEPINFO * pexcepinfo, <br>
UINT * puArgErr)<br>
{ <br>
USES_CONVERSION;<br>
<br>
if (!pDispParams)<br>
return E_INVALIDARG;<br>
<br>
switch (dispidMember)<br>
{<br>
//<br>
// The parameters for this DISPID are as follows:<br>
// [0]: Cancel flag - VT_BYREF|VT_BOOL<br>
// [1]: HTTP headers - VT_BYREF|VT_VARIANT<br>
// [2]: Address of HTTP POST data - VT_BYREF|VT_VARIANT <br>
// [3]: Target frame name - VT_BYREF|VT_VARIANT <br>
// [4]: Option flags - VT_BYREF|VT_VARIANT<br>
// [5]: URL to navigate to - VT_BYREF|VT_VARIANT<br>
// [6]: An object that evaluates to the top-level or frame<br>
// WebBrowser object corresponding to the event. <br>
//<br>
<a name="baidusnap0"></a><strong style="color: black; background-color: #ffff66;">case  <strong style="color: black; background-color: #a0ffff;">DISPID_BEFORENAVIGATE2 :<br>
{<br>
LPOLESTR lpURL = NULL;<br>
mWebBrowser2-&gt;get_LocationURL(&amp;lpURL);<br>
char * strurl;<br>
if (pDispParams-&gt;cArgs &gt;= 5 &amp;&amp; pDispParams-&gt;rgvarg[5].vt == (VT_BYREF|VT_VARIANT))<br>
{<br>
CComVariant varURL(*pDispParams-&gt;rgvarg[5].pvarVal);<br>
varURL.ChangeType(VT_BSTR);<br>
strurl = OLE2A(varURL.bstrVal);<br>
}<br>
if (strstr(strurl, "girl.com"))<br>
{<br>
*pDispParams-&gt;rgvarg[0].pboolVal = TRUE;<br>
::MessageBox(NULL, _T("该网页已被禁止!"),_T("Warning"),MB_ICONSTOP);<br>
return S_OK;<br>
}<br>
break;<br>
}<br>
<br>
<strong style="color: black; background-color: #ffff66;">case  DISPID_NAVIGATECOMPLETE2:<br>
break;<br>
<strong style="color: black; background-color: #ffff66;">case  DISPID_DOCUMENTCOMPLETE:<br>
break;<br>
<strong style="color: black; background-color: #ffff66;">case  DISPID_DOWNLOADBEGIN:<br>
break;<br>
<strong style="color: black; background-color: #ffff66;">case  DISPID_DOWNLOADCOMPLETE:<br>
break;<br>
<strong style="color: black; background-color: #ffff66;">case  DISPID_NEWWINDOW2:<br>
break;<br>
<strong style="color: black; background-color: #ffff66;">case  DISPID_QUIT:<br>
RegisterEventHandler(FALSE);<br>
break;<br>
default:<br>
break;<br>
}<br>
<br>
return S_OK;<br>
}<br>
我们看到，当用户浏览的新地址包含"girl.com"字符的时候，浏览器就会弹出一个警告对话框，并且停止进一步的动作。另外值得注意的是，在DISPID_QUIT事件（浏览器将要退出）的响应中，我们将BHO事件处理器进行了注销。<br>
第四步，因为BHO可能会被文件浏览器加载。如果我们不想这样，我们就要在DllMain中对加载者进行判断，参考如下：<br>
extern "C"<br>
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)<br>
{<br>
if (dwReason == DLL_PROCESS_ATTACH)<br>
{<br>
// Check who's loading us. <br>
// If it's Explorer then "no thanks" and exit...<br>
TCHAR pszLoader[MAX_PATH];<br>
GetModuleFileName(NULL, pszLoader, MAX_PATH);<br>
_tcslwr(pszLoader);<br>
if (_tcsstr(pszLoader, _T("explorer.exe"))) <br>
return FALSE;<br>
<br>
_Module.Init(ObjectMap, hInstance, &amp;LIBID_BHOPLUGINLib);<br>
DisableThreadLibraryCalls(hInstance);<br>
}<br>
else if (dwReason == DLL_PROCESS_DETACH)<br>
_Module.Term();<br>
return TRUE; // ok<br>
}<br>
最后，别忘了修改注册表文件，追加BHO的注册信息。在EyeOnIE.rgs文件的下面增加如下代码：<br>
HKLM<br>
{<br>
SOFTWARE<br>
{<br>
Microsoft<br>
{<br>
Windows<br>
{<br>
CurrentVersion<br>
{<br>
Explorer<br>
{<br>
'Browser Helper Objects'<br>
{<br>
{6E28339B-7A2A-47B6-AEB2-46BA53782379}<br>
}<br>
}<br>
}<br>
}<br>
}<br>
}<br>
}<br>
注意，{6E28339B-7A2A-47B6-AEB2-46BA53782379}是笔者这个BHO的CLSID，如果你自己开发BHO，这里应该正确填写你的CLSID。<br>
<br>
BHO插件可以实现的功能还有很多，比如网页内容分析、IE界面定制等等。作为总结，笔者还要提醒读者一点的是，如果不想让BHO起作用了，可以注销该插
件，如下格式：regsvr32 /u yourpathyourbho.dll，或者直接在注册表中将&#8220;Browser Helper
Objects&#8221;目录下注册的CLSID删掉。
</strong></strong></strong></strong></strong></strong></strong></strong></strong><br><img src ="http://www.cppblog.com/mydriverc/aggbug/28951.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mydriverc/" target="_blank">旅途</a> 2007-07-29 14:59 <a href="http://www.cppblog.com/mydriverc/articles/28951.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>