打造个性化的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对象,需要实现如下几个接口:
- IDeskBand
- IObjectWithSite
- IPersistStream
- 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
打造个性化的Internet Explorer相关推荐
- Internet Explorer 8 Beta 2十大看点
Internet Explorer,即我们常说的IE浏览器,从诞生伊始到现在已经经历了十余年,从功能简单的Internet Explorer 1到功能强大的Internet Explorer 7,In ...
- 加载java ie停止工作_打开网页,IE浏览器提示Internet Explorer 已停止工作什么原因?怎么解决?...
如果我们正在网页编辑重要的内容,浏览器突然提示"Internet Explorer 已停止工作",这是件非常不愉悦的事.so,凡事都要知道点,下面小编就为大家详细介绍一下4种解决方 ...
- android模拟器32位下载,【天天安卓模拟器和Internet Explorer 10(32位)哪个好用】天天安卓模拟器和Internet Explorer 10(32位)对比-ZOL下载...
ie10浏览器是为全世界所广泛使用的 Windows Internet Explorer浏览器系列的最新版本,ie10浏览器集成了更多个性化.智能化.隐私保护的新功能,为您的网络生活注入新体验,让您每 ...
- Internet Explorer已停止工作的解决方法
方案1:重置Internet Explorer默认选项 在Internet Explorer浏览器中,打开"工具"选项,打开"Inetnet 选项"对话 ...
- Win7系统Internet Explorer已停止工作怎么办?
在使用电脑的时候我们总是会遇到一些问题,其中比较常见的就是遇到Internet Explorer已停止工作,那么当你遇到Win7系统Internet Explorer已停止工作怎么办?其实很好解决的, ...
- vs2008与IIS 7.0使用在vista上时出现的问题及解决方法(Internet Explorer 无法显示该页面)(VS2008: IE Cannot Display Web Page)...
我的系统是Vista Ultimate SP1,先安装了vs2008 ,然后再安装了IIS7.0之后就出现了一系列的问题. 问题:通过vs2008启动程序调试时报错.错误提示为:Internet Ex ...
- Windows Server 2008 禁用Internet Explorer 增强的安全配置
1.1.1 任务3:禁用Internet Explorer 增强的安全配置 Internet Explorer 增强的安全配置 (IE ESC) 采用一种方式配置您的服务器和 Microsoft In ...
- 如何安装或卸载 Internet Explorer 9?
如果您的电脑上运行的是 Windows Vista 或 Windows 7,则可以安装 Windows Internet Explorer 9 来替代您现有的 Internet Explorer 版本 ...
- [导入]解决“Internet Explorer 无法打开 Internet站点已终止操作”问题
昨天晚上添加了展现/隐藏菜单的按钮,今天早晨一打开博客,出现Internet Explorer 无法打开 Internet站点已终止操作.开始以为是网络的问题,可是刷新以后问题依旧.在google上搜 ...
最新文章
- iOS架构设计-URL缓存(上)
- android launcher
- 准确率不变 损失率下降_最新斯诺克排名奥沙利文排在第二,丁俊晖排第十,第一保持不变...
- python游戏编程入门txt-Python真好玩:教孩子学编程 PDF 完整原版
- 取消任务栏中又出现了红色的盾牌
- 安装仪表盘控件Iocomp会遇到的几个常见问题
- 万能makefile深入浅出 - 第二篇
- python 检测文件更新失败_依赖错误,检测更新失败,提示这个
- ubantu中rpm转换成deb(软件包格式)
- 20190415 - iOS11 无法连接到 App Store 的解决办法
- LibreOJ 6283 数列分块入门 7(区间加区间乘区间求和)
- 固高运动控制卡IO口输入输出
- 软件工程系统建模总结
- 最新版校园招聘进大厂系列----------(5)百度篇 -----未完待续
- 单细胞转录组测序建库方法小结
- Codevs 1253 超级市场
- 淘宝API app商品搜索
- php为网页更改颜色,php如何设置网页颜色?
- 图解Stm32使用jlink下载程序时jtag接口(SW和JTAG模式)的简化方法
- The Generalized Detection Method for the Dim Small Targets by Faster R-CNN Integrated with GAN 论文翻译
热门文章
- 開發MOSS2007 Masterpage的一些經驗
- matlab柱状斜线_Matlab小练习:按斜线方向依次赋值矩阵
- STM32安装Keil5、芯片支持包、startup启动文件(启动过程分析)、建立工程、烧写
- jmeter聚个报告怎么看qps_【jmeter】jmeter测试网站QPS
- php 对比两个压缩包内容,php实现的zip文件内容比较类
- 【整理】Spring 常用注解!千万不要错过!
- Linux有关Shell算数运算的用法补充笔记
- Linux进程的概念笔记
- webstock php,workerman_connection
- python中的装饰器-(重复阅读)