April 2007

Write Outlook add-ins with C#

针对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即可看到插件的列表,以及启用和禁用插件。

DDC and EDID

DDC和EDID是显示器硬件方面的技术标准,最早的标准诞生于1994年。该标准简单地说就是为了让计算机知道显示器的各种规格信息(如支持的分辨率模式,支持的刷新频率,支持的行场频范围,产品型号,生产厂商等等)的一个通信标准。这样,当显示器接到计算机上时,几乎不用做任何调整,系统(Windows)就可以自动为用户设置好显示器,达到了“即插即用”(Plug and Play)。

DDC是Display Data Channel的缩写,是display adapter(显示卡)和display device(显示器)之间的通信标准。所有的信息都将从显示器当中的ROM传送到显示卡。这个信息就叫做EDID(Extended Display Identification Data)。

当然,EDID里提供的信息毕竟是显示设备(显示器)所支持的各种规格,如果显示卡不支持,终于还是不会被系统支持。此外,由于驱动程序决定了显示卡的行为,因此有时候需要升级驱动程序以支持某一种特定的显示规格。

在Windows当中,Windows就是利用显示卡的DDC功能来获取所支持的显示模式、分辨率列表的。

RDL Licensing

关于Windows的License形态,除了市面上盒包装出售的“零售版”(Retail)、随着新电脑捆绑出售的OEM License,还有Volume License等等。不过RDL License倒是很少听说,这里就简单介绍一下。

首先,RDL是“Remote Desktop Licensing”或“Remote Desktop License”的缩写,是用在WIndows XP上的一种附加License形态。基本上一般的用户不会有机会和这种License Mode打交道,因为RDL一般用在Blade PC或者是VDI之类的Thin Client系统当中。

用一句话来概括RDL的用途就是,当用户使用某种终端设备,通过Remote Desktop远程使用Windows XP PC的时候,不仅Windows XP PC本身需要License,用来远程访问的终端(或者用户)也需要一个License——这个License可以是一个普通的Windows XP License,也可以是一个Widows XP RDL。由于RDL比一般的Windows XP License要便宜得多,自然在那些非Windows XP的终端上(例如PDA,Thin Client Terminal)选择RDL是一个廉价的选择。

Remote Desktop License

RDL还具体可分为Device RDL和User RDL。顾名思义,Device RDL就是每个Thin Client终端,无论有多少用户使用它,都要有一个RDL;而User RDL则是每个用户,无论它使用多少Thin Client终端,都需要一个RDL。这和微软一贯的License方针是很相似的,熟悉MS SQL Server的CAL的朋友肯定立刻就会联想起来。

如果您是Blade PC系统或者虚拟化系统的业内人士,可以参考微软的技术文章:

  • Microsoft Desktop Operating Systems - Licensing in blade PC Environments
  • Microsoft Windows Desktop Operating Systems for Software Virtual Machine Use

上述两篇文章均在Microsoft Product Licensing Web- RDL有直接链接可以下载。

HBA and WWN

首先介绍一下什么是HBA。

这里所说的HBA,全称FC HBA,也就是Fibre Channel Host Bus Adapter。在FC网络中,主机(如服务器)需要和FC网络、FC存储设备(如SAN)连接时,需要使用一种接口卡,就如同连接以太网需要以太网卡一样。这种接口卡就叫做FC HBA,简称HBA

和以太网卡的MAC地址一样,HBA上也有独一无二的标识,这就是WWN(World Wide Name)。HBA上的WWN有两种:

  • Node WWN(WWNN):每个HBA有其独有的Node WWN
  • Port WWN(WWPN):HBA卡上每个port有其独一无二的Port WWN。由于通讯是通过port进行的,多数情况下需要使用WWPN而不是WWNN。

WWN的长度为8bytes,用16进制表示并用冒号分隔。例如:50:06:04:81:D6:F3:45:42