针对Microsoft Office系列的编程一直是比较麻烦的领域;这主要是因为Office里面大量的COM对象构成的庞大的体系结构让人望而生畏。

编写Outlook插件也是如此。虽然.Net的到来使得深入系统底层的RAD开发成为了可能,但是由于Outlook仍然是COM接口,要写Outlook插件,从根本上来说仍旧是编写Outlook COM插件的过程。凭借.Net和COM的互操作能力,才使得C#/VB.Net等语言编写的插件能够被Outlook等Office程序调用。

首先,无论是Outlook也好,Word也好,所有的Office应用程序都使用了共同的COM接口_IDTExtensibility2来与插件进行通信。因此,要开发一个Outlook插件(当然其他Office插件也一样),必须实现这个接口。这个接口定义在AddInDesigner Object Library当中,位于<drive>\Program Files\Common Files\DESIGNER\MSADDNDR.DLL文件中。在Visual Studio.Net当中,对这个COM接口进行了包装,包装后的Assembly名为Extensibility,该接口名为IDTExtensibility2。

下面就通过一个演示项目来说明一下。

在Outlook当中有一个BUG/设计缺陷,就是当回复、转发一封邮件的时候,这封回复/转发邮件的文字编码会自动变得和收到的邮件相同。例如,我经常从美国收到编码为US-ASCII的邮件,当我用中文添加一些评注,转发给中国人的朋友时,如果我忘记了手动把编码改成UTF-8或GB2312,邮件内容就会变成乱码。即便我在“新邮件选项”里面把新邮件的默认编码设置为UTF-8也没有用。

这种时候,如果有一个Outlook插件能够自动将所发送邮件的编码改成UTF-8有多好啊。于是,就有了下面的制作过程。

Step 1 创建Outlook插件项目

首先,在Visual Studio 2005(其他版本也可。这里以2005为例说明)中,新建一个Project,类型为Other Project Types > Extensibility > Shared Add-in,并在Application Host画面选择要使用这个Add-in的Office程序。这样一个Add-in Project就生成了。在这个Project当中,我们可以看到如下两个平时不常见的Reference被添加到了项目当中:

  • Extensibility:这就是所有Office插件都要实现的接口所在的Assembly
  • Microsoft.Office.Core:Office插件的共通组件库

Step 2 实现IDTExtensibility2接口

打开Connect.cs文件,我们就可以看到VS已经帮我们创建好了一个叫做Connect的类,来实现IDTExtensibility2接口。这个接口和一般的Host/Add-in体系结构类似,内容如下:

  • void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom);
    当COM add-in被连接到host应用程序时OnConnection事件将被触发。

    • Application - 指向加载这个add-in的host应用程序,如Outlook。
    • ConnectMode - 指定了add-in的加载模式。
    • AddInInst - 指向一个代表当前add-in的COMAddIn对象(定义在Microsoft.Office.Core当中)。
    • Custom - 用户自定义参数。
  • void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom);
    与上一个相反,这个是把add-in从host程序中卸载时引发的事件。RemoveMode指定了卸载模式。
  • void OnAddInsUpdate(ref Array custom);
    当COM add-in被安装或移除的时候引发该事件。
  • void OnStartupComplete(ref Array custom);
  • void OnBeginShutdown(ref Array custom);
    上面两个事件在host程序完全结束加载、以及即将开始关闭的时候引发。

简单实现上面几个接口的例子可以参考微软知识库的302901号文章

Step 3 截获ItemSend事件 更改MailItem属性

实现了上面的接口,我们就要在其中添加代码了。由于我们的目的是在发送邮件时强制改变邮件的编码(encoding),因此只需要在OnConnection当中添加截获发送邮件的事件即可:

public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
    outlookApplication = application as Outlook.Application;
    addInInstance = addInInst as COMAddIn;
    if (outlookApplication == null || addInInstance == null)
    { return; }
    outlookApplication.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(outlookApplication_ItemSend);
    if (connectMode != Extensibility.ext_ConnectMode.ext_cm_Startup)
    {
        OnStartupComplete(ref custom);
    }
}

之后,我们就可以在事件函数中控制邮件的编码了:

void outlookApplication_ItemSend(object Item, ref bool Cancel)
{
    Outlook.MailItem mitem = (Outlook.MailItem)Item;
    mitem.InternetCodepage = 65001; //Code page for UTF-8
}

在完成上面的代码之后,我们只需要打开Outlook,就可以测试这个插件的效果了。要确认该插件是否安装,可以在Outlook的菜单上选择Tools > Options > Other,然后按Advances Options按钮,COM Add-ins即可看到插件的列表,以及启用和禁用插件。