嗨,Scripting Guy!快感

Microsoft Scripting Guy

毫無疑問的,在我們心目中,為《TechNet Magazine》撰寫文章可說是最高尚,而且回報最多的一項工作了 (當然囉,如果我們真的有薪水可拿的話,那回報就更多了,不過這是題外話啦)。話說回來,這份工作到底有多崇高呢?我不妨這麼說吧:現在全世界躺在被窩裡準備睡覺的小孩,都會抬頭看著媽媽說:「媽咪,等我長大後,一定要每月幫《TechNet Magazine》寫一篇指令碼專欄。」就是這麼高尚。

附註當然嘍,世界各地也有人相信這篇專欄其實根本是兒童寫的。不過這又離題了,暫且不談)。

大家都知道,Scripting Guy 很享受來自各方對於我們幫《TechNet Magazine》撰文所給予的讚美;甚至已經懂得如何應付那些爭相索取親筆簽名的粉絲還有狗仔了。不過我們從事這份工作可不是為了榮譽和面子,而是為了享受撰寫系統管理指令碼所帶來的快感。

的確,「快感」和「系統管理指令碼」這兩個名詞,很少同時出現在一個句子當中 (當然啦,如果「快感」是發生在完全沒有分泌腎上腺素的情況下,那就另當別論了)。

通常一般人會把編寫系統管理指令碼歸類為相當有用、但好像不是那麼刺激的工作,與其說不那麼刺激,不如說有點乏味吧。那是因為:他們根本沒在 Windows Vista® 或 Windows Server® 2008 寫過開機設定資料指令碼!

怎麼樣,心跳有沒有加快一點哪?大部分看過本專欄的人大概都知道,Windows Vista 和 Windows Server 2008 已經捨棄舊的 boot.ini 檔,換上可以增加開機程序管理彈性 (和功能) 的新開機設定資料儲存區了。

很酷吧。不過真正酷的是:多虧新 Windows® Management Instrumentation (WMI) 提供者的幫忙,現在使用指令碼來存取開機設定資料 (BCD) 儲存區要比以前容易多了 — 而且儲存區也獲得全面的管理。

如果您突然感覺心頭小鹿亂撞,別擔心,那就是所謂腎上腺素分泌。只要開始使用 BCD 指令碼之後,您就會習慣了。

不過在正式開講之前,我們要先表明一點,那就是我們不可能一開始就把使用 BCD 提供者所能做到的事情一一涵蓋,至少本文不可能 (我們已經寫信給雜誌編輯,問他可不可能特別針對編寫開機設定資料指令碼,做一次長達兩期的專題報導,不過還沒收到他的回函就是了)。

如果您需要更多完整資訊,請查訪 BCD WMI 提供者文件,網址是:go.microsoft.com/fwlink/?LinkId=116953。同時,我們還要示範針對多重開機電腦而設計的範例程式碼 — 所謂多重開機電腦,就是不只安裝一個作業系統的電腦。

對了,這些指令碼只能用在 Windows Vista 和 Windows Server 2008。之前我們也提過,那是因為它們兩個是目前唯一支援 BCD 的作業系統。

我們先來看看一個可以告訴我們電腦目前正在使用哪一個作業系統的指令碼。那的確不是 BCD 提供者最刺激的用法;畢竟我們已經可以使用 WMI 類別 Win32_OperatingSystem 來判斷電腦目前正在使用哪一個作業系統了。不過,這卻是相當簡單好用的指令碼 (至少到目前為止的 BCD 指令碼是如此),很適合用來示範使用 BCD 必備的基本技巧 (那些技巧至少都有那麼一點…不尋常)。

而且,我們也不好在專欄裡面描述太多刺激的內容:《TechNet Magazine》已經指示我們要為《嗨,Scripting Guy!》瘋狂粉絲所造成的任何危險事件負責到底。說到危險事件,自從我們發表以規則運算式為題的專欄之後,全球各地就陸續掀起受傷潮,讓我們深感困擾,實在是禁不起任何刺激了。

附註記不記得披頭四被粉絲包圍的影帶?還有當約翰藍儂、保羅麥卡里、喬治哈里森和林格史塔一露面時,引起現場女粉絲尖叫昏倒的照片?那正是我們每次一發表新專欄時的寫照。

好啦,也許我是有點誇大其詞啦。但是許多人 (不分男女) 在讀了我們的專欄之後,禁不住淚流滿面,泣不成聲,這話可是一點也不假。

不管怎樣,讓我們先來看看 [圖 1] 所示的指令碼,這個指令碼是使用 BCD 提供者來判斷電腦目前正在使用哪一個作業系統。在執行這個指令碼之前,應該先知道您必須以系統管理員的身分執行,否則指令碼就無法執行。

[圖 1] 取得目前的作業系統

