实现Office的COM插件
作者: ZDNet China
2003-08-11 01:43 PM

当软件开发者考虑扩展Microsoft Office时,最常想到方法就是是使用VB了。实际上,用C++和ATL扩展Office是相当容易的。

我将向你演示如何建立项目以及如何把Office插件作为COM对象注册到Office中去。(本文用Outlook来作为插件的主程序。)

项目的建立

让我们建立一个新的ATL项目。首先,添加一个叫着Plugin的简单COM对象。用类向导把_IDTExtensibility2接口添加到Plugin类中。(别忘了选中IsupportErrorInfo复选框。)

类向导会把下述方法添加到Plugin类中:OnConnection、OnDisconnection、OnAddinsUpdate、 OnStartupComplete、OnBeginShutdown。本文只处理OnConnection和OnDisconnection方法。

然后,在Plugin头文件中创建一个类型定义(typedef,它只是用来简化代码的),如下所示:

#define APPID 102
class CPlugin;
typedef IDispEventImpl<APPID, CPlugin, &DIID_ApplicationEvents,
&LIBID_Outlook,9,0> OutLookSink;

我们现在需要在Plugin类的继承列表中添加OutLookSink类:

class ATL_NO_VTABLE CPlugin :
       public IPlugin,
       public IDispatchImpl<_IDTExtensibility2, &__uuidof(_IDTExtensibility2),
               &LIBID_AddInDesignerObjects, /* wMajor = */ 1, /* wMinor = */ 0>,
    public OutLookSink

在Plugin.h文件中为Mso9.dll和Msoutl9.olb引入声明,如下所示:

#import "C:\Program Files\Common Files\Designer\msaddndr.dll" \
raw_interfaces_only, \
raw_native_types, no_namespace, named_guids, auto_search

#import "C:\Program Files\Microsoft Office\Office\mso9.dll" \
rename_namespace("Office") named_guids

#import "C:\Program Files\Microsoft \
 Office\Office\msoutl9.olb" \
rename_namespace("Outlook"), raw_interfaces_only, \
 named_guids

using namespace Office;
using namespace Outlook;

注意,你需要按照你的系统中的文件路径来修正上面那些被引入文件的路径。



你必须把Office扩展作为COM组件注册到Office中去。Visual C++自动产生.rgs文件来控制COM注册。我们将需要添加额外的项目加到.rgs文件来支持Office的自动注册。

在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office键下,有若干个子键,每一个子键对应于系统中安装的每一个Office应用程序。在这些键中有插件关键字(add-in key)。


为了在Office中注册我们的COM对象,我们需要在插件键中添加新键。在HKEY_CURRENT_USER下同样有对应的树来控制单个用户的插件,而HKEY_LOCAL_MACHINE控制整个机器的插件。

下面是.rgs文件中Outlook的插件注册码:

HKCU
{
    NoRemove Software
    {
        NoRemove Microsoft
        {
            NoRemove Office
            {
                NoRemove Outlook
                {
                    NoRemove Addins
                    {
                        ForceRemove OutlookDemo.Plugin.1
                        {
                            val FriendlyName = s 'Outlook Plugin'
                            val Description = s 'An Outlook plugin'
                            val LoadBehavior = d 3
                            val CommandLineSafe = d 0
                        }
                        
                    }
                }
            }
        }
    }
}

这套注册项是为特定用户注册插件的,为了把插件设置为整个系统范围之内可用,只需把HKCU改为HKLM。



LoadBehavior的值控制了主程序运行后插件的行为。为了让主程序开始运行后插件自动运行,把LoadBehavior的值设置为3。如果想让用户手工载入插件,就把LoadBehavior设置为8。

如果你编译了这个项目,你就有了一个Outlook 插件;这个插件实际上没有做任何事,但是它的确是一个可以工作的插件。

我们现在向这个插件添加一些功能。首先,添加一个新的成员变量:

     _ApplicationPtr m_pApp;

然后设置OnConnection方法:

    STDMETHOD(OnConnection)(LPDISPATCH Application,
                                                            ext_ConnectMode ConnectMode,
                                                            LPDISPATCH AddInInst,
                                                            SAFEARRAY * * custom)
    {
             HRESULT rslt = S_OK;
             try{
                  m_pApp = Application;
                  OutLookSynch::DispEventAdvise(m_pApp);
             }catch(_com_error &e){
                  OutputDebugString(e.ErrorMessage());
                                    rslt = E_UNEXPECTED;                
             }
             return rslt;
    }

再设置与之对应的OnDisconnection方法,如下所示:

    STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode,SAFEARRAY
* * custom)
      {
                 HRESULT rslt = S_OK;
                 try{
                       OutLookSynch::DispEventUnadvise(m_pApp);
                 }catch(_com_error &e){
                       OutputDebugString(e.ErrorMessage());
                       rslt = E_UNEXPECTED;
                 }
                 return rslt;
    }

现在我们可以收到好几种事件,例如,OnNewMail事件。得到事件的信息以及它们的ID的最好方法就是打开MSVC工具菜单上的OLE/COM观察器(viewer)。

  1. 进入File菜单并选择View Typelib。
  2. 浏览Msoutl9.olb文件。
  3. Scroll down ApplicationEvents的dispinterface。注意OnNewMail事件的ID号为0x0000f003.。
  4. 向Cplugin类添加一个叫着OnNewMail的方法。
  5. 建立一个Sink映射来把OnNewMail方法关联到OnNewMail事件。

    BEGIN_SINK_MAP(CPlugin)
              SINK_ENTRY_EX(APPID, Outlook::DIID_ApplicationEvents, /
0x0000f003, OnNewMail)
    END_SINK_MAP()

现在我们只需要完成OnNewMail事件处理函数了。

    void _stdcall OnNewMail(){
           MessageBox(NULL, "New Mail", NULL, MB_OK);
    }

如果你想消除这些讨厌的弹出式对话框(由该项目所创建),那么最简单方法就是遵循下面的步骤:

  1. 进入Outlook的Tools | Options菜单项。
  2. 选择Other标签。
  3. 点击Advanced Options按钮。
  4. 点击COM 的插件按钮。
  5. 选中Outlook的Plugin项并点击Remove。
如你所见,Office的COM插件实现起来相对来说并不麻烦甚至可以说很轻松。创建一个COM Office插件最困难的计算出所有参数的初值。不过一旦你发现可以在OLE/COM查看器中得到这些信息,这也就不成问题了。