VSTO將Outlook批量郵件導出Word

           VS2010將Outlook郵件導出成Word文檔格式

            週末加班,整理一下outlook中的郵件,其中有個幾個郵件目錄中的文件都是重要的資料,想要導出成word,於是按照我的思維慣性,首先手工實現了一次,大概工序如下:

1、首先打開郵件,ctrl+A全選郵件;

2、然後在備份目錄下新建一個word文檔,黏貼進去;

3、重命名word文件;

         手工操作了幾封郵件之後,發現人肉是有點累,看到數以萬封的郵件等着導出,頓感絕望,於是再次按照我的思惟慣性,需要事先一個自動化的程序來實現。

        如何來實現呢,這個功能是直接在office outlook來觸發操作,一定要做一個插件,於是想到了vs2010中的VSTO,應該沒問題,接下來,就是百度+google,網上搜索了一下,看看有沒有現成的案例,結果還真是出乎意料,MSDN還真有:http://msdn.microsoft.com/zh-CN/library/scff9c7c.aspx。

      另外,還搜索到了一個有價值的,按照我的要求,基本實現了50%的功能了,http://www.cr173.com/html/10714_all.html"VS2010將Outlook郵件導出成Word文檔格式".再次按照思維慣性,邊抄、邊評、邊完善。

      Visual Studio允許創建Office類型的工程,用於給Office產品創建外接程序以及自定義模板等。這是一個非常實用的功能,在早期版本的Office中我們只能通過VBA代碼來實現一些自定義的功能,如文本的自動替換、宏錄製功能等等。VBA的功能很有限,有些時候我們希望自定義程序能夠完成更多的功能,比如在Office多個不同產品之間進行文檔轉換、調用系統API、遠程過程調用及Web Service訪問等。下面是在Visual Studio 2010中創建Office類型工程的對話框。 


  本例中我將向大家介紹如何通過Office外接程序將Outlook中的郵件導出到本地Word文檔中。當然,你完全可以手動將Outlook中的郵件通過複製粘貼的方式拷貝到Word文檔中,但是要想同時將大批的郵件導出到Word中恐怕就需要藉助於程序來完成了。來看看我們如何實現這個小插件!

  首先在Visual Studio中創建Outlook 2010 Add-in類型的工程,取名爲OutlookToWord。這裏需要申明一下我開發和測試的環境:Visual Studio 2010 + Office 2010。當然,在Visual Studio 2008和稍低版本的Office中同樣也可以實現,只是工程類型和外接程序所支持的載體版本稍有區別。

  工程創建成功後Visual Studio會自動爲你生成一些文件和代碼,我們只需要在代碼中實現自定義的功能即可。我們希望程序通過一個按鈕來實現郵件的導出,因此需要在Outlook的工具欄中創建一個自定義按鈕。這個很簡單,直接查MSDN,這裏有非常詳細的介紹,將代碼拷到工程中,並做適當的修改。

完整的代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Windows.Forms;
using Word = Microsoft.Office.Interop.Word;
using System.IO;

namespace OutlookToWord
{
    public partial class ThisAddIn
    {
        private Office.CommandBar newToolBar;
        private Office.CommandBarButton exportButton;
        private Outlook.Explorers selectExplorers;
        private FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog(); 

        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            selectExplorers = this.Application.Explorers;
            selectExplorers.NewExplorer += new Outlook
                .ExplorersEvents_NewExplorerEventHandler(newExplorer_Event);
            AddToolbar();
        }
        private void newExplorer_Event(Outlook.Explorer new_Explorer)
        {
            ((Outlook._Explorer)new_Explorer).Activate();
            newToolBar = null;
            AddToolbar();
        }
        private void AddToolbar()
        {
            if (newToolBar == null)
            {
                Office.CommandBars cmdBars = this.Application.ActiveExplorer().CommandBars;
                newToolBar = cmdBars.Add("NewToolBar", Office.MsoBarPosition.msoBarTop, false, true);
            }
            try
            {
                Office.CommandBarButton btExportToWord = (Office.CommandBarButton)newToolBar.Controls.Add(1, missing, missing, missing, missing);
                //btExportToWord.Style = Office.MsoButtonStyle.msoButtonCaption;
                btExportToWord.Style = Office.MsoButtonStyle.msoButtonIconAndCaption;
                btExportToWord.Caption = "導出word";
                btExportToWord.Tag = "Export current mail to word";
                
                btExportToWord.Picture = getImage();
                if (this.exportButton == null)
                {
                    this.exportButton = btExportToWord;
                    exportButton.Click += new Office._CommandBarButtonEvents_ClickEventHandler(exportButton_Click);
                }
                newToolBar.Visible = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        /// <summary>
        /// Create export file name from a string.
        /// </summary>
        /// <param name="sPath"></param>
        /// <param name="sFileName"></param>
        /// <returns></returns>
        private string CreateFileName(string sPath, string sFileName)
        {
            // Remove unsupport charts for file name.
            string sRst = sFileName.Replace("\\", "");
            sRst = sRst.Replace("/", "");
            sRst = sRst.Replace(":", "");
            sRst = sRst.Replace("*", "");
            sRst = sRst.Replace("?", "");
            sRst = sRst.Replace("\"\"", "");
            sRst = sRst.Replace("<", "");
            sRst = sRst.Replace(">", "");
            sRst = sRst.Replace("|", "");
            if (sRst.Length > 100)
            {
                sRst = sRst.Substring(0, 100);
            }
            // Make sure the file name is unique.
            int i = 1;
            if (File.Exists(string.Concat(sPath, sRst, ".docx")))
            {
                while (true)
                {
                    if (File.Exists(string.Concat(sPath, sRst, i.ToString(), ".docx")))
                    {
                        i++;
                    }
                    else
                    {
                        sRst += i.ToString();
                        break;
                    }
                }
            }
             // Return *.docx file name.
            return string.Concat(sPath, sRst, ".docx");
        }
        private void exportButton_Click(Office.CommandBarButton ctrl, ref bool cancel)
        {
            //MessageBox.Show("You clicked: " + ctrl.Caption);
            object sFileName;
            string sPath = string.Empty;
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                sPath = folderBrowserDialog.SelectedPath;
                if (sPath != Path.GetPathRoot(sPath))
                {
                    sPath += "\\";
                }
            }
            else
            {
                return;
            }
            Word.Application app = new Word.Application();
            Word.Document doc = null;
            object unknow = Type.Missing;
            object format = Word.WdSaveFormat.wdFormatDocumentDefault;
            Outlook.Explorer activeExplorer = this.Application.Explorers.Application.ActiveExplorer();
            try
            {            
                // Export all selected mail items to word.
                 foreach (object selectedItem in activeExplorer.Selection)
                 {
                     Outlook.MailItem mailItem = selectedItem as Outlook.MailItem;
                     if (mailItem != null)
                     {
                         sFileName = CreateFileName(sPath, mailItem.Subject);
                         Outlook.Inspector inspector = mailItem.GetInspector;
                         doc = (Word.Document)inspector.WordEditor;
                         doc.SaveAs(ref sFileName, ref format, ref unknow, ref unknow, ref unknow,
            ref unknow, ref unknow, ref unknow, ref unknow, ref unknow, ref unknow,
            ref unknow, ref unknow, ref unknow, ref unknow, ref unknow);
                         //附件處理方案有兩個;
                         //1、直接另存附件(比較原始,當前方案);
                         //2、附件插入郵件word正文之後(優化方案,未實現);
                         if (mailItem.Attachments.Count > 0)
                         {                     
                             for (int i = 1; i <= mailItem.Attachments.Count; i++)
                             {
                                 mailItem.Attachments[i].SaveAsFile
                                     (sPath + //@"C:\TestFileSave\" +
                                     mailItem.Attachments[i].FileName);                             
                                 
                             }
                         }                            
                         //doc.Close(ref unknow, ref unknow, ref unknow);
                     }
                 }
            }catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }finally
            {
                if (app != null)
                {
                    app.Quit(ref unknow, ref unknow, ref unknow);
                }
            }
    
    }


        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }


        private stdole.IPictureDisp getImage()
        {
            stdole.IPictureDisp tempImage = null;
            try
            {
                System.Drawing.Icon newIcon =
                    Properties.Resources.bigicon_48;


                ImageList newImageList = new ImageList();
                newImageList.Images.Add(newIcon);
                tempImage = ConvertImage.Convert(newImageList.Images[0]);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            return tempImage;
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }
        
        #endregion
    }

    sealed public class ConvertImage : System.Windows.Forms.AxHost
    {
        private ConvertImage()
            : base(null)
        {
        }
        public static stdole.IPictureDisp Convert
            (System.Drawing.Image image)
        {
            return (stdole.IPictureDisp)System.
                Windows.Forms.AxHost
                .GetIPictureDispFromPicture(image);
        }
    }
}

