分享错误,学习生活

Outlook 插件开发小结

  最近实习在做outlook插件开发,阅读了一些VSTO的相关概念和知识。遂将整理所得与大家分享和交流。PS:这篇博客为本人的第一篇正式技术博客,如有错误和不妥之处请读者见谅。

  

 I.基本介绍

  1.VSTO外接程序体系结构

             

 

  2.Outlook add-in注册表项

    1.Microsoft Office 2010 应用程序可加载在 HKEY_LOCAL_MACHINE 或 HKEY_CURRENT_USER 下注册的外接程序。默认情况下,2007 Microsoft Office system 中的应用程序只能加载在 HKEY_CURRENT_USER 下注册的外接程序。

    2.外接程序注册表项位于所有应用程序(Visio 除外,它的 根 为 HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE)的以下注册表项之下:Root\Software\Microsoft\Office\应用程序名称\Addins\外接程序ID(外接程序ID一般为项目名)

    3.Outlook 窗体区域的注册表项:Root\Software\Microsoft\Office\Outlook\FormRegions\消息类

 

  3.安装部署

    1. ClickOnce 部署:仅当前用户可以注册外接程序。这是因为 ClickOnce 只支持在 HKEY_CURRENT_USER 下创建密钥。

    2. Windows Installer部署:无限制

 

  4.定制项

   UI方面

   1.自定义UI

   2.窗体区域/ribbon:与outlook界面相整合

   逻辑方面

   1.与outlook PIA或其他office PIA进行交互

   2.自定义属性:用于用户自定义数据和扩展功能

 

  5.outlook对象模型

   Application 对象

   Application 对象表示 Outlook 应用程序,它是 Outlook 对象模型中最高级的对象。

 

   Explorer 对象

   Explorer 对象表示显示包含项(如电子邮件、任务或约会)的文件夹内容的窗口。Explorer 对象包括可用来修改窗口的方法和属性,以及窗口更改时所引发的事件。

 

   Inspector 对象

   Inspector 对象表示显示单个项(如电子邮件、任务或约会)的窗口。Inspector 对象包括可用来修改窗口的方法和属性,以及窗口更改时所引发的事件。

 

   MAPIFolder 对象

   MAPIFolder 对象表示包含电子邮件、联系人、任务及其他项的文件夹。Outlook 提供 16 个默认 MAPIFolder 对象。

 

   MailItem、AppointmentItem、TaskItem、ContactItem分别对应邮件项、约会项、任务项、联系人项

 

  6.卸载

    1.手动删除(控制面板\程序\卸载程序)

    2.删除注册表(其实并未完整卸载,只是outlook不能检测到。控制面板\程序中仍有该插件信息)

    3.禁用插件(推荐:使outlook将插件禁用,但保留在加载项栏中。想重用时可以手动启用。)

    后两项都能用代码完成。

  

 II.实例讲解

  以下demo在VS 2010下完成。

 

  1.VS 为office拓展程序开发提供了很好的开发项。创建OutlookAddIn:File->New->Project->Visual C#->Office->2010(2007)->Outlook 2010 Add-in.

   

  2.命名好工程名确认后,系统会自动生成ThisAddIn类以及一些相应的事件。而ThisAddIn类就是整个拓展程序的一个抽象表示。

  

  3.现在,我们可以添加自己的定制项。这里我首先添加一个窗体区域(FormRegion),窗体区域的特点是可以将该区域嵌入到outlook的工作区。右击工程名->Add->New Item->Outlook Form Region.点击确认后会进入FormRegion的导航设置  框。分别有一下几项:

    一.创建方式:创建新窗体区域/从已有窗体区域导入(这里选择前者)

    二.窗体样式:按窗体位置和区域分有四种样式(这里选择第二项:adjoining加在工作窗体的底部)

    三.添加描述和呈现喜好(这里选择默认)

    四.选择呈现该窗体区域的消息类。由于我们是要做一个简单的联系人的拓展程序(下面会介绍),所以我们呈现该窗体区域的消息类选择Contact。

  设置完成点击Finish窗体区域就会成功添加。这时会出现一个自定义控件,我们可以设置里面的控件和样式。

  

  现在介绍一下我们的拓展程序。这里我们是做一个跟联系人有关的拓展程序:为联系人添加附加属性。我们知道联系人默认的属性是有限的,为了方便用户自定义属性,Outlook为我们设置了UserProperties方便用户设置各种自定义属性。这里我们就要  用它设置我们的定制项。这里我们帮联系人添加父母及其生日的附加属性。为简单起见我们需要两个textBox和两个DataTimePicker。布局完成如下:

  

  4.接下来,我们需要处理拓展程序的逻辑部分。主要思路是定义自定义属性、绑定属性、属性变更处理。主要代码及注释如下:

View Code

  1         // 自定义的属性名字
  2         private const string PROPERTY_NAME_MOTHER_NAME = "PROPERTY NAME MOTHER NAME";
  3         private const string PROPERTY_NAME_MOTHER_BIRTHDAY = "PROPERTY NAME MOTHER BIRTHDAY";
  4         private const string PROPERTY_NAME_FATHER_NAME = "PROPERTY NAME FATHER NAME";
  5         private const string PROPERTY_NAME_FATHER_BIRTHDAY = "PROPERTY NAME FATHER BIRTHDAY";
  6 
  7         // 自定义属性对象
  8         private Outlook.UserProperty _MotherNameProperty = null;
  9         private Outlook.UserProperty _MotherBirthdayProperty = null;
 10         private Outlook.UserProperty _FatherNameProperty = null;
 11         private Outlook.UserProperty _FatherBirthdayProperty = null;
 12 
 13         // 对应的Contact对象
 14         public Outlook.ContactItem _Contact = null;
 15 
 16         // 标记是否内容修改
 17         private bool _Changed = false;
 18 
 19         // Occurs before the form region is displayed.
 20         // Use this.OutlookItem to get a reference to the current Outlook item.
 21         // Use this.OutlookFormRegion to get a reference to the form region.
 22         private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
 23         {
 24             // 获得FormRegion所对应的Contact对象
 25             _Contact = ((Microsoft.Office.Tools.Outlook.FormRegionControl)sender).OutlookItem as Outlook.ContactItem;
 26             //_Contact = Globals.ThisAddIn.Application.ActiveInspector().CurrentItem;
 27 
 28             // 从联系人的自定义属性中,获得母亲姓名属性
 29             _MotherNameProperty = _Contact.UserProperties.Find(PROPERTY_NAME_MOTHER_NAME, Type.Missing);
 30             if (_MotherNameProperty != null)
 31             {
 32                 // 如果存在这个属性,则取出Value为控件赋值
 33                 tbMotherName.Text = _MotherNameProperty.Value as String;
 34             }
 35             else
 36             {
 37                 // 不存在则创建这个属性
 38                 _MotherNameProperty = _Contact.UserProperties.Add(PROPERTY_NAME_MOTHER_NAME, Outlook.OlUserPropertyType.olText, Type.Missing, Type.Missing);
 39             }
 40 
 41             // 母亲生日,原理相同
 42             _MotherBirthdayProperty = _Contact.UserProperties.Find(PROPERTY_NAME_MOTHER_BIRTHDAY, Type.Missing);
 43             if (_MotherBirthdayProperty != null)
 44             {
 45                 dtpMotherBirthday.Value = (DateTime)_MotherBirthdayProperty.Value;
 46             }
 47             else
 48             {
 49                 _MotherBirthdayProperty = _Contact.UserProperties.Add(PROPERTY_NAME_MOTHER_BIRTHDAY, Outlook.OlUserPropertyType.olDateTime, Type.Missing, Type.Missing);
 50             }
 51 
 52             // 父亲姓名
 53             _FatherNameProperty = _Contact.UserProperties.Find(PROPERTY_NAME_FATHER_NAME, Type.Missing);
 54             if (_FatherNameProperty != null)
 55             {
 56                 tbFatherName.Text = _FatherNameProperty.Value as String;
 57             }
 58             else
 59             {
 60                 _FatherNameProperty = _Contact.UserProperties.Add(PROPERTY_NAME_FATHER_NAME, Outlook.OlUserPropertyType.olText, Type.Missing, Type.Missing);
 61             }
 62 
 63             // 父亲生日
 64             _FatherBirthdayProperty = _Contact.UserProperties.Find(PROPERTY_NAME_FATHER_BIRTHDAY, Type.Missing);
 65             if (_FatherBirthdayProperty != null)
 66             {
 67                 dtpFatherBirthday.Value = (DateTime)_FatherBirthdayProperty.Value;
 68             }
 69             else
 70             {
 71                 _FatherBirthdayProperty = _Contact.UserProperties.Add(PROPERTY_NAME_FATHER_BIRTHDAY, Outlook.OlUserPropertyType.olDateTime, Type.Missing, Type.Missing);
 72             }
 73 
 74             // 将这四个控件绑定change事件,只有在修改之后,我们才会将值回写到Contact对应的属性中去
 75             tbMotherName.TextChanged += new EventHandler(content_Changed);
 76             dtpMotherBirthday.ValueChanged += new EventHandler(content_Changed);
 77             tbFatherName.TextChanged += new EventHandler(content_Changed);
 78             dtpFatherBirthday.ValueChanged += new EventHandler(content_Changed);
 79 
 80             // 在Write事件中,把修改的值保存到属性中去
 81             _Contact.Write += new Microsoft.Office.Interop.Outlook.ItemEvents_10_WriteEventHandler(contact_Write);
 82         }
 83 
 84         void content_Changed(object sender, EventArgs e)
 85         {
 86             // 有修改时,将_Change置为true
 87             _Changed = true;
 88         }
 89 
 90         void contact_Write(ref bool Cancel)
 91         {
 92             if (_Changed)
 93             {
 94                 // 保存值到属性中去
 95                 _MotherNameProperty.Value = tbMotherName.Text.Trim();
 96                 _MotherBirthdayProperty.Value = dtpMotherBirthday.Value;
 97                 _FatherNameProperty.Value = tbFatherName.Text.Trim();
 98                 _FatherBirthdayProperty.Value = dtpFatherBirthday.Value;
 99             }
100 
101         }
102 
103         // Occurs when the form region is closed.
104         // Use this.OutlookItem to get a reference to the current Outlook item.
105         // Use this.OutlookFormRegion to get a reference to the form region.
106         private void FormRegion1_FormRegionClosed(object sender, System.EventArgs e)
107         {
108             // 关闭事件绑定
109             _Contact.Write -= new Microsoft.Office.Interop.Outlook.ItemEvents_10_WriteEventHandler(contact_Write);
110 
111             // 释放对象
112             System.Runtime.InteropServices.Marshal.ReleaseComObject(_Contact);
113             _Contact = null;
114         }

  

  现在,拓展程序已经可以调试了。运行后outlook程序会自动启动并加载此拓展项。打开联系人信息工作区会看到刚才的窗体区域被成功地加了进去。

  

  

  好了。我们联系人的自定义属性已经做好了。为了实例的完整性,现在我们再加一个ribbon控件,使其附加在本地outlook的ribbon菜单项内。其功能为:一.显示联系人信息(主要是我们刚才添加的附加属性信息);二.拓展程序卸载/禁用。

  5添加ribbon:右击工程名->Add->New Item->Ribbon (Visual Designer).点击确认后会看到一个ribbon控件被添加了进来。值得注意的是RibbonType属性,它决定着ribbon的显示位置默认为Mail.Read。由于我们要将该ribbon显示在主窗体的  ribbon项集中,因此其RibbonType应勾选Explore(同理如何想让该ribbon显示在联系人窗体的ribbon项中可勾选Contact)。现在我们要为其添加两个RibbonButton:分别为Show Contacts Info和Unistall。

  

  6.针对Show Contacts Info为了显示联系人信息,我们需要添加一个窗体来进行显示。于是我们可以添加一个UserControl名为ContactsInfoDisplay,其形式如下:

  

  为了显示联系人信息,我们可以传入一个ContactItem作为其构造函数的参数。然后获取其对象中的相关属性。代码如下: 

