嗨,Scripting Guy!小心隔牆有耳

Microsoft Scripting Guys

在美國南北戰爭時期 (更準確地說是在斯波特瑟爾韋尼亞縣府戰役 (Battle of Spotsylvania) 中),聯邦將領約翰塞吉威克 (John Sedgwick) 驚訝地看到他的人馬在南軍狙擊手的突擊下畏縮不敢前進。相傳塞吉威克跟他的人馬說:「你們這樣逃,真是太可恥了。距離這麼遠,他們根本連一頭大象也打不到。」過了一會兒,塞吉威克就被殺了,成為這場戰爭中陣亡的最高階聯邦軍官。也許南軍狙擊手在這麼遠的距離下打不到大象,但卻可以打到將領,塞吉威克太倒霉了。

很顯然,故事的寓意是....您一定會說:「人家瞄準您開槍時,把頭壓低一點兒不就好了」,這就是很好的寓意,不是嗎?哦,不對,我們不是講這個。我們得到的寓意是:「說話小心,哪天就輪到你」。約翰塞吉威克就是這樣,對很多人來說,向來也是如此。例如,沒有人記得湯瑪斯華生 (Thomas Watson) 這位 IBM 永遠的領袖,也是美國歷史上最偉大的企業家之一。名垂千古的華生曾經說過:「我認為全球市場大概只要五台電腦就夠了」。

實在有趣,我們無法證實華生是否真的說過這句話,他頂多可能只是提到某人對這件事的看法而已。但誰在乎呢?畢竟,IBM 領導者可能說過沒有電腦市場的這個想法創造了一個精彩的故事,我們不能任由蠢事成真而搞砸一切,不是嗎?

順便一提,華生曾經說過:「如果想要成功,就讓失敗率加倍」。這是 Scripting Guys 一心奉為圭臬的告誡。

好吧,至少在失敗率加倍這部分是如此。

接下來看這句話:「如果您在編寫 VBScript 指令碼,您就無法存取 .NET Framework」。誠然,向人們說指令碼設計師無法存取 Microsoft® .NET Framework 並不像「距離這麼遠,他們根本連一頭大象也打不到」或「我認為全球市場大概只要五台電腦就夠了」那麼有說服力。然而,這種說法太過偏頗。.NET Framework 的設計的確以 C# 和 Visual Basic® 開發人員為對象,VBScript 也確實無法初始化原生的 .NET Framework 類別。但這並不表示指令碼設計師就無法存取 .NET Framework,至少不是完全無法存取。

沒錯,無可否認大多數的 .NET Framework 類別還是無法供 VBScript 使用。但還是有相當多的 .NET 類別有 COM 可呼叫的包裝函式,簡單地說就是這些類別有 COM 介面,可供 VBScrip t 等指令碼語言來存取。您難以置信嗎?那就看一下登錄的 HKEY_CLASSES_ROOT 部份,如 [圖 1] 所示;您會看到以 System 開頭的各種類別。

圖 1 登錄中的 System 類別

圖 1** 登錄中的 System 類別 **(按影像可放大)

這大多數是 .NET Framework 類別,而且大部分都可以透過 VBScript 來初始化。

相信了吧!

當然,可以做並不表示應該做,只要曾在深夜電視購物頻道買過東西的人就知道 (這些商品不在店面銷售通常是有原因的)。當然,您可以在指令碼中建立 System.ContextMarshalException 類別的執行個體,我們只是不瞭解您為何要做或應該怎麼做。

但並非所有 .NET Framework 類別都是如此。例如,指令碼中最麻煩的事情之一就是很難排序項目清單。將清單按字母順序排列必須使用離線資料集或自行建立祕密的排序函數,就像下列的泡泡排序 (Bubble Sort) 程式碼一樣 (腸胃不太好的人可以考慮跳到下一段):

For i = (UBound(arrNames) - 1) to 0 Step -1
    For j= 0 to i
        If UCase(arrNames(j)) > 
          UCase(arrNames(j+1)) Then
            strHolder = arrNames(j+1)
            arrNames(j+1) = arrNames(j)
            arrNames(j) = strHolder
        End If
    Next
Next

別灰心,我們自己也不是很清楚自己所寫的程式碼意思為何。

那麼 .NET Framework 在這種情況下有什麼用?為了回答這個問題,我們來看一小段很簡單的指令碼:

Set DataList = CreateObject _
      ("System.Collections.ArrayList")

DataList.Add "B"
DataList.Add "C"
DataList.Add "E"
DataList.Add "D"
DataList.Add "A"

DataList.Sort()

For Each strItem in DataList
    Wscript.Echo strItem
Next

如您所見,我們在這裡做的是建立 .NET Framework 類別 System.Collections.ArrayList 的執行個體。接著用 Add 方法在清單中加入五個項目。好歸好,但有個問題:我們以任意順序加入項目 (B, C, E, D, A),但必須按字母順序來顯示項目 (A, B, C, D, E)。該是搬出泡泡排序程式碼的時候了,對吧?

錯!(恕我直言,但錯就是錯。)結果是我們不需要搬出泡泡排序程式碼,只要呼叫 arraylist 的 Sort 方法即可:

DataList.Sort()

就這麼簡單。只要一行程式碼就可以排序陣列清單中的所有項目。

要證明嗎?哎呀!真是固執。沒關係,一定是會有一兩位只相信眼見為憑的讀者。因此,指令碼包含一個 For Each 迴圈來遞迴檢查清單中所有的項目,將每個項目的值回應在螢幕上:

For Each strItem in DataList
    Wscript.Echo strItem
Next

現在問您一個問題:執行這段指令碼之後會出現什麼?