Const BcdLibraryString_Description = &h12000004
Const Current = "{fa926493-6f1c-4193-a414-58f0b2456d1e}"

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore
objStore.OpenObject Current, objDefault

objDefault.GetElement BcdLibraryString_Description, objElement
Wscript.Echo "Current operating system: " & objElement.String

不過,這不表示您非得用系統管理員權限登入帳戶,而是您必須在 [開始] 功能表的 [命令提示字元] 按一下滑鼠右鍵,然後選取 [以系統管理員身分執行],開啟 [命令提示字元] 視窗。接著再從命令提示字元執行這個指令碼。

到底這個指令碼是做什麼用的呢?問得好,我們就是怕您問。不過無所謂啦,我們還是會盡可能地說清楚講明白 (這麼做應該可以把刺激感降到最低,至少可以冷卻一段時間吧)。

一開始我們先定義兩個常數:BcdLibraryString_Description 和 Current。BcdLibraryString_Description 是指我們要擷取的物件;在本例中,該物件包含了目前作業系統的描述 (名稱)。

對了,這是使用 BCD 提供者其中一項可愛的轉變。在一般情況下,您根本不必擷取屬性值,而是使用其中一個常數值 (和 GetElement 方法) 來擷取另一個物件,然後回應屬於該物件的屬性值。沒錯,這麼做是有點白目,不過您應該聽過一句話:旅遊的樂趣有一半是在旅途上。

附註您一定在想,他們的目的地到底是哪裡對吧?

以常數 Current 來說,目的地就是代表使用中作業系統的 GUID。該值

 {fa926493-6f1c-4193-a414-58f0b-2456d1e} 

被認為是「眾所周知」的 GUID (這句話證明 Microsoft 的人確實有點幽默感)。

雖然這個 GUID 已經名聞遐耳了,不過我們還是把它囊括進來,以免您之中的哪一位不記得下列哪一個才是正確的值:

{fa926493-6f1c-4193-a414-58f0b2456d1e} 
{fa926493-6f1c-4193-a414-58f0b2456d1f}

附註如果您好奇的話,GUID 其實可以唸成「gwid」或「goo-id」。我們還可以告訴您更多小撇步喔,像是第一個 GUID 必須是 V1 GUID,以及第三組數字如何以 4 開頭。不過,我們答應您一定會努力把刺激降到最低,而且我們說到做到。

您或許已經猜到了,無論在什麼場合,只要使用 BCD 提供者,就必須使用常數、十六進位值,以及 GUID。雖然本月份的專欄無法涵蓋所有的細節,不過您可以前往我們之前提過的,位於 MSDN® 的 BCD 網頁 (go.microsoft.com/fwlink/?LinkId=116953),查看其他資訊。這裡有的是刺激快感,只要您擋得住,就進去吧。

定義完兩個常數之後,接著就要連接本機電腦上的 WMI 服務。您可以使用這個指令碼來擷取遠端機器的開機設定資料嗎?當然可以。

老實說,如果不行的話,這個 BCD 就沒什麼價值了。如果要從遠端機器擷取開機設定資料 (同樣的,遠端機器必須執行 Windows Vista 或 Windows Server 2008),只要把那部機器的名稱指派給變數 strComputer 就行了:

strComputer = "atl-fs-001"

我們也會在我們的 WMI 連接字串中,指出幾個重要項目。一開始您也許已經注意到我們在連接字串囊括了備份和還原權限,這是 {(Backup,Restore)} 建構的做法。這一點很重要嗎?這個嘛,如果您希望指令碼能夠運作的話,這一點就很重要了:如果沒有明確加入這兩項權限,指令碼就無法運作。

第二點,請注意我們並未連接到 root\cimv2 命名空間 (這是系統管理指令碼最常用的命名空間),而是連接到 root\WMI 命名空間,直接連結 BCDStore 類別。這就是下面這一段程式碼的作用:

"\root\wmi:BcdStore" 

這還不算什麼,好戲還在後頭呢。

雖然偶而會有一兩個例外出現,但是您所寫的任何 BCD 指令碼,多半都是以下面三個步驟開頭:定義常數、連接到 WMI 服務,然後開啟 BCD 儲存區。我們已經完成第 1 步和第 2 步了;下面這行程式碼則是第 3 步:

objStoreClass.OpenStore "", objStore

根據我們之前的說法,接下來我們就要開啟 BCD 儲存區,這是儲存所有開機設定資訊的作業系統實體。如果要開啟儲存區,只要呼叫 OpenStore 方法,在方法中代入兩個參數即可:

  • 空字串 ("")。這是在告訴指令碼,我們要開啟預設的儲存區。
  • objStore。這是我們提供給指令碼的 "out" 參數。我們提供變數名稱給方法,而方法則提供一個以變數名稱作為物件參照的物件作為回報 (這個案例所提供的物件,是一個代表 BCD 儲存區的物件)。

