最近實習在做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。