猜對了:

A
B
C
D
E

好極了,不是嗎?

咦?好像還有人不太滿意哦!您覺得這樣很好是沒錯,但您真正想要做的是按遞減順序回應這些值,也就是說,您想要一個從 Z 排到 A 的清單,而不是從 A 排到 Z。如果不行的話,那麼.... .

好啊,沒有問題。您要做的只是要求:

Set DataList = CreateObject _
      ("System.Collections.ArrayList")

DataList.Add "B"
DataList.Add "C"
DataList.Add "E"
DataList.Add "D"
DataList.Add "A"

DataList.Sort()
DataList.Reverse()

For Each strItem in DataList
    Wscript.Echo strItem
Next

您說的對:看起來跟剛才的指令碼不是差不多嗎?可是其中多了一個重要的部分。請看一下我們加上的一行程式碼,在 Sort 方法下面的那一行:

DataList.Reverse()

看到沒?按遞增順序排序清單之後,我們就呼叫 Reverse 方法,這竟然反轉陣列中所有項目的順序。這樣會出現什麼?這樣會變成下列的輸出:

E
D
C
B
A

很厲害吧!

請稍安勿燥。還不只如此。您試過從 VBScript 陣列中移除項目嗎?別害怕,我們不會要您做的。我們會示範從 .NET Framework arraylist 中移除項目有多麼簡單。不喜歡 D 這個字母嗎?那就把它拿掉吧。下列指令碼會建立陣列清單、加以排序,然後移除 D 這個項目:

Set DataList = CreateObject _
  ("System.Collections.ArrayList")

DataList.Add "B"
DataList.Add "C"
DataList.Add "E"
DataList.Add "D"
DataList.Add "A"

DataList.Sort()
DataList.Remove("D")

For Each strItem in DataList
    Wscript.Echo strItem
Next

同樣地,請看一下 Sort 方法下面的一小行程式碼:

DataList.Remove("D")

我們用 Remove 方法來移除等於 D 的項目。下列是執行指令碼之後出現的東西:

A
B
C
E

D 就拜拜囉!

別忘了最厲害的部分:我們不必重新調整陣列的大小或重訂維度,就可以移除項目。酷吧!當然,不用重新調整大小或重訂維度也一樣可以在陣列中加入項目。您還敢說指令碼很難嗎?

信不信由您,我們連怎麼使用 .NET Framework arraylist 都還沒摸到 (如果需要詳細資訊,請參考 .NET Framework SDK 中的 arraylist 項目 (英文))。

arraylist 對指令碼設計師有用嗎?這樣說好了:看過 .NET Framework arraylist 之後,我們不知道您想怎麼使用內建的 VBScript 陣列,至少不是像這裡用的項目清單那麼簡單。

無可否認,Scripting Guys 並沒有徹底調查 .NET Framework 中有哪些類別對指令碼設計師有用 (或哪些類別沒有用)。這也是我們正在努力的地方。目前就請您逛逛完整的 .NET Framework SDK。每次瀏覽 SDK 時就會發現有一些新的和有趣的東西是指令碼作者用得到的。

例如呢?嗯...譬如產生隨機數字。想要用 VBScript 隨機數字產生器來產生 1 和 100 之間的隨機數字嗎?沒問題,只要用這樣的程式碼就好了:

Randomize
Wscript.Echo Int((100 - 1 + 1) * Rnd + 1)

將公式背起來了嗎?如果背不起來,別擔心,利用 .NET Framework System.Random 類別就更簡單又直覺:

Set objRandom = CreateObject("System.Random")
Wscript.Echo objRandom.Next_2(1,100)

不用多說,想必您一定懂的。不過我們還是稍微說明一下,沒辦法,TechNet Magazine 叫專欄作家們每個月一定要寫出一些東西來。(追問為什麼的話 - 那是他們的政策,不是我們訂的。)

如您所見,我們建立 System.Random 類別的執行個體,然後呼叫 Next_2 方法,並且傳入兩個參數:

  • 1,範圍的第一個數字 (記得,我們希望產生 1 和 100 之間的隨機數字)。
  • 100,範圍的最大數字。

這樣就可以了。

看來還有時間回答最後一個問題。沒錯,就是您。是誰說指令碼作者不能存取 .NET Framework 的啊?坦白說,畢竟我們一開始就沒有控制好時間。下個月見了!

祝一切順利

二月份快到了,這意味著一件事。不是情人節,也不是林肯誕辰。土撥鼠節嗎?天啊!拜託!都不是,二月份就要舉辦 2007 年冬季指令碼比賽 (2007 Winter Scripting Games),這是全球首屈一指的 (可能是全球唯一的) 國際指令碼競賽。今年比賽的規模和品質一定勝過以往,這次分為四組:

  • 初級 VBScript
  • 進階 VBScript
  • 初級 Windows PowerShell™
  • 進階 Windows PowerShell

如果需要詳細資訊,請參閱 2007 冬季指令碼比賽首頁 (英文)。

不知道嗎?噢!那就謝謝了。祝土撥鼠節快樂。

立即下載 Windows PowerShell

現在已開放下載 Windows PowerShell 1.0,而且還是免費的!您怎能錯過呢!請立刻下載軟體並開始使用。

Microsoft Scripting Guys 為 Microsoft 做事,也就是受雇於 Microsoft。他們不玩/指導/觀賞棒球 (及各種其他活動) 時,就負責管理 TechNet 指令碼中心 (TechNet Script Center)。請看一下 www.scriptingguys.com

© 2008 Microsoft Corporation and CMP Media, LLC. 保留所有權利;未經允許,嚴禁部分或全部複製.