只要儲存區一開啟,我們就可以使用 OpenObject 方法來擷取另一個物件 (儲存在 out 參數 objDefault 中):

objStore.OpenObject Current, objDefault

那麼這個新物件是什麼呢?沒錯:它就是目前正在使用的作業系統。這一點我們知道,因為我們在常數 Current 代入 OpenObject,也就是代表目前作業系統的知名 GUID。

好了,現在我們已經知道電腦目前所用的是哪一個作業系統了對吧?這個嘛,就快知道了。如果要得到這項資訊,還需要使用 GetElement 來擷取代表該作業系統的描述的物件:

objDefault.GetElement _
  BcdLibraryString_Description, objElement

不過,您得答應我不能駭到受傷才行。很好。那我們就告訴您,現在可以回應 String 屬性的值,並且 (終於!) 判斷電腦目前正在使用的是哪一個作業系統了:

Wscript.Echo "Current operating system: " _
  & objElement.String

請保持冷靜,拜託。記得您答應過我:不可以駭過頭。沒錯,我知道很難。您盡力就好。碰到這種情況時,就深吸一口氣,我們每次編寫 BCD 指令碼時,這一招都很管用。

就像我們所說,光是要判斷目前電腦正在使用哪一個作業系統,就夠我們傷腦筋的了;其實還有別的方法可以運用。但是,從好的方面來看,您現在已經瞭解 BCD 指令碼的運作方式,也就是說,您可以做一些指令碼撰寫人員一直做不到的事 (冷靜,保持冷靜)。舉個例說,在多重開機電腦上,永遠會有一個作業系統被標為「預設值」;如果電腦重新開機時,沒人另外特別註明,電腦就會自動載入預設的作業系統。在 Windows Vista (和 BCD 提供者) 啟動之前,指令碼作者根本無法判斷電腦上哪一個是預設作業系統。但是現在就跟執行 [圖 2] 所示的指令碼一樣簡單。

[圖 2] 判斷預設的作業系統

Const BcdLibraryString_Description = &h12000004
Const BootMgrId = "{9dea862c-5cdd-4e70-acc1-f32b344d4795}"
Const DefaultType = &h23000003

strComputer = "." 

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
    strComputer & "\root\wmi:BcdStore")
objStoreClass.OpenStore "", objStore

objStore.OpenObject BootMgrId, objBootMgr 

objBootMgr.GetElement DefaultType, objDefaultOSIdentifier
objStore.OpenObject objDefaultOSIdentifier.Id, objDefault

objDefault.GetElement BcdLibraryString_Description, objElement 
WScript.Echo "Default operating system: " & objElement.String

您說得沒錯:的確需要一點受傷事件點綴一下對吧?不過只要一點點就好,可以嗎?我們不打算詳細解說這個指令碼;假設您沒有跳著看前 10 個或 12 個段落,應該可以跟上邏輯。不過您也許會注意到,為了找出預設的作業系統,我們必須採取一個中間步驟:我們必須使用 OpenObject 方法,來開啟一個「開機管理程式」物件的執行個體。開啟開機管理程式之後,就可以使用常數 DefaultType 來擷取一個代表預設作業系統的物件。

怎麼樣?夠刺激吧:我們可以判斷目前的作業系統,同時也可以判斷預設的作業系統。但是真正勁爆的是什麼您知道嗎?那就是擷取一份所有安裝在電腦上的作業系統的清單。這才叫做刺激!繫好安全帶了,讓找們看看 [圖 3]

這裡到底要做什麼呢?這個嘛,一開始我們要定義兩個新常數:一個是 WindowsImages,可讓我們擷取所有支援 BCD 之作業系統的執行個體 (也就是 Windows Vista 和 Windows Server 2008);另一個是 LegacyImages,可讓我們擷取電腦上所有「舊版」作業系統的執行個體。連接到 BCD 儲存區之後,接著就使用 EnumerateObjects 方法來擷取電腦上所有支援 BCD 之作業系統的執行個體:

objStore.EnumerateObjects _
  WindowsImages, colObjects 

[圖 3] 尋找電腦上所有的作業系統

Const BcdLibraryString_Description = &h12000004
Const WindowsImages = &h10200003
Const LegacyImages = &h10300006

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore 

objStore.EnumerateObjects WindowsImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next
Wscript.Echo

objStore.EnumerateObjects LegacyImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next

待 EnumerateObjects 完成它自己的任務之後,我們就要安裝 For Each 迴圈,逐一執行集合中所有的作業系統。在這個迴圈當中,我們使用下面這兩行程式碼來擷取、然後顯示作業系統的描述:

objObject.GetElement _
  BcdLibraryString_Description, objElement 
Wscript.Echo objElement.String

接著在所有安裝在電腦上的舊版作業系統重複這個程序:

