上課了

作者: Microsoft Scripting Guy

指碼街

歡迎來到「指碼街」,這是專為初學指令碼的人所寫的專欄。本篇專欄的目的在於教導您如何編寫系統管理自動化的初級 Windows 指令碼。我們將提供您所需的資訊,幫助您開始閱讀和瞭解指令碼並配合個人的需求著手修改指令碼。如果您對指令碼有任何不清楚的地方,請告訴我們;也許有人和您一樣不清楚。

若要查看過去的文章,請參閱《指碼街過往文章》(英文)。

本页内容

上課了
何謂類別?
屬性
方法
方法參數
物件
連結
釋放物件
真實世界
快樂時光


上課了

Scripting Guy 常看到有人興致勃勃的跑去上電腦程式設計初級課程 (甚至是指令碼初級課程),卻帶著一頭霧水回家。這些人都是成日與電腦為伍的人,而且看起來都相當聰明。既然這樣,為什麼個個上完回來,都覺得自己笨得可以?難道他們都不如外表那麼聰明嗎?雖然我們不想排除這種可能性,但也可能是因為他們操之過急,一頭就栽進類別和物件的觀念裡。別擔心,慢慢來,我們會盡量幫助您度過難關,也許 (只是也許啦) 上完之後,您會發現自己其實也沒那麼糟啦。

何謂類別?

類別就像房屋規劃圖或藍圖一樣。您可以從房屋規劃圖看出來房子的結構,例如一廚、一臥、一衛。甚至告訴您,廚房裡有瓦斯爐、流理台,而臥室有衣櫥。但是它不會畫出流理台水龍頭該往哪邊轉開,打開之後會流出多少水,以及衣櫥裡掛什麼。舉個例說,下面是廚房的規劃圖:

廚房規劃圖

類別就跟它一樣,只提供基本架構讓您著手。我們來看看廚房類別大概長什麼樣子。

廚房類別

當然您不能直接在廚房規劃圖上煎蛋,您得真的蓋出一個廚房,在裡面安置瓦斯爐之後才能煎蛋。同樣的,您也不能在廚房類別做什麼事,您得利用這個類別來建立一個廚房物件才行。那物件又是什麼?別急,等下在給您說明。

屬性

拿房子 (甚至廚房) 來舉例是稍嫌大了點,也複雜了點。我們先把它簡化,只看一個冰箱。好,先從檢查冰箱特性開始著手。首先,來部彩色冰箱!您不會希望在您美美的春綠色廚房裡,擺上一部死白的冰箱吧。再來,裡面要有層架可以擺放食物。有多少層架呢?冰箱門有供冰器嗎?

我們來看看結果吧:


Refrigerator
    Color
    Shelves
    IceDispenser


嘩!您已經定義出「冰箱」類別的屬性了。再看一次:


Refrigerator
    string Color
    int Shelves
    boolean IceDispenser


Color 是指顏色的名稱,我們把它定義為特色字串。Shelves 是指冰箱層架的數目,我們把它定義為整數。而 IceDispenser 只是 Yes/No 值 (代表有或沒有供冰器),因此我們把它定義為布林值。

深入說明: 即使我們在這裡給定屬性資料類型 (字串、整數和布林值),VBScript 也不是這樣使用類型的。VBScript 只用一種資料類型:變數。何謂變數呢?對 VBScript 來說,變數就是指「任何類型」。但是當您細看 MSDN 的任何一個軟體開發套件 (SDK) 時,會發現幾個特定的資料類型。這些資料類型是其他語言 (像 C++) 所必需,但對 VBScript 來說,只有 SDK 中的定義才有關聯,因為它會告訴您應該使用哪一種資料。將來我們可能會以另外的篇幅描述資料類型。


其他資訊

VBScript 資料類型 – Microsoft Windows 2000 Scripting Guide (英文)

基本資料類型 – Visual Basic Language Reference (英文)

方法

我們已經定出幾種特色或屬性,或是我們的冰箱。接下來就要想想冰箱的用途。當然,最明顯的用途就是將東西保持低溫,要保持低溫就要設定溫度。如果有供冰器,可能還需要選擇碎冰或冰塊。請看!這就是我們的方法:


Refrigerator
        SetTemperature
        SelectIce


方法參數

現在我們就可以用 SetTemperature 方法來設定冰箱溫度了。不過不是光叫冰箱「設定溫度!」就可以了,還要告訴它需要什麼溫度,因此必須在方法使用參數。這裡的參數就代表我們想要設定的溫度值:


SetTemperature(39)


如果是冰,則給方法一個字串參數 (「crushed」或「cubed」):


SelectIce("crushed")


這就是所謂的將參數傳遞給方法。將參數傳給方法,讓方法有「行動方案」可循。我們也可以利用變數來傳遞參數 (就像我們在前一個專欄所討論):


intTemperature = 39
SetTemperature(intTemperature)


我們怎麼知道在呼叫 SetTemperature 方法時要代入數字,或是要在 SelectIce 方法代入字串「crushed」或「cubed」呢?答案是查看相關的 SDK。大部份我們所用的指令碼技術,在 MSDN 上都有相關的 SDK 文件。

深入說明:屬性與方法

在我們理想的冷凍世界中,屬性和方法是涇渭分明的:屬性描述東西,而方法則對東西採取動作。夠清楚吧!不過問題來了,為什麼我們要為 SetTemperature 設定方法,為什麼不用 Temperature 屬性,然後指派一個代表溫度的數值給它就好?為什麼還需要方法?

這幾個問題如果您答得出來,可要跌破一堆人的眼鏡,因為連我們都答不出來。讓我們先看看幾個實際例子,您就會明白我這句話的意思了。

WMI 類別 Win32_OperatingSystem 有不少屬性,還有四個方法。LastBootUpTime、SerialNumber 和 Version 就是屬性的一種。這些都是與作業系統有關的特性,因此根據我們的屬性定義,它們的意義都非常明確。就連方法的意義都很明確:Reboot、SetDateTime、Shutdown 和 Win32Shutdown。這些都是動作,也就是必須在作業系統發生的事。到目前為止都還明白吧?

接著我們來看看 WMI 類別 Win32_Directory。這個類別有許多屬性,與使用您所預期的目錄有關,例如 FileName、Extension 和 Hidden。再來看看方法。等一下,我們發現有個方法叫做 Rename,可是另外也找到一個屬性叫做 Name。為什麼我們還需要呼叫 Rename 方法,而不乾脆設定 Name 屬性就好?

更氣人的是,有的類別沒有 Name 屬性,卻有 Rename 方法。怎會這樣?!對不起,我們也不知道。目前就只能靠建立類別的開發人員,盡量分辨它們是特性還是動作了。其他人不見得都搞得清楚,不過也沒辦法了。


其他資訊

WMI SDK

ADSI SDK

物件

下面就是我們的類別:


Refrigerator
    string Color
    int Shelves
    boolean IceDispenser

    SetTemperature(int Temp)
    SelectIce(string IceType)


既然有 Refrigerator 類別,那就設定冰箱的顏色吧:


Refrigerator.Color = "green"


我們的冰箱是綠色的吧?錯!記住,類別只是冰箱的藍圖,您不能將藍圖變成綠色。(那不就變成「綠圖」了?嗯,這扯得太遠了吧)。首先我們需要建立一個真的物件,這裡就是指冰箱嘍:


Set objFridge = CreateObject("Refrigerator")


物件一定是以 Set 陳述式建立。有時候我們用 CreateObject,有時候用 GetObject,有時候是其他,不過一定都是 Set 陳述式。那為什麼不這麼做就好?


objFridge = CreateObject("Refrigerator")


因為這個方法對物件行不通。要從類別建立物件,不能只用等號,還需要 Set。

深入說明:好吧,不能兩三句打發讀者。這其實牽涉到記憶體位址。我們不只是在變數代入一值,而是真的參照記憶體的一個位置,因此您經常會看到「物件參照」。變數 (例如 objFridge) 不是真的包含物件,而是包含儲存在記憶體中的物件參照。所以老實說,即使沒有 Set 陳述式,您也可以建立物件,不過卻不能指派物件參照給變數,也不能在其他任何地方使用這個物件參數,除非您已經在設定陳述式中用過 Set 了。


有了物件之後,我們就可以做一些事,像是設定溫度、更改顏色、以及查詢它是否有供冰器:


Set objFridge = CreateObject("Refrigerator")
objFridge.SetTemperature(43) ' Degrees Fahrenheit
objFridge.Color = "green"
Wscript.Echo objFridge.IceDispenser


