打造个性化的Internet Explorer

作者:孙辉

  在Microsoft的软件哲学中,框架窗口是一个十分重要的角色,这类窗口简直无处不在。所谓框架窗口,就是四个窗口边上具有停靠对象能力的窗口对象,从现象上看,框架窗口有十分特别的“边”,Microsoft构造的许许多多的东西都可以在其边上“靠泊”,这就是所谓的“Docking”,Internet Explorer是Microsoft的一个典型的运用框架窗口的代表作品。今天的IE,在不知不觉中,你会发现工具栏中会多出一些东西来,这一切的缘由归根到底都是“边”在作祟,本文试图抛砖引玉,将你带入丰富多彩的IE扩展世界。

Band,IE扩展的利器

  Band,是Windows Shell对象,当Microsoft将Internet Explorer与Windows Shell彻底集成时,Band对象就成为Windows Shell的一个活跃对象,典型的Band对象有IE的搜索栏、收藏栏、历史栏以及IE的各种工具栏等等。Microsoft公开的Band对象有四类,一类是桌面Band,如位于任务栏上的“快速启动”、“Windows Media Player”等工具条。而其他三种Band,则均出现在IE之中,包括工具栏Band(如MSN Search工具栏)、Explorer Band(如历史、媒体等出现在IE左侧的窗口)和通讯Band(如位于用户区下方的“讨论”窗口)。

  Band对象停靠在对应于IE的框架窗口的四周,因此,给IE的界面提供了灵活的扩张机制。由于IE的用户区基本上用于HTML对象的浏览,因此现在流行的B/S结构不能充分地运用IE灵活、强大的扩展机制,一旦可以将具有C/S结构特征的自定义子窗口挂接到IE的四周,将可以将IE打造成同时具备B/S、C/S体系优势的开发框架。现在,Microsoft已经在Office系列产品中体现类似的思路,例如在Microsoft Word以及Excel中,Microsoft引进了Actions Pane对象,使得位于中心的Office文档可以与停靠在一边的Actions Pane对象进行通讯、交互操作等等,如图1所示。


图1  Word右侧的“任务窗格”对象是个典型的Docking对象,这里体现了Microsoft最新的Smart Document技术

开发你的Band对象,让IE因你而变

  从IE4开始,Band对象就已经存在了,但其复杂的接口使大多数开发人员望而却步。一个典型的Band对象是一个COM对象,需要实现如下几个接口:

  1. IDeskBand
  2. IObjectWithSite
  3. IPersistStream
  4. IInputObject

  每个接口各自包含一些列方法。接口IDeskBand决定了Band对象的基本行为,接口IObjectWithSite提供了Band对象与IE的通讯渠道,接口IInputObject提供了Band对象的输入消息处理,特别该接口是处理对话框消息的关键所在,当需要实现桌面Band对象时,接口IPersistStream提供实现相关的信息存储(如Band对象的位置)。关于Band对象的一般介绍,可以参考Microsoft Internet SDK技术文档,由于Microsoft仅提供了很少的文档与范例,因此Band对象基本上被广大开发者忽略了。事实上,IE内部的扩展性十分强大(当然,强大也意味着危险),就其扩展编程接口而言,IE的扩展性并不输给FireFox。

  理论上,只要支持COM技术的开发工具,都可以用来构造Band对象,然而对于更深层的COM技术细节而言,ATL/MFC类库还是最佳的选择。毋庸置疑,MFC具备强大的界面构造能力,然而,由于缺乏一个有效的衔接,MFC类库与深层COM技术开发一直被一层很薄的“纸”隔开了,这一点不能不说是个遗憾。

CTangramBandImpl,一个Band对象的起点

  首先,我们需要一个构造Band对象的起点,为此我们构造一个基类,以实现上面提到的四个接口,CTangramBandImpl对象的详细实现细节请参考我们提供的源代码,以下是其基本构造:

class CTangramBandImpl :
    public IDeskBand,
    public IObjectWithSite,
    public IPersistStream,
    public IInputObject
{
public:
    CTangramBandImpl(void);
    virtual ~CTangramBandImpl(void);

// IDeskBand
public:
    STDMETHOD(GetBandInfo)(DWORD dwBandID, DWORD dwViewMode,
    DESKBANDINFO* pdbi);

// IObjectWithSite
public:
    STDMETHOD(SetSite)(IUnknown* pUnkSite);
    STDMETHOD(GetSite)(REFIID riid, void **ppvSite);

// IOleWindow
public:
    STDMETHOD(GetWindow)(HWND* phwnd);
    STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode);

// IDockingWindow
public:
    STDMETHOD(CloseDW)(unsigned long dwReserved);
    STDMETHOD(ResizeBorderDW)(const RECT* prcBorder,
    IUnknown* punkToolbarSite, BOOL fReserved);
    STDMETHOD(ShowDW)(BOOL fShow);

// IPersist
public:
    STDMETHOD(GetClassID)(CLSID *pClassID);

// IPersistStream
public:
    STDMETHOD(IsDirty)(void);
    STDMETHOD(Load)(IStream *pStm);
    STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty);
    STDMETHOD(GetSizeMax)(ULARGE_INTEGER *pcbSize);

// IInputObject
public:
    STDMETHOD(HasFocusIO)(void);
    STDMETHOD(TranslateAcceleratorIO)(LPMSG lpMsg);
    STDMETHOD(UIActivateIO)(BOOL fActivate, LPMSG lpMsg);

public:
    BOOL m_bFocus;
    BOOL m_bCommBand;
    void FocusChange(BOOL);

protected:
    virtual BOOL RegisterAndCreateWindow(){return false;};
    virtual void SetBandTitle(DESKBANDINFO* pdbi){};
    CFrameWnd* m_pFrameWnd;
    DWORD m_dwBandID;
    DWORD m_dwViewMode;
    BOOL m_bShow;
    BOOL m_bEnterHelpMode;
    HWND m_hWndParent;
    IInputObjectSite* m_pSite;
};

  对于IE,除了工具栏外,我们将停靠于IE框架左侧的Band称为Explorer Band,而将停靠于框架底边的Band称为通讯Band(Communication Band),从COM的角度看,这两类对象分别属于不同的对象范畴(Category),所有这两类对象均被罗列在IE的View菜单的“Explorer Bar”子菜单中。我们实现的Explorer Band对象的类结构如下:

class ATL_NO_VTABLE CBand : public CTangramBandImpl,
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CBand, &CLSID_Band>,
    public IDispatchImpl<IBand, &IID_IBand, &LIBID_TANGRAMBANDLib>
{
public:
    CBand();

    DECLARE_REGISTRY_RESOURCEID(IDR_BAND)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_CATEGORY_MAP(CBand)
        IMPLEMENTED_CATEGORY(CATID_InfoBand)
        IMPLEMENTED_CATEGORY(CATID_DeskBand)
    END_CATEGORY_MAP()

    BEGIN_COM_MAP(CBand)
        COM_INTERFACE_ENTRY(IBand)
        COM_INTERFACE_ENTRY(IOleWindow)
        COM_INTERFACE_ENTRY(IObjectWithSite)
        COM_INTERFACE_ENTRY_IID(IID_IDockingWindow, IDockingWindow)
        COM_INTERFACE_ENTRY_IID(IID_IInputObject, IInputObject)
        COM_INTERFACE_ENTRY_IID(IID_IDeskBand, IDeskBand)
        COM_INTERFACE_ENTRY(IPersist)
        COM_INTERFACE_ENTRY(IPersistStream)
        COM_INTERFACE_ENTRY(IDispatch)
    END_COM_MAP()

protected:
    virtual BOOL RegisterAndCreateWindow();
    virtual void SetBandTitle(DESKBANDINFO* pdbi);
};

代码片断:

BEGIN_CATEGORY_MAP(CBand)
    IMPLEMENTED_CATEGORY(CATID_InfoBand)
    IMPLEMENTED_CATEGORY(CATID_DeskBand)
END_CATEGORY_MAP()

表明CBand对象隶属两个对象范畴,分别是桌面Band以及Explorer Band,因此,此类对象可以停靠于Windows Shell的桌面,同时也可以停靠于IE框架窗口的左侧,当你在桌面的工具栏中点击鼠标右键,选择Toolbars时,你会看到创建桌面TangramBand的菜单项:

  Communication Band对象CHBand的类结构与CBand的基本一致,最大的区别是:

BEGIN_CATEGORY_MAP(CHBand)
    IMPLEMENTED_CATEGORY(CATID_CommBand)
END_CATEGORY_MAP()

  由于Band对象实现了接口IObjectWithSite,因此,Band对象可以通过该接口得到Band对象所从属的IE实例。为此,我们注意到CTangramBandImpl中已经实现了IObjectWithSite的方法SetSite,我们可以在其中添加如下代码:

