Office Space:撰寫 Microsoft Office 應用程式指令碼的秘訣

Office Space

歡迎蒞臨《Office Space》專欄,這裡提供撰寫 Microsoft® Office 應用程式指令碼的秘訣。每週二和週四我們將刊登新的秘訣,若要參閱以前討論過的秘訣,請造訪 Office Space 過往文件。如果您有關於 Microsoft Office 指令碼方面的問題,請將電子郵件傳送到 scripter@microsoft.com。我們無法保證能夠逐一回答每個問題,不過我們會盡力而為。

儲存您收件匣內所有的附件

Scripting Guys 的主管十分在意我們每個專欄都各有自己的特色 (您可能以為 Scripting Guys 的主管壓根兒不關心這種事吧!)。老實說,咱們 Scripting Guys 絕不會為這檔子事而睡不安穩的,反正讀者關心的是資訊內容而不是資料出處。但是主管得盡責,咱們也該盡力滿足上面的要求,確保每篇專欄都依照既定格式,不要離題。

至少到目前為止是這樣的。為了使我們的專欄角色分明:《嗨,Scripting Guy!》專欄是專門用來回覆讀者問題的,而其他專欄則沒有參考讀者意見,隨我們發揮主題。不要說喔,今天我們要破戒,用《Office Space》專欄來回答讀者的問題。

噓......我們知道這個消息非同小可,但是不該讓某些人知道,想必您懂我們指的是誰吧。

事實上這個問題不只一人問過,所以我們決定要回覆:請問有沒有辦法自動儲存 Outlook 收件匣內所有電子郵件的附件?我們瞭解為何有此疑問,因為 Outlook 的使用者介面只允許一次儲存一封電子郵件的一個附件。 舉例來說,如果您在 Outlook 裡選擇的項目超過兩個,[檔案] 功能表的 [儲存附件] 選項就會變成灰色:

Microsoft Outlook


真不方便,尤其是您的收件匣快爆時更是討厭。儲存所有附件並刪除電子郵件是不錯的解決辦法。那麼真的有辦法替 Outlook 收件匣自動儲存所有電子郵件的附件嗎?您瞧,就是這樣:


Const olFolderInbox = 6

Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set objFolder = objNamespace.GetDefaultFolder(olFolderInbox)

Set colItems = objFolder.Items

For Each objMessage in colItems
    intCount = objMessage.Attachments.Count
    If intCount > 0 Then
        For i = 1 To intCount
            objMessage.Attachments.Item(i).SaveAsFile "C:\Temp\" &  _
                objMessage.Attachments.Item(i).FileName
        Next 
    End If
Next


指令碼開始會先定義一個名為 olFolderInbox 的常數,接著將值設為 6,我們會用這個常數來指定要擷取資訊的來源 Outlook 資料夾。建立 Outlook.Application 物件的執行個體,然後使用 GetNamespace 方法連接到 MAPI 命名空間 (順便一提,這是唯一可供連接的命名空間,但您仍須使用這一行程式碼)。接著使用 GetDefaultFolder 方法繫結到 [收件匣] 資料夾,請注意我們的確使用了 olFolderInbox 常數,沒有食言!

附註:這個指令碼假設 Outlook 已經執行,若您認為這不妥當,請參閱前一篇《Office Space》專欄文章 (英文) 裡面介紹如何檢查 Outlook 是否正在執行,如果沒有要如何啟動應用程式。


接著使用這行程式碼建立一個包含 [收件匣] 內所有項目的集合。


Set colItems = objFolder.Items


附註:技術上來說,這一步並不需要,其實只要直接參考 objFolder.Items 即可。但是如果您事後想篩選 [收件匣] 內的項目,額外建立集合有其必要,所以我們就決定建立一個 Items 集合的物件參考,順便熟悉一下作法。


下一步要建立一個 For Each 迴圈來循環處理所有訊息集合。我們替集合內的每一筆訊息檢查附件數目,如這行程式碼所示:


intCount = objMessage.Attachments.Count


如果 intCount 大於 0,表示這個訊息的附件超過一個。因此我們設定一個 For Next 迴圈,從 1 跑到附件的數目。如果有一個附件,迴圈跑一輪 (For i = 1 to 1),如果有五個附件,則迴圈跑五輪 (For i = 1 to 5)。

那我們在 For Next 迴圈裡面要做什麼?我們只做一件事情,呼叫 SaveAsFile 方法來儲存每一個附件。SaveAsFile 需要一個參數:儲存檔案的完整路徑。在範例指令碼中路徑由兩部分構成:C:\Temp\ (剛好是我們想儲存所有附件的資料夾) 和 objMessage.Attachments.Item(i).FileName。這是附件的 FileName 屬性。假設其中一個附件的檔名為 Budget.xls,那麼傳給 SaveAsFile 的路徑會是 C:\Temp\ 加上 Budget.xls,或是 C:\Temp\Budget.xls 路徑。

讀者應該已經猜到了,objMessage.Attachments.Item(i) 只不過參照了該訊息在 Attachments 集合內的個別附件。第一次跑迴圈時 i 等於 1,表示使用集合中的第一個附件。第二次跑迴圈時 i 等於 2,表示集合中的第二個附件。別忘了,每個電子郵件有各自的附件集合 (即便集合內的項目數為 0,集合仍存在)。也就是說,我們並非使用 [收件匣] 內的所有附件集合,而是 [收件匣] 內的所有訊息集合,再針對每個訊息處理其附件集合。

瞧,很酷吧。這裡有個潛在問題,SaveFileAs 並不會檢查檔案是否存在。假設您的第一封電子郵件有一個名為 Budget.xls. 的附件。指令碼會忠實地將附件儲存為 C:\Temp\Budget.xls。現在假設您的第二封電子郵件也有一個名為 Budget.xls. 的附件。指令碼不會停下來問問您的意見,就以第二個 Budget.xls 檔案會取代第一個。當初的設計就是這個樣子,沒法修改。

所以我們得找個偏方。毫無疑問,最好的辦法是使用 FileSystemObject 並在儲存檔案前檢查是否已經有同檔名存在。如果有,指令碼會有所動作,確保檔名獨一無二。無奈今天篇幅不夠,不加詳述,改介紹另一種更快 (但沒有那麼漂亮的) 方法來產生獨特檔名。這個修改過的指令碼使用 FileSystemObject 搭配 GetTempName 方法來建立檔名。GetTempName 會隨機產生類似這樣的檔名:


radE728B.tmp


改寫過的指令碼只不過將隨機檔名和底線放在附件檔名的前面,路徑名稱就像這樣:


C:\Temp\radE728B.tmp_budget.xls


如果您擔心檔名重複,這個偏方倒可以輕鬆快速地解決問題。

改寫的指令碼如下:


Const olFolderInbox = 6

Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set objFolder = objNamespace.GetDefaultFolder(olFolderInbox)

Set colItems = objFolder.Items

Set objFSO = CreateObject("Scripting.FileSystemObject")

For Each objMessage in colItems
    intCount = objMessage.Attachments.Count
    If intCount > 0 Then
        For i = 1 To intCount
            strTempFile = objFSO.GetTempName
            objMessage.Attachments.Item(i).SaveAsFile "C:\Temp\" &  strTempFile & "_" & _
                objMessage.Attachments.Item(i).FileName
        Next 
    End If
Next


這樣就沒問題啦!別忘了,如果有任何主管提起,就說這是您在《嗨, Scripting Guy!》而不是《Office Space》專欄內讀到的 (反正我們 Scripting Guys 不怕犯規,只怕被抓)。

顯示: