返回列表 发帖

[交流] [原创]十五分钟教你写出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已经为我们自动产生了这个类的定义:

  1. class CTWPlugin :
  2.     public ITWPlugin,
  3.     public CComObjectRoot,
  4.     public CComCoClass<CTWPlugin,&CLSID_TWPlugin>
复制代码


一个完整的Band Object需要支持IDispatch、IObjectWithSite和IDeskBand三个接口。前两个我们可以用ATL的默认实现,第三个是从浏览器组件里面来的,ATL并没有带默认实现,所以我们一会儿还需要实现它。修改后如下:

  1. class CTWPlugin :
  2.     public CComObjectRoot,
  3.     public CComCoClass<CTWPlugin,&CLSID_TWPlugin>,
  4.     public IDispatchImpl<ITWPlugin, &IID_ITWPlugin, &LIBID_TWPLUGINDEVLib>,
  5.     public IObjectWithSiteImpl<CTWPlugin>,
  6.     public IDeskBand
复制代码



添加头文件,让编译器能够找到IDeskBand

  1. #include <shlobj.h>
  2. #include <comdef.h>
复制代码



由于我们新增了三个接口,为了实现COM的接口转换,我们还需要添加如下的接口映射,修改后:

  1. BEGIN_COM_MAP(CTWPlugin)
  2.     COM_INTERFACE_ENTRY(ITWPlugin)
  3.     COM_INTERFACE_ENTRY2(IDispatch, ITWPlugin)
  4.     COM_INTERFACE_ENTRY(IObjectWithSite)
  5.     COM_INTERFACE_ENTRY(IDeskBand)
  6.     COM_INTERFACE_ENTRY2(IOleWindow, IDeskBand)
  7.     COM_INTERFACE_ENTRY2(IDockingWindow, IDeskBand)
  8. END_COM_MAP()
复制代码


接下来,我们需要给出IDeskBand的具体实现。把IDeskBand中需要被实现的接口加到TWPlugin.h的public函数当中

  1.     // *** IOleWindow methods ***
  2.     STDMETHOD(GetWindow) (THIS_ HWND * lphwnd);
  3.     STDMETHOD(ContextSensitiveHelp) (THIS_ BOOL fEnterMode);

  4.     // *** IDockingWindow methods ***
  5.     STDMETHOD(ShowDW)         (THIS_ BOOL fShow);
  6.     STDMETHOD(CloseDW)        (THIS_ DWORD dwReserved);
  7.     STDMETHOD(ResizeBorderDW) (THIS_ LPCRECT   prcBorder,
  8.                                      IUnknown* punkToolbarSite,
  9.                                      BOOL      fReserved);
  10.     // *** IDeskBand methods ***
  11.     STDMETHOD(GetBandInfo)    (THIS_ DWORD dwBandID, DWORD dwViewMode,
  12.                                 DESKBANDINFO* pdbi);
复制代码


在TWPlugin.cpp当中,我们给出上面接口的空实现(mock up)。

  1. STDMETHODIMP CTWPlugin::GetWindow(HWND *phWnd)
  2. {
  3.     return E_NOTIMPL;
  4. }

  5. STDMETHODIMP CTWPlugin::ContextSensitiveHelp(BOOL fEnterMode)
  6. {
  7.     return S_OK;
  8. }

  9. STDMETHODIMP CTWPlugin::ShowDW(BOOL fShow)
  10. {
  11.     return E_NOTIMPL;
  12. }

  13. STDMETHODIMP CTWPlugin::CloseDW(DWORD dwReserved)
  14. {
  15.     return S_OK;
  16. }

  17. STDMETHODIMP CTWPlugin::ResizeBorderDW( LPCRECT prcBorder, IUnknown* punkSite, BOOL fReserved)
  18. {
  19.     return E_NOTIMPL;
  20. }

  21. STDMETHODIMP CTWPlugin::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi)
  22. {
  23.     return E_NOTIMPL;
  24. }
复制代码


此时,我们插件的COM接口部分代码已经实现完毕。

插件界面的实现

下面让我们来把UI加上去。实现方法有多种,如Win32API、MFC等,本文的例子选择用WTL——一套基于ATL的轻量级窗口运行库。

在TWPlugin.h当中加入头文件<atlwin.h>,并给CTWPlugin再添加一个基类CWindowImpl<CTWPlugin>和一个私有成员DWORD m_dwBandID,在构造函数中初始化为-1,加入窗口消息映射
  1. BEGIN_MSG_MAP(CTWPlugin)
  2.     MESSAGE_HANDLER(WM_PAINT, OnPaint)
  3.     MESSAGE_HANDLER(WM_CREATE, OnCreate)
  4. END_MSG_MAP()
复制代码



另外创建两个消息**函数OnPaintOnCreate
  1. LRESULT OnPaint( UINT, WPARAM, LPARAM, BOOL& );
  2. LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& );
复制代码



并在TWPlugin.cc当中实现
  1. LRESULT CTWPlugin::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bResult) {
  2.     PAINTSTRUCT ps;
  3.     HDC hDC = BeginPaint(&ps);
  4.     TextOut(hDC, 0, 0, _T("Hello"), 5);
  5.     EndPaint(&ps);
  6.     return 0;
  7. }  
  8. LRESULT CTWPlugin::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bResult) {
  9.     // Rebar Info
  10.     if( m_dwBandID != -1 )
  11.     {
  12.         REBARBANDINFO    rbBand;
  13.         rbBand.cbSize        = sizeof(REBARBANDINFO);
  14.         rbBand.fMask        = RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_ID;
  15.         rbBand.wID            = m_dwBandID;


  16.         HWND hWndReBar = GetParent();
  17.         int nIndex = ::SendMessage( hWndReBar, RB_IDTOINDEX, m_dwBandID, NULL );
  18.         ::SendMessage( hWndReBar, RB_GETBANDINFO, nIndex, (LPARAM)&rbBand );

  19.         rbBand.cxIdeal        = 50;
  20.         rbBand.cxMinChild    = 50;
  21.         ::SendMessage( hWndReBar, RB_SETBANDINFO, nIndex, (LPARAM)&rbBand );
  22.     }
  23.     return 0;
  24. }
复制代码

窗体实现完毕。

[ 本帖最后由 needed 于 2008-9-1 13:47 编辑 ]
1

评分人数

  • mutalisker

C++好久没用了

TOP

对我而言,是对牛弹琴

TOP

提示: 作者被禁止或删除 内容自动屏蔽

TOP

hjfgjhfghc

TOP

不懂这个有什么用处
【寻木】 http://xunmoo.taobao.com/ 仙作经典 木雕传奇 (木雕工艺品)

TOP

就是呀, 给你sample呀, 按楼主的这种写法, 根本没法操作呀, 我用VS2010了

TOP

试一下 看好用不

TOP

没个demo吗

TOP

api编程还是比较有难度的,主要是没注释这点让初学者非常头疼....

TOP

怎麼沒有 sample project下載, 不完善.... 樓主加個吧........

TOP

支持一下

TOP

返回列表