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

Office Space

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

使用 Microsoft Outlook 清點郵件

雖然電腦的專長之一是當一位「統計員」,但我們很少會利用它的算術功能,就算我們很想計算出來某些數字,也不見得會借助電腦。就拿 Microsoft Outlook 當例子吧。如果您在走廊上閒晃 (這是 Scripting Guy 常做的事),很可能會聽到有人在怨嘆:

「10 月份的會議好像比平常多出一倍呢。」

「我想我大部份的人脈都在 Fabrikam 吧,要不就是在 Contoso。哎呀我也搞不清楚。」

「禮拜二不吉利:不知道為什麼,每到禮拜二我總會收到「一拖拉庫」的電子郵件,或者至少我認為會收到這麼多。」

聽起來很像統計員的工作。

大家都知道 Microsoft Outlook 存放了大量資訊;問題是那些資訊也拿不到。至少是到目前為止是如此。在今天的專欄中,我們要教您如何清點 Outlook 資料夾裡面的項目,方法非常簡單,我們要計算您每天收到的郵件數目。也許您覺得這不是很重要,不過別忘了,只要瞭解基本原理,就可以修改這個範例指令碼,讓它無所不能,像是:

  • 檢查 [寄件備份] 資料夾,清點「您」每天寄送的郵件數目。

  • 檢查您的 [收件匣],依寄件人或主旨來清點郵件。

  • 檢查 [行事曆],依星期或月份來計算約會數目。

  • 檢查您「所有」的 Outlook 資料夾,清點您所收到的郵件總數,以及收到的時間。

等諸如此類的事情。

不過,今天我們想把事情簡單化:我們只想抓出 [收件匣] 裡所有的郵件,根據日期加以清點就好 (例如,您在 10 月 10 日收到幾封郵件,在 10 月 9 日收到幾封郵件等等)。記住,我們清點的是確實「位在」[收件匣] 裡面的郵件。如果您在 10 月 10 日收到郵件之後,就將它們刪除或移到其他資料夾,這個指令碼就清點不到了。我們只能處理還在 [收件匣] 裡的項目。

請看看下面這個程式碼:

Const olFolderInbox = 6

Set objDictionary = CreateObject("Scripting.Dictionary")

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

Set colItems = objFolder.Items

For Each objItem in colItems
    strDate = FormatDateTime(objItem.SentOn, vbShortDate)
    If objDictionary.Exists(strDate) Then
        objDictionary.Item(strDate) = objDictionary.Item(strDate) + 1
    Else
        objDictionary.Add strDate, "1"
    End If
Next

colKeys = objDictionary.Keys

For Each strKey in colKeys
    Wscript.Echo strKey, objDictionary.Item(strKey)
Next

這個指令碼在做什麼呢?一開始很簡單:它先定義一個常數 olFolderInbox,將其值設為 6 (在我們告訴 Outlook 使用哪一個資料夾時,這個常數才會派上用場)。接下來就是為 Dictionary 物件建立一個執行個體,請它持續按照日期清點傳送的郵件數目。

萬一不清楚時,可以利用 Dictionary 物件來追蹤索引鍵與項目的搭檔組合,這很方便:您提供 Dictionary 一組索引鍵 (例如,日期) 以及一組對應項目 (例如,在那些日期傳給您的郵件數目)。雖然在大部份的情況下,Dictionary 物件只是一個陣列而已,不過它至少還具備一個附加價值:讓您既快速又方便的查出是否有特定的索引鍵存在。如果日期是 2005 年 10 月 10 日,就可以用 Exists 方法,看看 Dictionary 中是否已有這個名稱的索引鍵存在。相較之下,如果是使用陣列,我們就得一一檢查該陣列中所有的值,看看是否有任何值等於 2005 年 10 月 10 日。接著再對「下一個」日期重複同樣的步驟。再下一個也是,真是沒完沒了 …

雖然我們對 Dictionary 物件的介紹稍嫌粗略了點,不過您應該也抓到一些要領了吧。您也可以看看《Microsoft Windows 2000 Scripting Guide》(英文) 的相關部分,多少瞭解一點 Dictionary 物件的背景資訊。

等等,我們說到哪了?對了,指令碼。Dictionary 物件建立之後,我們就用下面這幾行程式碼,為 Outlook.Application 物件建立一個執行個體、繫結到 MAPI 命名空間,然後再開啟 [收件匣] 資料夾:

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

這麼一來就可以呼叫下面這行程式碼,擷取 [收件匣] 中所有項目的集合:

Set colItems = objFolder.Items

接下來好戲就要上場了!

其實,現在已經夠好玩了吧?不過接下來會更精采。

我們在開始時設定了 For Each 迴圈,將 [收件匣] 中的項目集合 (即郵件) 全部巡迴一次。接著就到下面這行程式碼:

strDate = FormatDateTime(objItem.SentOn, vbShortDate)

郵件的 SentOn 屬性,代表郵件傳送的日期和時間。過在這個指令碼中,我們只管「日期」就好:我們想知道在 2005 年 10 月 10 日當天有多少電子郵件傳給我們,至於「什麼時候」傳送則無所謂。因此,我們使用 FormatDateTime 函數,代入 SentOn 屬性的值和 VBScript 常數 vbShortDate。為什麼這麼做呢?因為此舉會把 SentOn 值的時間部份去掉,比方說,把「10/10/2005 1:47 PM」變成「10/10/2005」。這個值就儲存在 strDate 變數中。

接下來我們就要使用 Dictionary 物件了。我們先利用 Exists 方法,判斷 Dictionary 是否已經有一個與 strDate 值相同的索引鍵 (例如,10/10/2005):

If objDictionary.Exists(strDate) Then

如果 Exists 方法傳回 False 值,就表示沒有這樣的索引鍵存在。這時候,我們就在 Else 陳述式中執行下面這行程式碼:

objDictionary.Add strDate, "1"

我們要呼叫 Dictionary 物件的 Add 方法,傳遞兩個參數:變數 strDate (這表示我們要加入一個與 strDate 值相同的索引鍵) 和 1。為什麼是 1 呢?因為這是我們發現在 2005 年 10 月 10 日當天送出的第一封郵件,而且我們要使用索引鍵與項目組合的項目部份,來追蹤每天傳給我們的郵件總數。而到目前為止,傳送的郵件總數是 1 封。

不過,如果 Exists 方法傳回 True 值的話,又代表什麼呢?答案只有一個:Dictionary 已經有一個與 strDate 等值的索引鍵了 (喔,對了,Dictionary 物件的索引鍵必須是唯一專屬的索引鍵)。換句話說,我們至少找到一封在 2005 年 10 月 10 日當天發送的郵件,而且也把這件事記錄在 Dictionary 中。現在我們又發現另一封 2005 年 10 月 10 日發送的郵件,因此我們要用下面這行程式碼,累加當天傳給我們的郵件總數:

objDictionary.Item(strDate) = objDictionary.Item(strDate) + 1

我們只要繫結到指定索引鍵搭檔的項目,然後再加 1 就行了。很簡單吧?

這樣就大功告成了。接下來就是對 [收件匣] 的下一封郵件,重複執行這個程序。

等我們將那些日期以及在那些日期傳給我們的郵件數目清點完畢之後,就可以利用下面這一段程式碼來回應結果了:

colKeys = objDictionary.Keys

For Each strKey in colKeys
    Wscript.Echo strKey, objDictionary.Item(strKey)
Next

這也是很基本的部份:我們只要把 Dictionary 索引鍵逐項執行一次,然後對索引鍵集合中的每個索引鍵,回應索引鍵和項目的值。(請記住,索引鍵是指日期,而項目是指當天傳給我們的郵件數目)。接著就得出類似下面所示的結果:

10/6/2005 2
10/7/2005 2
10/8/2005 5
10/9/2005 21
10/10/2005 1
10/11/2005 1
10/12/2005 4

從結果看出,10 月 9 日特別忙碌。所以請提醒我們把下一個 10 月 9 日排開。

我們在前面說過,這個指令碼不是即時運作的指令碼。它不會在您一收到郵件時就立刻追蹤並且更新清點結果。(至於有沒有可能做到呢?其實,也不是不可能啦,只是比較複雜一點)。所以我們決定只清點仍在 [收件匣] 內的所有郵件。如果您在看完郵件之後就將它刪除了,那麼以我們所介紹的指令碼來說,目前還做不到這一點。不過,如果您的 [收件匣] 類似我們在 Microsoft 看到的一些收件匣 (亦即 [收件匣] 裡塞滿成千上萬封郵件),也許這個指令碼能夠派上用場也說不定。(編者小記:別這樣嘛,我們有人真的很需要 2002 年 5 月 8 日傳來的那 24 封郵件好嗎!)

總之,現在您手上的 number-crunching,是全球排名第三的計數產品 (僅次於雀巢和克普典)。