Visual Studio允許創(chuàng)建Office類型的工程,用于給Office產(chǎn)品創(chuàng)建外接程序以及自定義模板等。這是一個(gè)非常實(shí)用的功能,在早期版本的Office中我們只能通過VBA代碼來實(shí)現(xiàn)一些自定義的功能,如文本的自動(dòng)替換、宏錄制功能等等。VBA的功能很有限,有些時(shí)候我們希望自定義程序能夠完成更多的功能,比如在Office多個(gè)不同產(chǎn)品之間進(jìn)行文檔轉(zhuǎn)換、調(diào)用系統(tǒng)API、遠(yuǎn)程過程調(diào)用及Web Service訪問等。下面是在Visual Studio 2010中創(chuàng)建Office類型工程的對(duì)話框。
本例中我將向大家介紹如何通過Office外接程序?qū)utlook中的郵件導(dǎo)出到本地Word文檔中。當(dāng)然,你完全可以手動(dòng)將Outlook中的郵件通過復(fù)制粘貼的方式拷貝到Word文檔中,但是要想同時(shí)將大批的郵件導(dǎo)出到Word中恐怕就需要借助于程序來完成了。來看看我們?nèi)绾螌?shí)現(xiàn)這個(gè)小插件!
首先在Visual Studio中創(chuàng)建Outlook 2010 Add-in類型的工程,取名為OutlookToWord。這里需要申明一下我開發(fā)和測(cè)試的環(huán)境:Visual Studio 2010 + Office 2010。當(dāng)然,在Visual Studio 2008和稍低版本的Office中同樣也可以實(shí)現(xiàn),只是工程類型和外接程序所支持的載體版本稍有區(qū)別。
工程創(chuàng)建成功后Visual Studio會(huì)自動(dòng)為你生成一些文件和代碼,我們只需要在代碼中實(shí)現(xiàn)自定義的功能即可。我們希望程序通過一個(gè)按鈕來實(shí)現(xiàn)郵件的導(dǎo)出,因此需要在Outlook的工具欄中創(chuàng)建一個(gè)自定義按鈕。這個(gè)很簡(jiǎn)單,直接查MSDN,這里有非常詳細(xì)的介紹,將代碼拷到工程中,并做適當(dāng)?shù)男薷摹?br />
http://msdn.microsoft.com/zh-CN/library/scff9c7c.aspx
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Xml.Linq;
6 using Outlook = Microsoft.Office.Interop.Outlook;
7 using Office = Microsoft.Office.Core;
8
9 namespace OutlookToWord
10 {
11 public partial class ThisAddIn
12 {
13 private Office.CommandBar newToolBar;
14 private Office.CommandBarButton exportButton;
15 private Outlook.Explorers selectExplorers;
16
17 private void ThisAddIn_Startup(object sender, System.EventArgs e)
18 {
19 selectExplorers = this.Application.Explorers;
20 selectExplorers.NewExplorer += new Outlook.ExplorersEvents_NewExplorerEventHandler(newExplorer_Event);
21 AddToolbar();
22 }
23
24 private void newExplorer_Event(Outlook.Explorer new_Explorer)
25 {
26 ((Outlook._Explorer)new_Explorer).Activate();
27 newToolBar = null;
28 AddToolbar();
29 }
30
31 private void AddToolbar()
32 {
33 if (newToolBar == null)
34 {
35 Office.CommandBars cmdBars = this.Application.ActiveExplorer().CommandBars;
36 newToolBar = cmdBars.Add("NewToolBar", Office.MsoBarPosition.msoBarTop, false, true);
37 }
38 try
39 {
40 Office.CommandBarButton btExportToWord = (Office.CommandBarButton)newToolBar.Controls.Add(1, missing, missing, missing, missing);
41 btExportToWord.Style = Office.MsoButtonStyle.msoButtonCaption;
42 btExportToWord.Caption = "Export to Word";
43 btExportToWord.Tag = "Export current mail to word";
44 if (this.exportButton == null)
45 {
46 this.exportButton = btExportToWord;
47 exportButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(exportButton_Click);
48 }
49 newToolBar.Visible = true;
50 }
51 catch (Exception ex)
52 {
53 MessageBox.Show(ex.Message);
54 }
55 }
56
57 void exportButton_Click(Office.CommandBarButton ctrl, ref bool cancel)
58 {
59 }
60
61 private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
62 {
63 }
64
65 #region VSTO generated code
66
67 /// <summary>
68 /// Required method for Designer support - do not modify
69 /// the contents of this method with the code editor.
70 /// </summary>
71 private void InternalStartup()
72 {
73 this.Startup += new System.EventHandler(ThisAddIn_Startup);
74 this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
75 }
76
77 #endregion
78 }
79 }
我們?cè)贠utlook工具欄中創(chuàng)建了一個(gè)Export to Word按鈕。退出Outlook在Visual Studio中直接按F5運(yùn)行查看效果。
如果你的Outlook工具欄中沒有Add-ins菜單,請(qǐng)點(diǎn)擊File-Options-Customize Ribbon,在其中勾選Add-Ins來顯示它。
接下來的任務(wù)就是實(shí)現(xiàn)郵件的導(dǎo)出功能了。由于需要在程序中調(diào)用Word的部分功能,所以你需要在工程添加Word的相關(guān)引用,如下圖。注意你本地可能包含多個(gè)Microsoft.Office.Interop.Word程序集的版本,選擇最高版本的那個(gè)即可。
然后在代碼中加入Word程序集的命名空間using Word = Microsoft.Office.Interop.Word。我們希望在導(dǎo)出郵件之前提示用戶選擇要導(dǎo)出文件的位置,因此程序中還需要一個(gè)文件夾選擇對(duì)話框控件,在程序中給出定義,F(xiàn)在我們來看看導(dǎo)出功能的核心代碼。
1 void exportButton_Click(Office.CommandBarButton ctrl, ref bool cancel)
2 {
3 object sFileName;
4 string sPath = string.Empty;
5
6 // Get mail export path.
7 if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
8 {
9 sPath = folderBrowserDialog.SelectedPath;
10
11 // If the selected path is a folder, add '\' at the end of the path string.
12 if (sPath != Path.GetPathRoot(sPath))
13 {
14 sPath += "\\";
15 }
16 }
17 else
18 {
19 return;
20 }
21
22 Word.Application app = new Word.Application();
23 Word.Document doc = null;
24 object unknow = Type.Missing;
25 object format = Word.WdSaveFormat.wdFormatDocumentDefault;
26 Outlook.Explorer activeExplorer = this.Application.Explorers.Application.ActiveExplorer();
27
28 try
29 {
30 // Export all selected mail items to word.
31 foreach (object selectedItem in activeExplorer.Selection)
32 {
33 Outlook.MailItem mailItem = selectedItem as Outlook.MailItem;
34 if (mailItem != null)
35 {
36 sFileName = CreateFileName(sPath, mailItem.Subject);
37 //doc = app.Documents.Add(ref unknow, ref unknow, ref unknow, ref unknow);
38 //doc.Content.Text = mailItem.Body;
39 //doc.Paragraphs.Last.Range.Text = mailItem.Body;
40
41 Outlook.Inspector inspector = mailItem.GetInspector;
42 doc = (Word.Document)inspector.WordEditor;
43 //mailItem.SaveAs(sFileName.ToString(), Outlook.OlSaveAsType.olDoc);
44
45 doc.SaveAs(ref sFileName, ref format, ref unknow, ref unknow, ref unknow,
46 ref unknow, ref unknow, ref unknow, ref unknow, ref unknow, ref unknow,
47 ref unknow, ref unknow, ref unknow, ref unknow, ref unknow);
48 //doc.Close(ref unknow, ref unknow, ref unknow);
49 }
50 }
51 }
52 catch (Exception ex)
53 {
54 MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
55 }
56 finally
57 {
58 if (app != null)
59 {
60 app.Quit(ref unknow, ref unknow, ref unknow);
61 }
62 }
63 }
有幾個(gè)地方需要說明一下。首先我們要得到當(dāng)前已選擇的郵件列表,這個(gè)是通過this.Application.Explorers.Application.ActiveExplorer().Selection來得到的,ActiveExplorer()方法返回的是當(dāng)前選中的郵箱視圖,比如發(fā)件箱、草稿箱、收件箱等。Selection屬性得到是其中所有被選中的郵件的集合,在Outlook中你可以同時(shí)選中多封郵件。程序通過foreach便利所有當(dāng)前被選中的郵件,然后依次導(dǎo)出到Word文檔中。郵件主題被用作Word文檔的名稱,這里有一個(gè)CreateFileName方法用來處理并生成最終的文件名,稍后給出這個(gè)方法的代碼。對(duì)于如何保存Word文檔的內(nèi)容,這里有幾個(gè)地方需要注意:
1. mailItem對(duì)象可以通過Body,HTMLBody,RTFBody來得到郵件的內(nèi)容,但是其中的格式會(huì)有所區(qū)別。通過Body得到的是去掉所有樣式之后的純文本內(nèi)容,HTMLBody得到的是轉(zhuǎn)換之后的HTML格式的郵件內(nèi)容,RTFBody則是包含富文本格式的二進(jìn)制郵件內(nèi)容。
2. 我們可以通過doc.Content.Text或doc.Paragraphs.Last.Range.Text來給Word文檔填充內(nèi)容,兩者的效果是一樣的。但是這兩種方法只能接收字符串內(nèi)容,也就是說如果郵件中包含特定格式的信息都會(huì)被自動(dòng)過濾掉,如郵件中的表格、文字樣式、圖片、超鏈接等內(nèi)容。
3. 通過mailItem.GetInspector將郵件的編輯器轉(zhuǎn)換成Word文檔可以將Outlook郵件中的所有內(nèi)容無差異地保存到Word中。因?yàn)镺utlook郵件編輯器使用的正是Word文檔的編輯器,因此這個(gè)轉(zhuǎn)換是有效的。
4. Word文檔的創(chuàng)建、保存方法與其它在C#中操作Word相同。需要注意的是如果使用doc.Content.Text或doc.Paragraphs.Last.Range.Text方法填充Word文檔內(nèi)容,完畢之后一定要關(guān)閉Word文檔。另外就是程序結(jié)束后要關(guān)掉Word進(jìn)程,否則每次導(dǎo)出都會(huì)在內(nèi)存中創(chuàng)建一個(gè)Word進(jìn)程造成資源的浪費(fèi)。
5. Word文檔保存的格式選用wdFormatDocumentDefault,這個(gè)是Word文檔保存的默認(rèn)格式,如果選用其它格式,需要確保你所保存的內(nèi)容能被選用的格式支持。
下面來看一下CreateFileName方法。這個(gè)方法是用來處理生成的Word文檔的文件名的。程序中使用了郵件主題作為Word文件名,由于Windows文件名不支持部分特殊字符,因此我們需要在生成文件名的過程中將這些特殊字符過濾掉,同時(shí)為了確保所生成的Word不會(huì)被重復(fù)覆蓋,需要對(duì)文件名作差異化處理——即如果文件存在則在后面增量增加一個(gè)數(shù)字,如新建文件1、新建文件2等。另外就是如果文件名過長(zhǎng)只截取前100個(gè)字符。
1 private string CreateFileName(string sPath, string sFileName)
2 {
3 // Remove unsupport charts for file name.
4 string sRst = sFileName.Replace("\\", "");
5 sRst = sRst.Replace("/", "");
6 sRst = sRst.Replace(":", "");
7 sRst = sRst.Replace("*", "");
8 sRst = sRst.Replace("?", "");
9 sRst = sRst.Replace("\"\"", "");
10 sRst = sRst.Replace("<", "");
11 sRst = sRst.Replace(">", "");
12 sRst = sRst.Replace("|", "");
13
14 if (sRst.Length > 100)
15 {
16 sRst = sRst.Substring(0, 100);
17 }
18
19 // Make sure the file name is unique.
20 int i = 1;
21 if (File.Exists(string.Concat(sPath, sRst, ".docx")))
22 {
23 while (true)
24 {
25 if (File.Exists(string.Concat(sPath, sRst, i.ToString(), ".docx")))
26 {
27 i++;
28 }
29 else
30 {
31 sRst += i.ToString();
32 break;
33 }
34 }
35 }
36
37 // Return *.docx file name.
38 return string.Concat(sPath, sRst, ".docx");
39 }
下面給出完整的代碼:
OutlookToWord
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Xml.Linq;
6 using Outlook = Microsoft.Office.Interop.Outlook;
7 using Office = Microsoft.Office.Core;
8 using System.Windows.Forms;
9 using Word = Microsoft.Office.Interop.Word;
10 using System.IO;
11
12 namespace OutlookToWord
13 {
14 public partial class ThisAddIn
15 {
16 private Office.CommandBar newToolBar;
17 private Office.CommandBarButton exportButton;
18 private Outlook.Explorers selectExplorers;
19 private FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
20
21 private void ThisAddIn_Startup(object sender, System.EventArgs e)
22 {
23 selectExplorers = this.Application.Explorers;
24 selectExplorers.NewExplorer += new Outlook.ExplorersEvents_NewExplorerEventHandler(newExplorer_Event);
25 AddToolbar();
26 }
27
28 private void newExplorer_Event(Outlook.Explorer new_Explorer)
29 {
30 ((Outlook._Explorer)new_Explorer).Activate();
31 newToolBar = null;
32 AddToolbar();
33 }
34
35 private void AddToolbar()
36 {
37 if (newToolBar == null)
38 {
39 Office.CommandBars cmdBars = this.Application.ActiveExplorer().CommandBars;
40 newToolBar = cmdBars.Add("NewToolBar", Office.MsoBarPosition.msoBarTop, false, true);
41 }
42 try
43 {
44 Office.CommandBarButton btExportToWord = (Office.CommandBarButton)newToolBar.Controls.Add(1, missing, missing, missing, missing);
45 btExportToWord.Style = Office.MsoButtonStyle.msoButtonCaption;
46 btExportToWord.Caption = "Export to Word";
47 btExportToWord.Tag = "Export current mail to word";
48 if (this.exportButton == null)
49 {
50 this.exportButton = btExportToWord;
51 exportButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(exportButton_Click);
52 }
53 newToolBar.Visible = true;
54 }
55 catch (Exception ex)
56 {
57 MessageBox.Show(ex.Message);
58 }
59 }
60
61 /// <summary>
62 /// Create export file name from a string.
63 /// </summary>
64 /// <param name="sPath"></param>
65 /// <param name="sFileName"></param>
66 /// <returns></returns>
67 private string CreateFileName(string sPath, string sFileName)
68 {
69 // Remove unsupport charts for file name.
70 string sRst = sFileName.Replace("\\", "");
71 sRst = sRst.Replace("/", "");
72 sRst = sRst.Replace(":", "");
73 sRst = sRst.Replace("*", "");
74 sRst = sRst.Replace("?", "");
75 sRst = sRst.Replace("\"\"", "");
76 sRst = sRst.Replace("<", "");
77 sRst = sRst.Replace(">", "");
78 sRst = sRst.Replace("|", "");
79
80 if (sRst.Length > 100)
81 {
82 sRst = sRst.Substring(0, 100);
83 }
84
85 // Make sure the file name is unique.
86 int i = 1;
87 if (File.Exists(string.Concat(sPath, sRst, ".docx")))
88 {
89 while (true)
90 {
91 if (File.Exists(string.Concat(sPath, sRst, i.ToString(), ".docx")))
92 {
93 i++;
94 }
95 else
96 {
97 sRst += i.ToString();
98 break;
99 }
100 }
101 }
102
103 // Return *.docx file name.
104 return string.Concat(sPath, sRst, ".docx");
105 }
106
107 void exportButton_Click(Office.CommandBarButton ctrl, ref bool cancel)
108 {
109 object sFileName;
110 string sPath = string.Empty;
111
112 // Get mail export path.
113 if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
114 {
115 sPath = folderBrowserDialog.SelectedPath;
116
117 // If the selected path is a folder, add '\' at the end of the path string.
118 if (sPath != Path.GetPathRoot(sPath))
119 {
120 sPath += "\\";
121 }
122 }
123 else
124 {
125 return;
126 }
127
128 Word.Application app = new Word.Application();
129 Word.Document doc = null;
130 object unknow = Type.Missing;
131 object format = Word.WdSaveFormat.wdFormatDocumentDefault;
132 Outlook.Explorer activeExplorer = this.Application.Explorers.Application.ActiveExplorer();
133
134 try
135 {
136 // Export all selected mail items to word.
137 foreach (object selectedItem in activeExplorer.Selection)
138 {
139 Outlook.MailItem mailItem = selectedItem as Outlook.MailItem;
140 if (mailItem != null)
141 {
142 sFileName = CreateFileName(sPath, mailItem.Subject);
143 //doc = app.Documents.Add(ref unknow, ref unknow, ref unknow, ref unknow);
144 //doc.Content.Text = mailItem.Body;
145 //doc.Paragraphs.Last.Range.Text = mailItem.Body;
146
147 Outlook.Inspector inspector = mailItem.GetInspector;
148 doc = (Word.Document)inspector.WordEditor;
149 //mailItem.SaveAs(sFileName.ToString(), Outlook.OlSaveAsType.olDoc);
150
151 doc.SaveAs(ref sFileName, ref format, ref unknow, ref unknow, ref unknow,
152 ref unknow, ref unknow, ref unknow, ref unknow, ref unknow, ref unknow,
153 ref unknow, ref unknow, ref unknow, ref unknow, ref unknow);
154 //doc.Close(ref unknow, ref unknow, ref unknow);
155 }
156 }
157 }
158 catch (Exception ex)
159 {
160 MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
161 }
162 finally
163 {
164 if (app != null)
165 {
166 app.Quit(ref unknow, ref unknow, ref unknow);
167 }
168 }
169 }
170
171 private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
172 {
173 }
174
175 #region VSTO generated code
176
177 /// <summary>
178 /// Required method for Designer support - do not modify
179 /// the contents of this method with the code editor.
180 /// </summary>
181 private void InternalStartup()
182 {
183 this.Startup += new System.EventHandler(ThisAddIn_Startup);
184 this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
185 }
186
187 #endregion
188 }
189 }