objStore.EnumerateObjects _
  LegacyImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement _
  BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next

附註我們非常瞭解您內心的激動,不過還是要請您耐住性子,好好執行 BCD 指令碼;人體撐得住的快感是有限的。那些懷孕或者可能懷孕、或者可能永遠不會懷孕、或者曾經懷孕過的女士,或者根本不是女士的人士,只要未先經過醫師檢查,千萬不要輕易執行 BCD 指令碼。

哎呀,好啦,您就不必經過醫師同意了。不過,如果您問醫生對於執行 BCD 指令碼有何看法時,我倒是很想聽聽他的回答。

接著我們要試試真正瘋狂的事了;看看能不能變更預設的作業系統。舉個例來說吧,假設我們有一部同時擁有 Windows Vista 和 Windows Server 2008 兩種作業系統的雙重開機電腦,而且我們想要把 Windows Vista 設為預設的作業系統。這時候該怎麼做呢?請看看 [圖 4],它為我們指出一條完成這項工作的途徑。

[圖 4] 變更預設的作業系統

Const BootMgrId = "{9dea862c-5cdd-4e70-accl-f32b344d4795}"
Const BcdLibraryString_Description = &h12000004
Const DefaultType = &h23000003
Const WindowsImages = &h10200003

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore 
objStore.EnumerateObjects WindowsImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 If Instr(objElement.String, "Vista") Then
  objStore.OpenObject BootMgrId, objBootMgr 
  objBootMgr.SetObjectElement DefaultType, objObject.ID 
 End If
Next

我們可以利用這個指令碼,再次開啟 BCD 儲存區,然後使用 EnumerateObjects 來擷取所有安裝在電腦上的 BCD 感知作業系統。在這裡我們要設定一個 For Each 迴圈,逐一執行集合中所有的項目,以下面這行目前已經熟悉的程式碼,擷取每一個作業系統的描述:

objObject.GetElement _
  BcdLibraryString_Description, objElement

只要一擷取到某個作業系統的描述,就利用 InStr function 看看該值當中有沒有出現 Vista 這個字眼:

If Instr(objElement.String, "Vista") Then

沒錯,這方法是有點笨拙。如果能夠使用 Windows Vista 的 GUID,就可以直接開啟那個作業系統,而不必逐一列舉並巡邏電腦上所有的作業系統了,堪稱是一個好方法。

只不過,要使用這個好方法,必須知道 Windows Vista 的 GUID。而採用這種方法,我們什麼都不用知道 (對於 Scripting Guy 來說真是一大福音);我們只要不斷搜尋,直到發現標題出現 Vista 這個字眼的作業系統就行了。

附註萬一您電腦上裝有多個 Windows Vista 執行個體的話,那該怎麼辦?嗯,這時候您可能得尋找像是 Vista Ultimate 或 Vista Enterprise 這樣的字串。

只要一找到 Windows Vista,就使用下面這兩行程式碼開啟 Windows 開機管理程式,然後將預設的作業系統設為…就設為 Windows Vista 吧:

objStore.OpenObject BootMgrId, objBootMgr 
objBootMgr.SetObjectElement _
  DefaultType, objObject.ID 

本月份我們只能講這麼多了;畢章就算是 Scripting Guy,也禁不起每週 7 天,每天 24 小時不停的駭翻天吧。不過別擔心,下個月我們會再推出另一個令人興奮的《嗨,Scripting Guy!》連載系列。運氣好的話,說不定那時候您已經恢復正常了。

Dr. Scripto 的指令碼謎題 (Scripting Perplexer)

這個每月一次的挑戰不僅測試您的解謎功力,更要測試您的指令碼技巧。

2008 年 7 月:VBScript 正方形

好吧,它們也許是長方形,而不是正方形啦,不過那是題外話了,暫且不談。要解開這個謎題,請把右手邊的每一個正方形,搭配左手邊的一個空正方形,建立 VBScript 函數的名稱。每一個正方形只能使用一次。請看看下面這個例子:

如果要正確解出這個範例,必須將 OU 正方形 (矩形) 移到第一個字當中的空正方形,然後將 MS 和 OX 移到第二個字當中的空正方形。這樣就得出 UBound 和 MsgBox 這兩個 VBScript 函數,如下所示:

現在就試試看吧:

答案是:

Dr. Scripto 的指令碼謎題 (Scripting Perplexer)

答案是:VBScript 正方形,2008 年 7 月

Scripting Guy 為 Microsoft 做事,也就是受雇於 Microsoft。他們在不玩、不教或不看棒球 (以及其他各種活動) 的時候,就負責管理 TechNet 指令碼中心。請造訪他們的網站:www.scriptingguys.com

© 2008 Microsoft Corporation and CMP Media, LLC.著作權所有,並保留一切權利。未經許可,不得部分或全部重製。