|
在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);
pCmdTarg [1] [2] 下一页 |