IOleCommandTarget* pCmdTarget = NULL;
IWebBrowser2* m_pBrowser;
HRESULT hr = pUnkSite->QueryInterface(IID_IOleCommandTarget,
    (LPVOID*)&pCmdTarget);
if(SUCCEEDED(hr))
{
    IServiceProvider* pSP;
    hr = pCmdTarget->QueryInterface(IID_IServiceProvider,
        (LPVOID*)&pSP);

    pCmdTarget->Release();
    if (SUCCEEDED(hr))
    {
        hr = pSP->QueryService(SID_SWebBrowserApp,
            IID_IWebBrowser2, (LPVOID*)&m_pBrowser);
        if(hr==S_OK)
            TRACE(_T("Get m_pBrowser: %x\n"),m_pBrowser);
        pSP->Release();
    }
}

  这样就可以以得到这个Band对象对应的IE实例(即上述代码中的m_pBrowser)。通过m_pBrowser,Band对象可以访问当前浏览器浏览的页面、调用网页中的脚本代码以及实现其他的Band对象与网页之间的交互功能。

实现基于MFC的Band对象

  为了将MFC类库的强大功能自然纳入Band对象,我们需要一个基于MFC的Frame窗口。我们注意到,CTangramBandImpl已经包含了一个成员变量:

CFrameWnd* m_pFrameWnd;

  这个变量建立了一座衔接Band对象与MFC类库的桥梁,在每个Band对象的RegisterAndCreateWindow函数中,均包含如下代码:

BOOL CHBand::RegisterAndCreateWindow()
{
    RECT rect;
    ::GetClientRect(m_hWndParent, &rect);
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    theApp.m_pBand = this;
    m_pFrameWnd = new CTangramBandFrame();
    CCreateContext m_Context;
    m_Context.m_pNewViewClass = RUNTIME_CLASS(TestFormView);
    CWnd* pWnd = CWnd::FromHandle(m_hWndParent);
    m_pFrameWnd->Create(NULL,_T("TangramBand"),
        WS_CHILD, CRect(rect.left, rect.top,
        rect.right - rect.left, rect.bottom - rect.top),
        pWnd, NULL, NULL, &m_Context);
    theApp.m_pBand = NULL;
    return true;
}

  我们注意到,这段代码创建了一个MFC CFrameWnd对象m_pFrameWnd。由此,一切关于MFC的代码技巧自然而然地盘活了,我们看到一个典型的MFC框架被融入IE之中:

BOOL CTangramBandFrame::OnCreateClient(LPCREATESTRUCT lpcs,
    CCreateContext* pContext)
{
    if(!m_pBand->m_bCommBand)
    {
        m_SplitterWnd.CreateStatic(this, 2, 1);
        m_SplitterWnd.CreateView(0, 0, RUNTIME_CLASS(TestFormView),
            CSize(0, 100), NULL);
        m_SplitterWnd.CreateView(1, 0, RUNTIME_CLASS(TestFormView2),
            CSize(0, 100), NULL);
    }
    else
    {
        m_SplitterWnd.CreateStatic(this, 1, 2);
        m_SplitterWnd.CreateView(0, 0, RUNTIME_CLASS(TestFormView),
            CSize(150, 100), NULL);
        m_SplitterWnd.CreateView(0, 1, RUNTIME_CLASS(TestFormView2),
            CSize(0, 0), NULL);
    }
    return true;
}

  由于Band对象存在于一个动态链接库中,故创建MFC对象时要注意在合适的位置调用代码:

AFX_MANAGE_STATE(AfxGetStaticModuleState());

以使得MFC能够正确处理消息。

输入消息处理

  接口IInputObject用于管理Band对象的输入以及对话框、加速键消息,关于MFC消息,由于Band对象本质上是一种IE插件,故消息队列由IE控制,因此MFC窗口的消息处理是个关键问题。当Band对象捕获输入焦点时,我们必须保证MFC窗口消息被正确处理,下述代码:

STDMETHODIMP CTangramBandImpl::TranslateAcceleratorIO(LPMSG lpMsg)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    if(m_bFocus&&theApp.m_pWnd)
    {
        theApp.m_pWnd->PreTranslateMessage(lpMsg);
        TRACE(_T("TranslateAcceleratorIO\n"));
        return S_OK;
    }
    return S_FALSE;
}

用来处理当前具有输入焦点的MFC窗口theApp.m_pWnd的相关消息,因此具有输入焦点的MFC窗口必须重载函数PreTranslateMessage,特别是CFormView的派生对象,在范例代码中我们给出了重载该函数的例子。

