- UID
- 5137
- 帖子
- 138
- 精华
- 3
- 贡献
- 24
- 推广
- 0
- 有效BUG
- 0
- 注册时间
- 2005-11-25
|
[交流] [原创]十五分钟教你写出TheWorld的COM状态栏插件
本文的对象是有基本visual studio开发经验,懂得使用visual studio appwizard来创建各种项目和类,懂得C++和Windows编程的基本概念,但可能没有COM组件开发经验的开发者。
什么是COM?通俗来说,COM是一套在Windows内部广泛使用的接口标准。它的好处很多,例如跨语言,跨平台,二进制兼容等等,但是我们不需要了解过多,在这篇文章里面,我们只需要记住,IE平台其实也是由成百上千个COM组件搭建起来的,各个组件之间通过COM定义了一套标准的通讯语言。TheWorld是基于IE内核的一个浏览器框架实现,所以也遵循着这些接口标准。
TheWorld的状态栏插件其实是一个简单的COM组件,它使用了标准的IDeskBand接口,是一个标准的符合微软规范的Band Object(不知道BandObject的可以参考知识库文章http://msdn.microsoft.com/en-us/library/bb776819(VS.85).aspx,不过这不影响我们后面内容的理解)。TheWorld浏览器通过这个标准的COM接口和插件进行通讯,包括通知插件进行加载,重画等,插件由此可以实现强大的几乎无所不能的功能。(Javascript插件受语言本身的限制很大,基本只能实现一些很简单的网页渲染和数据转换功能。)
我们选择用ATL来实现这个COM组件。ATL是一套封装COM的通用实现,以便开发者进行快速开发的轻量级类库。本文以VC6为例讲解具体的操作流程,VC2005及VC2008的操作大同小异,不再赘述。
COM组件的实现
首先,新建一个项目,选择ATL COM AppWizard,项目名字我们选TWPluginDev,Server类型我们选DLL,保持其他选项为默认值,确定。这样VC自动帮我们创建好了一个ATL项目。
然后,在ClassView当中创建一个新的ATL Class,类名CTWPlugin,接口类型我们选择相对简单的Custom,保持其他选项为默认值,确定。此时,ClassView里面就新增添了一个COM接口ITWPlugin和对应的实现类CTWPlugin。
我们打开CTWPlugin的头文件TWPlugin.h,VC的Wizard已经为我们自动产生了这个类的定义:
- class CTWPlugin :
- public ITWPlugin,
- public CComObjectRoot,
- public CComCoClass<CTWPlugin,&CLSID_TWPlugin>
复制代码
一个完整的Band Object需要支持IDispatch、IObjectWithSite和IDeskBand三个接口。前两个我们可以用ATL的默认实现,第三个是从浏览器组件里面来的,ATL并没有带默认实现,所以我们一会儿还需要实现它。修改后如下:
- class CTWPlugin :
- public CComObjectRoot,
- public CComCoClass<CTWPlugin,&CLSID_TWPlugin>,
- public IDispatchImpl<ITWPlugin, &IID_ITWPlugin, &LIBID_TWPLUGINDEVLib>,
- public IObjectWithSiteImpl<CTWPlugin>,
- public IDeskBand
复制代码
添加头文件,让编译器能够找到IDeskBand
- #include <shlobj.h>
- #include <comdef.h>
复制代码
由于我们新增了三个接口,为了实现COM的接口转换,我们还需要添加如下的接口映射,修改后:
- BEGIN_COM_MAP(CTWPlugin)
- COM_INTERFACE_ENTRY(ITWPlugin)
- COM_INTERFACE_ENTRY2(IDispatch, ITWPlugin)
- COM_INTERFACE_ENTRY(IObjectWithSite)
- COM_INTERFACE_ENTRY(IDeskBand)
- COM_INTERFACE_ENTRY2(IOleWindow, IDeskBand)
- COM_INTERFACE_ENTRY2(IDockingWindow, IDeskBand)
- END_COM_MAP()
复制代码
接下来,我们需要给出IDeskBand的具体实现。把IDeskBand中需要被实现的接口加到TWPlugin.h的public函数当中
- // *** IOleWindow methods ***
- STDMETHOD(GetWindow) (THIS_ HWND * lphwnd);
- STDMETHOD(ContextSensitiveHelp) (THIS_ BOOL fEnterMode);
- // *** IDockingWindow methods ***
- STDMETHOD(ShowDW) (THIS_ BOOL fShow);
- STDMETHOD(CloseDW) (THIS_ DWORD dwReserved);
- STDMETHOD(ResizeBorderDW) (THIS_ LPCRECT prcBorder,
- IUnknown* punkToolbarSite,
- BOOL fReserved);
- // *** IDeskBand methods ***
- STDMETHOD(GetBandInfo) (THIS_ DWORD dwBandID, DWORD dwViewMode,
- DESKBANDINFO* pdbi);
复制代码
在TWPlugin.cpp当中,我们给出上面接口的空实现(mock up)。
- STDMETHODIMP CTWPlugin::GetWindow(HWND *phWnd)
- {
- return E_NOTIMPL;
- }
- STDMETHODIMP CTWPlugin::ContextSensitiveHelp(BOOL fEnterMode)
- {
- return S_OK;
- }
- STDMETHODIMP CTWPlugin::ShowDW(BOOL fShow)
- {
- return E_NOTIMPL;
- }
- STDMETHODIMP CTWPlugin::CloseDW(DWORD dwReserved)
- {
- return S_OK;
- }
- STDMETHODIMP CTWPlugin::ResizeBorderDW( LPCRECT prcBorder, IUnknown* punkSite, BOOL fReserved)
- {
- return E_NOTIMPL;
- }
- STDMETHODIMP CTWPlugin::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
- {
- return E_NOTIMPL;
- }
复制代码
此时,我们插件的COM接口部分代码已经实现完毕。
插件界面的实现
下面让我们来把UI加上去。实现方法有多种,如Win32API、MFC等,本文的例子选择用WTL——一套基于ATL的轻量级窗口运行库。
在TWPlugin.h当中加入头文件<atlwin.h>,并给CTWPlugin再添加一个基类CWindowImpl<CTWPlugin>和一个私有成员DWORD m_dwBandID,在构造函数中初始化为-1,加入窗口消息映射
- BEGIN_MSG_MAP(CTWPlugin)
- MESSAGE_HANDLER(WM_PAINT, OnPaint)
- MESSAGE_HANDLER(WM_CREATE, OnCreate)
- END_MSG_MAP()
复制代码
另外创建两个消息**函数OnPaint和OnCreate:
- LRESULT OnPaint( UINT, WPARAM, LPARAM, BOOL& );
- LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& );
复制代码
并在TWPlugin.cc当中实现
- LRESULT CTWPlugin::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bResult) {
- PAINTSTRUCT ps;
- HDC hDC = BeginPaint(&ps);
- TextOut(hDC, 0, 0, _T("Hello"), 5);
- EndPaint(&ps);
- return 0;
- }
- LRESULT CTWPlugin::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bResult) {
- // Rebar Info
- if( m_dwBandID != -1 )
- {
- REBARBANDINFO rbBand;
- rbBand.cbSize = sizeof(REBARBANDINFO);
- rbBand.fMask = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_ID;
- rbBand.wID = m_dwBandID;
- HWND hWndReBar = GetParent();
- int nIndex = ::SendMessage( hWndReBar, RB_IDTOINDEX, m_dwBandID, NULL );
- ::SendMessage( hWndReBar, RB_GETBANDINFO, nIndex, (LPARAM)&rbBand );
- rbBand.cxIdeal = 50;
- rbBand.cxMinChild = 50;
- ::SendMessage( hWndReBar, RB_SETBANDINFO, nIndex, (LPARAM)&rbBand );
- }
- return 0;
- }
复制代码
窗体实现完毕。
[ 本帖最后由 needed 于 2008-9-1 13:47 编辑 ] |
-
1
评分人数
-
|