運行效果如下:



  有幾個地方需要說明一下。首先我們要得到當前已選擇的郵件列表,這個是通過this.Application.Explorers.Application.ActiveExplorer().Selection來得到的,ActiveExplorer()方法返回的是當前選中的郵箱視圖,比如發件箱、草稿箱、收件箱等。Selection屬性得到是其中所有被選中的郵件的集合,在Outlook中你可以同時選中多封郵件。程序通過foreach便利所有當前被選中的郵件,然後依次導出到Word文檔中。郵件主題被用作Word文檔的名稱,這裏有一個CreateFileName方法用來處理並生成最終的文件名,稍後給出這個方法的代碼。對於如何保存Word文檔的內容,這裏有幾個地方需要注意:

  1. mailItem對象可以通過Body,HTMLBody,RTFBody來得到郵件的內容,但是其中的格式會有所區別。通過Body得到的是去掉所有樣式之後的純文本內容,HTMLBody得到的是轉換之後的HTML格式的郵件內容,RTFBody則是包含富文本格式的二進制郵件內容。

  2. 我們可以通過doc.Content.Text或doc.Paragraphs.Last.Range.Text來給Word文檔填充內容,兩者的效果是一樣的。但是這兩種方法只能接收字符串內容,也就是說如果郵件中包含特定格式的信息都會被自動過濾掉,如郵件中的表格、文字樣式、圖片、超鏈接等內容。

  3. 通過mailItem.GetInspector將郵件的編輯器轉換成Word文檔可以將Outlook郵件中的所有內容無差異地保存到Word中。因爲Outlook郵件編輯器使用的正是Word文檔的編輯器,因此這個轉換是有效的。

  4. Word文檔的創建、保存方法與其它在C#中操作Word相同。需要注意的是如果使用doc.Content.Text或doc.Paragraphs.Last.Range.Text方法填充Word文檔內容,完畢之後一定要關閉Word文檔。另外就是程序結束後要關掉Word進程,否則每次導出都會在內存中創建一個Word進程造成資源的浪費。

  5. Word文檔保存的格式選用wdFormatDocumentDefault,這個是Word文檔保存的默認格式,如果選用其它格式,需要確保你所保存的內容能被選用的格式支持。

  下面來看一下CreateFileName方法。這個方法是用來處理生成的Word文檔的文件名的。程序中使用了郵件主題作爲Word文件名,由於Windows文件名不支持部分特殊字符,因此我們需要在生成文件名的過程中將這些特殊字符過濾掉,同時爲了確保所生成的Word不會被重複覆蓋,需要對文件名作差異化處理——即如果文件存在則在後面增量增加一個數字,如新建文件1、新建文件2等。另外就是如果文件名過長只截取前100個字符。



好了,到此程序已經大功告成了,但是有兩個問題:

1、郵件選擇只能以當前視圖下的多選爲準,沒有實現郵件目錄的選擇;

2、附件的方案有待優化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章