调试与中文处理

  Band对象的调试例程是IE,因此需要修改工程配置,使得调试过程定向到IE。为使得Band对象能够正确接受中文输入,代码工程必须是基于UNICODE的。

  您可以到杂志的站点(http://mag.csdn.net/msdn)上下载本刊附带的源代码,其中的MsdnJxControl解决方案中有本文所提到的项目的完整实现。该解决方案已经为您做好了所有的配置工作,但还是希望您能够通过属性面板仔细观察该解决方案的具体设置。

小结

  正如Microsoft系列的其他产品一样,Internet Explorer中存在着丰富的扩展空间。对于Microsoft而言,框架窗口的四周存在着可停靠对象的口岸,因此你可以在这些岸边开拓你的软件空间以展示你的软件魅力,如果你读到了本文,那么你就开始吧,充满诱惑的海岸,等待你去开拓。



本文引用通告地址: http://blog.csdn.net/dotnet_editor/services/trackbacks/455812.aspx

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百度家电行业报告摘录

上图反映了关注家电信息的网民在百度频道的活跃情况。很明显&#xff0c;百度贴吧、百度图片和百度知道是网民最活跃的频道&#xff0c;特别是百度贴吧&#xff0c;有超过四分之一的检索量都来自于百度贴吧。百度有不少以家电品牌或产品为主题的贴吧&#xff0c;比如海尔吧、TC…

如何识别能把桥压塌的大车?快看!能救命!

全世界只有3.14 % 的人关注了青少年数学之旅2019年10月10日晚6:10左右&#xff0c;江苏无锡市北环路附近一高架桥出现桥面侧翻&#xff0c;经现场初步勘测&#xff0c;桥下被压小车3辆&#xff08;其中一辆系停放车辆&#xff0c;无人&#xff09;。经搜救确认&#xff0c;事故…

如何用outlook express 收发邮件

一&#xff0e;创建用户1.打开Outlook express软件&#xff0c;单击“工具”选项&#xff0c;选择“帐户”选项。2.单击“添加”按钮&#xff0c;选择“邮件”选项。3.在“连接向导”中&#xff0c;输入发件人姓名&#xff0c;在对方收到邮件后&#xff0c;发件人姓名将会显示在…

java虚拟机 山寨机_十年前的山寨机居然有系统?没错,还是纯国产的

文丨太平洋数码苍茫的天涯是我的爱&#xff0c;绵绵的青山脚下花正开&#xff0c;什么样的节奏是最呀最摇摆……你是我天边最美的云彩&#xff0c;让我用心把你留下来(留下来)……不知道大家是否还记得这段熟悉的旋律&#xff0c;总之小编我今天再次回听这首歌的时候身体依然会…

dotnet中的counters说明(一)

在dotnet中&#xff0c;常用的收集指标有&#xff1a;System.Runtime 计数器Microsoft.AspNetCore.Hosting计数器Microsoft.AspNetCore.Http.Connections计数器Microsoft-AspNetCore-Server-Kestrel计数器System.Net.Http计数器(>.NET5可用)System.Net.NameResolution计数器…

当才华还撑不起梦想时,你应该静下心来看这些

全世界只有3.14 % 的人关注了青少年数学之旅在工作之余&#xff0c;我们大量的碎片时间被手机占据。无意识的刷手机打发无聊&#xff0c;不如有趣又高品质的积累。我们特意精选了在不同领域的几个高品质公众号代表&#xff0c;希望让你在快乐打发闲暇时光的同时&#xff0c;也能…

.net core ——微服务内通信Thrift和Http客户端响应比较

1、Benchmark介绍wiki中有定义&#xff1a;基准测试是运行计算机程序&#xff0c;一组程序或其他操作的行为&#xff0c;以便评估对象的相对性能&#xff0c;通常是通过对其运行许多标准测试和试验。目前许多成熟的github开源项目&#xff0c;均采用Benchmark测试结果作为性能依…

Discuz添加自定义模板广告

在做Discuz中广告的时候碰到个大问题&#xff0c;现在我需要做一个轮播的通屏广告位&#xff0c;调用广告图片的代码应该是以下代码&#xff1a;<ul> <li style"background:url(图片地址) no-repeat center top;"><a href"#"></a…

我居然手写了Spring框架

手写完了刚参加工作那会接触java还是用的struct的时代&#xff0c;后面在SSH火爆时代的时候我转战.net,多年之后公司转java技术栈已经是Spring的天下&#xff0c;源码嚼了很多遍于是很想尝试把这套东西用在.net平台上。社区有个Spring.net项目已经多年不维护了&#xff0c;而且…

下班以后看什么,决定你人生的高度

全世界只有3.14 % 的人关注了青少年数学之旅王小波说&#xff1a;我活在世上&#xff0c;无非想要明白些道理&#xff0c;遇见些有趣的事&#xff0c;倘能如我所愿&#xff0c;我的一生就算成功。你的圈子将决定你的人生。每一位对事物都有着独特的态度让你成为一个有趣的人。今…

Mahout的taste推荐系统里的几种Recommender分析

Taste简介 看自:http://blog.csdn.net/zhoubl668/article/details/13297583Mahout 是apache下的一个java语言的开源大数据机器学习项目&#xff0c;与其他机器学习项目不同的是&#xff0c;它的算法多数是mapreduce方式写的&#xff0c;可以在hadoop上运行&#xff0c;并行化处…

bytecode java_Java 字节码解读

一、源代码public classPeople {privateString name;private intage;}二、利用Javap 反编译查看字节码结构信息javap -v People.class结果如下Classfile /D:/work/byte-code/src/People.classLast modified2020-12-9; size 240bytesMD5 checksum 54b8c1ad94a9c9cf5074fd8520454…

他狂骗五千万美元消失17年...却被一个纪录片导演锲而不舍的追到了镜头前!...

全世界只有3.14 % 的人关注了青少年数学之旅他&#xff0c;曾经是个做啥都无师自通的天才。他&#xff0c;在众多的行业里&#xff0c;莫名进入到了艺术品行业&#xff0c;没过多久&#xff0c;他靠着惊人的自学能力&#xff0c;成了艺术圈里所有人津津乐道的画商。他&#xff…

合肥.NET俱乐部第二期技术沙龙活动预告

各位亲爱的.NET从业和爱好者们&#xff1a; 大家好&#xff0c;自从19年举办.NET俱乐部第一期技术沙龙后&#xff0c;.NET在开源以及跨平台的加持下继续飞速发展&#xff0c;各种开源项目不断涌现&#xff0c;各种社区活动持续开展&#xff0c;.NET的生态逐步丰富&#xff0…

你见过扇贝游泳吗? | 今日趣图

全世界只有3.14 % 的人关注了青少年数学之旅正常颈椎VS颈椎病患者的颈椎丁香医生小学入学测试题你知道答案吗&#xff1f;图源网络为什么我的假期这么短&#xff1f;文丁香医生五子棋必胜秘籍图源网络那些女生称呼的真相图沙县小吃你有见过扇贝游泳吗小迷妹神吐槽

实名羡慕,国内这些厂.NET薪资高的吓人!

小米招C#工程师&#xff0c;要求WPF自动化设备&#xff0c;20~40k * 14薪&#xff1b;小鹏招WPF/.NET/PLC&#xff0c;25~40k年终&#xff1b;特斯拉招自动化控制16~25k * 15薪&#xff0c;华为应届硕士C#运动控制&#xff0c;就已经17~25k *14薪了。这些招聘让人眼热&#xff…

修车工在生命最后,才知道自己的儿子是如今世界首富!

全世界只有3.14 % 的人关注了青少年数学之旅在美国亚利桑那州凤凰城&#xff0c;曾经住着一个叫Ted Jorgensen的自行车修理工&#xff0c;他开了一家自行车店&#xff0c;平时默默的卖车修车&#xff0c;日子过的平平淡淡。在凤凰城郊区&#xff0c;他和二婚妻子住在这么一间不…

Sublime Text 2 中运行 PHP

2019独角兽企业重金招聘Python工程师标准>>> Sublime Text 2 has the concept of build systems. This basically means that if you are editing a Python file then you can run the Python interpreter on the source file your are editing and see the output …

python退出帮助系统help应该使用exit_python--help - tesion

python help学习python的过程中&#xff0c;难免遇到一些生疏的东西&#xff0c;为此需要参看相关的文档说明。Linux下众所周知有man可以查找系统的命令帮助页&#xff1b;对于python也提供了自己的帮助文档的2种方式&#xff1a;方式一&#xff1a;启动python解释器&#xff0…

试用GitHub Copilot一周后,我给你的建议是:不要使用它

如果你还不知道“GitHub Copilot”&#xff0c;它是GitHub推出“AI程序员”插件&#xff0c;可以根据你在VS Code输入的部分代码或注释&#xff0c;自动生成完整的代码。在我上次的文章中&#xff0c;演示了GitHub Copilot是如何工作的。刚开始&#xff0c;我对能够试用GitHub …