也許您在納悶,為什麼我們非得一一做完這些步驟不可。為什麼不能就使用類別,非得建立物件不可?因為它不是真的冰箱,它還在電腦上。這麼說好了,如果一個餐廳有兩個冰箱時,該怎麼辦呢?您要如何分辨兩者的特性呢?這時候就要使用物件了:建立兩個物件,兩者都以 Refrigerator 類別為依據。如果我們希望冰箱 1 比冰箱 2 更冷,只要使用 Refrigerator 類別建立兩個物件即可:


Set objFridge1 = CreateObject("Refrigerator")
Set objFridge2 = CreateObject("Refrigerator")

objFridge1.SetTemperature(39)
objFridge2.SetTemperature(43)


簡單吧。

其他資訊

The COM Process – Microsoft Windows 2000 Scripting Guide (英文)

Creating an Object Reference – Microsoft Windows 2000 Scripting Guide (英文)

連結

您還跟得上吧?還撐得住嗎?要不要停下來喝點東西?慢慢來,我們可以等。

現在可以了嗎?接下來就比較複雜了,要有心理準備哦。

要談物件,就不能不談連結。如果您曾看過許多 Script Center 裡面的文章,下面這個句子應該不陌生:「我們先從連結到 Active Directory 中的使用者帳戶著手」,或是下面這個句子:「我們先從連接 WMI 服務著手」。雖然第二句我們用「連接」取代「連結」,不過其實是同樣的意思。(別誤會,我們不是犯了不一致的錯,只是出自一片好意,希望讓您接觸到更多的單字)。或許您也聽過「具體化」這個名詞,那也代表同樣的意思。

那麼,連結 (或連接或具體化) 到某個東西又是什麼意思呢?其實它真正的意思,就是建立物件。您藉由呼叫 CreateObject 或 GetObject,而連結到 Active Directory 或 WMI 或 ADO 或其他任何物件 (根據物件類型及出處而定)。至於何時呼叫哪一個,我們就不在這裡多說了。您可以尋找使用您所要物件的範例,然後取而用之。(這也是「指令碼存放庫」的宗旨,無論您需要什麼東西,都歡迎取用)。

其他資訊

Binding – Microsoft Windows 2000 Scripting Guide (英文)

Connecting to Objects – Microsoft Windows 2000 Scripting Guide(英文)

釋放物件

很多人都不清楚釋放物件的意思。釋放物件必須執行下面這個動作:


Set objFridge = Nothing


物件設為 Nothing 之後,就不能用在指令碼了。很多人都誤以為他們必須在指令碼結束時,對所有的物件執行這個動作。告訴您一個祕密,其實不必這麼做。這只是多打幾個不必要的字罷了,我們說過了,Scripting Guy 絕不鼓勵您多打不必要的字。

可是為什麼有那麼多人都認為必須這麼做呢?這是過去的經驗使然。請看看下面這個故事:

當您建立物件時,電腦會在記憶體裡面為該物件建立一個空間。因此當您建立 objFridge 時,電腦會有一個小小的空間告訴您:「你好!現在我就是 objFridge 了!」許多程式設計語言 (如 C 和 C++) 撰寫的程式即使結束了,記憶體裡面的那一塊還在電腦裡面閒晃,嘴裡不斷唸著:「我是 objFridge。」當您再次執行這個程式時,便換成記憶體的另外一塊開口說:「您好,我是 objFridge!」這樣反覆執行幾次之後,就會出現一大堆「自稱」為 objFridge 的記憶體空間,什麼事也沒做,卻認為自己很重要。那是因為這個記憶體認為他有任務在身,不能用在別的地方。當您再次啟動該程式時,不能再使用原有的那一個 objFridge,因為他們都認為自己很忙,所以程式只好每次都製作一個新的分身。

當您建立幾千個 objFridge 之後,大概就可以想像電腦效能會產生什麼變化了吧。這些 objFridge 把記憶體全佔滿了,誰也別想用它。如果要防止這種情況繼續惡化,在結束程式時,記得一定要清除物件:將它們設為 Nothing,釋放記憶體讓別人使用。

好了,記憶體擬人化到此為止。

那麼,為什麼我們說您不必這麼做呢?因為 VBScript 與其他語言不同,他會自我清除。只要指令碼一結束,剛剛所用的記憶體都會自動釋放。因此,您在結束時把物件設為 Nothing,只不過是在做待會 VBScript 所要做的動作罷了。所以何必多此一舉呢?

不過,如果您所建立的指令碼又冗長又複雜,要做很多處理或者要花很多時間,最好還是先釋出一部份不用的物件,把記憶體讓給其他人吧。至於小指令碼就無需擔心了。