View Code

 1 public ContactsInfoDisplay(Microsoft.Office.Interop.Outlook.ContactItem contact)
 2         {
 3             InitializeComponent();
 4             this.name.Text = contact.LastName+" "+contact.FirstName;
 5             this.phone.Text = contact.MobileTelephoneNumber;
 6             this.email.Text = contact.Email1Address;
 7             //获取自定义属性
 8          this.father.Text = string.Format("{0}({1})", contact.UserProperties.Find("PROPERTY NAME FATHER NAME").Value, contact.UserProperties.Find("PROPERTY NAME FATHER BIRTHDAY").Value);
 9             this.mother.Text = string.Format("{0}({1})", contact.UserProperties.Find("PROPERTY NAME MOTHER NAME").Value, contact.UserProperties.Find("PROPERTY NAME MOTHER BIRTHDAY").Value);
10         }

  7.针对Unistall,我们有两种策略(第一部分提到)。

         一:删除注册表:第一部分提到过outlook加载插件时先检查注册表里的特定目录下的项。如果删除该项,outlook就不会检测到从而也就不会加载。代码如下:

View Code

 1 RegistryKey regKey = null;
 2             RegistryKey regSubKey = null;
 3             try
 4             {
 5                 //Read the key 
 6                 regKey = Microsoft.Win32.Registry.CurrentUser;
 7                 //获取outlook插件目录下的子键
 8                 regSubKey = regKey.OpenSubKey(string.Format(@"Software\Microsoft\Office\Outlook\Addins"), true);
 9                 //获取当前插件名
10                 regSubKey.DeleteSubKey(Assembly.GetExecutingAssembly().GetName().Name);
11                 System.Windows.Forms.MessageBox.Show("插件卸载成功,下次启动生效!");
12             }
13             catch (Exception e)
14             {
15                 System.Windows.Forms.MessageBox.Show(e.Message);
16             }

   二:禁用插件:由于第一种方法只是处理注册表,因此卸载的不是很彻底。还有很多遗留项。因此推荐使用第二种方法。使outlook将插件禁用,但保留在加载项栏中。想重用时可以手动启用。其相应的代码也很简单。

View Code

1             Microsoft.Office.Core.COMAddIn addin= Globals.ThisAddIn.Application.COMAddIns.Item("OutlookAddInDemo");
2             addin.Connect = false; 

  

  到现在我们的实例已经完整的介绍完了,运行结果如图。完整工程源代码下载:http://files.cnblogs.com/maxliu/OutlookAddInDemo.rar

 

 

 

暂无评论

发表评论

电子邮件地址不会被公开。