另外,關於釋放物件還有一件事要注意:我們說過,當您把物件設為 Nothing 時,就不能在指令碼使用它了。不過,如果這個物件建立了一個執行中的應用程式執行個體,那個應用程式仍會繼續執行。即使結束指令碼也不會停止它;雖然記憶體不會持續追蹤它,但是應用程式仍在執行中,當然也會佔用記憶體本身。舉個例說,下面這行程式碼會建立一個 Microsoft Excel 的執行個體:


Set objExcel = CreateObject("Excel.Application")


如果您在指令碼中執行這一行,就會啟動 Excel。這個動作不會關閉 Excel:


Set objExcel = Nothing


雖然將 objExcel 設為 Nothing,代表您不能在指令碼中參照該 Excel 的執行個體,但是 Excel 仍會繼續執行。對於這類物件,您必須借助 Visible 屬性來顯現 Excel,讓使用者用手動方式關閉它;另一個方法就是從指令碼關閉 Excel:

顯現 Excel:


objExcel.Visible = True


關閉 Excel:


objExcel.Quit


簡而言之,將物件設為 Nothing,並不會結束執行中的應用程式物件,它也不會執行 VBScript 不做的事,除非您的指令碼既複雜又花時間,又佔用記憶體。(好了,休息一下吧)。

其他資訊

When Are You Required To Set Objects To Nothing?– Eric Lippert’s Blog

Unloading Objects from Memory (英文) – Microsoft Windows 2000 Scripting Guide(英文)

真實世界

我們已經講完了冰箱和房屋規劃圖,接下來要看看它怎麼用在真的指令碼中。我們來看看 FileSystemObject 類別。FileSystemObject 類別可定義一組方法和屬性,讓您建立物件,來存取電腦的檔案系統。(請注意,這個類別只能用在本機電腦,不能用在遠端電腦)。不過今天暫且不談這個,好,繼續。)

如果您看這份 MSDN 的文件 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/jsobjfilesystem.asp),應該會看到 FileSystemObject 類別定義了不少方法,還有一個屬性。下面這個指令會建立 FileSystemObject 物件,然後檢查是否有特定的資料夾存在:


Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FolderExists("C:\Scripts") Then
    Wscript.Echo "Folder exists."
Else
    Wscript.Echo "Folder does not exist."
End If


如您所見,我們先從 FileSystemObject 類別建立一個 FileSystemObject 物件,然後稱該物件為 objFSO。因此 objFSO 是我們可以使用的真正物件,或者說得更技術性一點,objFSO 是我們所用的 FileSystemObject 類別執行個體的物件參照 (看看具體化的源頭在哪裡)。

注意: 您常常會看到我們參照「FileSystemObject」。因為「FileSystemObject 物件」聽起來很多餘,而且有點好笑。不過我們就是指那個意思。


接下來我們要呼叫從 FileSystemObject 類別建立的任何物件所能使用的其中一個方法,這裡我們用 FolderExists 方法。我們在這個方法代入一個參數,也就是通往我們想要檢查它是否存在的資料夾的路徑:


objFSO.FolderExists("C:\Scripts")


關於方法我們還得再加一筆,那就是它們不僅可以代入參數,許多還可以傳回值。如果我們做過這個動作:


IsAFolder = objFSO.FolderExists("C:\Scripts")


就會將 IsAFolder 變數用在 If 陳述式:


If IsAFolder Then


不過我們採取另一條捷徑,只將方法呼叫置於 If 陳述式中。這種捷徑是您在使用傳回布林 (true/false) 值的方法時,所用的標準作法,FolderExists 方法就是這麼做。

之後我們只要回應陳述式,表示 FolderExists 方法是否找到資料夾就可以了。

其他資訊

Taking Multiple Actions by Using If Then Else – Microsoft Windows 2000 Scripting Guide (英文)

If…Then…Else Statement – VBScript Language Reference (英文)

FolderExists Method – Scripting Runtime Library (英文)

FileSystemObject Basics – Scripting Runtime Library (英文)

快樂時光

就醬,講完了。可以出去好好慶祝一番了,您不再是一提到類別和物件就腦袋一片混亂的可憐蟲了。至少我們希望不是。如果是的話,這個嘛...還是值得恭喜啦,至少文章已經接近尾聲了。還是出去慶祝一下吧。

顯示: