Scripting Guy 為您解答問題

Hey, Scripting Guy!

歡迎使用 TechNet 專欄,Microsoft Scripting Guy 會在此為您解答有關系統管理指令碼的常見問題。您有關於系統管理指令碼方面的問題嗎?請將電子郵件傳送到 scripter@microsoft.com。我們無法保證能夠逐一回答每個問題,不過我們會盡力而為。

資源

如何使用 Windows PowerShell 以便在應用程式終止時收到通知?

Hey, Scripting Guy! Question

嗨,Scripting Guy!我們有一套自製的應用程式,但是經常當機。一旦發生當機,我們就必須逐一執行冗長的程序才能重新啟動該程式。如果使用 Windows PowerShell,是否有任何方式可以在這個應用程式當機時收到通知?

        -- OI

Hey, Scripting Guy! Answer

OI,您好。其實,我們一開始想說:「嘿,我們今天才不藥回答問題」。而且我們真的噗要回答問題,絕對不要。不過,現在我們會蛔答囉。被搞迷糊了吧?

抱歉,有點離題了。這完全是因為今天早上我們興奮過頭了。還記得《綠野仙蹤》裡面,每個人圍著圈邊跳邊唱:「叮噹!女巫已死」的場景嗎?其實,我們感同身受:Scripting Editor 本週南下聖地牙哥參加 Microsoft Management Summit (MMS) 會議,因此我們至少得以從她恐怖的紅鉛筆統治中暫時解放。幾年以來,我們都一直在等待拼錯字的機會,而現在終於如願以償了。幾年以來,我們都一直在等待使用錯誤文法的機會,現在終於如願以償了。幾年以來,我們都一直在等待機會撰寫毫無意義的段落,哦,不,是完全毫無意義的專欄。現在終於可以了。好吧,其實我們原本就已經這樣做了。但是,至少我們可以撰寫無意義的專欄,而不用每隔兩分鐘就被她的:「這段話有什麼意義?」問題打斷。

來吧,大家一起唱:叮噹!女巫已死...

不好意思。我們還是有點興奮過頭。

有趣的是,多年以來我們一直想要做的另一件事,就是告訴您使用 Windows PowerShell 時,您可以在每次特定處理序終止時收到通知。不過,由於某些原因,Scripting Editor 不允許我們這麼做。但是,現在她再也無法阻止我們了。

請注意:邪惡?呃,邪惡是個相當強烈的字眼。不過,確實,我們必須承認她很邪惡。

我們發現,使用 Windows PowerShell 中的事件通知有點難以處理,因為 PowerShell 的 Get-WMIObject Cmdlet 不支援事件通知查詢(何謂事件通知查詢?如需詳細資訊,請參閱這個 Scripting Guy 網路廣播 (英文))。那麼,為什麼 Get-WMIObject 支援事件通知查詢呢?老實說,我們也不知道:因為某人從未讓我們深入研究這點。不過,目前為止,重點是我們無法使用好用的舊式 WMI 以便在每次處理序終止時收到通知。所以,我們必須改為運用一些其他可用的選項。

其中一個選項涉及了 Get-Process Cmdlet。在 OI 所描述的案例中,我們不需要在每次處理序終止時收到通知,我們只需要在特定處理序 (就我們的目的而言是 Calculator) 終止時收到通知。因此,我們只要使用三行程式碼就可以建立事件通知指令碼:

Script Center
$a = get-process calc
$a.waitforexit()
"Calculator has stopped running."

首先,我們要使用 Get-Process Cmdlet,將處理序與名稱 Calc 繫結在一起 (順道一提,此名稱是去除副檔名的可執行檔名稱)。請務必注意,我們在此假設只有一個含有名稱 Calc 的處理序。如果有多個執行中的 Calculator 執行個體該怎麼辦呢?嗯,如果是這種情況,第一行程式碼將會運作 (我們將取得包含所有這些 Calculator 執行個體的集合),但是指令碼將在第 2 行失敗。不過,請別煩惱:我們很快將說明一種較通用的處理序監視指令碼。

一旦我們取得 Calc 處理序的控制代碼之後,就可以呼叫 WaitForExit() 方法:

$a.waitforexit()

然後,這樣會產生什麼結果呢?這樣做會導致指令碼「封鎖」,表示指令碼只會在原地等候,直到上述處理序終止為止。當該處理序確實終止時,我們接著要使用最終的第三行程式碼來回應 Calculator 已經停止執行的事實。

至此,這個指令碼可正常運作。請您自行嘗試一下並查看結果。不過,這並不表示該指令碼很完美。例如,每次 Calculator 停止時,指令碼也會停止。這也就是說,您必須重新啟動 Calculator 與監視指令碼。

沒錯,這 就是 Scripting Editor 找事情讓您做的方式。但是請記住,Scripting Editor 不在這裡,這表示我們可以告訴您另一種方法:

$b = 1

do 
    {
        $a = get-process calc
        $a.waitforexit()
        Invoke-Item c:\windows\system32\calc.exe

    } 
while ($b -eq 1)

這裡的動作是要做什麼呢?我們要設定一個只要變數 $b 等於 1 就會執行的 Do 迴圈 (而且因為 $b 一定會等於 1,表示該迴圈會一直執行)。在迴圈內部,我們要再次繫結至 Calc 處理序並呼叫 WaitForExit() 方法。但是,請注意我們在 Calculator 終止時的做法。我們會使用 Invoke-Item Cmdlet 來重新啟動應用程式,而非回應訊息至螢幕:

Invoke-Item c:\windows\system32\calc.exe

一旦 Calculator 重新啟動之後,我們就要執行迴圈並繼續監視。我們不知道 OI 所謂重新啟動自製應用程式的「冗長程序」為何,但是如果能夠以程式設計方式進行此程序,這樣做可能是比較好的做法。當然,除了重新啟動應用程式以外,我們也可以透過回應訊息至螢幕或將注意事項寫入記錄檔或事件記錄等方式,回報 Calculator 必須重新啟動的事實。全看讀者的意思。

請注意:如之前所說的,上述指令碼會設計成一直執行。停止它的唯一方式就是終止 Windows PowerShell 的該特定執行個體。例如,您可以修改指令碼,以便在每次按下鍵盤的 Q 鍵時停止,但是由於時間有限,故無法說明此複雜程序。

如同上述第一個指令碼,只有當您擁有單一執行中目標處理序的執行個體時,複雜的重新啟動處理序指令碼才會運作。如果您擁有多個 Calculator 執行個體,就會發生問題。此外,這個指令碼只會使用處理序,因為 Get-Process Cmdlet 包含了 WaitForExit() 方法。但是,其他實體不一定適用於這種情況。

知道這一點之後,接下來讓我們看看一種較通用的事件監視指令碼,而這種指令碼會使用 .NET Framework。首先,我們將展示程式碼,然後我們會簡短地描述該指令碼如何運作:

$a = 0

$timespan = New-Object System.TimeSpan(0, 0, 1)
$scope = New-Object System.Management.ManagementScope("\\.\root\cimV2")
$query = New-Object System.Management.WQLEventQuery `
    ("__InstanceDeletionEvent",$timespan, "TargetInstance ISA 'Win32_Process'" )
$watcher = New-Object System.Management.ManagementEventWatcher($scope,$query)

do 
    {
        $b = $watcher.WaitForNextEvent()
        $b.TargetInstance.Name
    } 
while ($a -ne 1)

有了這個指令碼之後,我們就會每隔一秒檢查刪除的處理序。我們怎麼知道會每隔一秒檢查呢?這完全是因為我們設定 System.Timespan 物件的方式:

$timespan = New-Object System.TimeSpan(0, 0, 1)

如您所見,我們在建立新物件時,加入了三個參數:001。第一個 0 代表小時、第二個 0 代表分鐘,而 1 則代表秒鐘。如果想要每隔 15 分鐘 30 秒檢查刪除的處理序該怎麼辦?那麼,我們就要使用下面這行程式碼:

$timespan = New-Object System.TimeSpan(0, 15, 30)

那麼,我們怎麼知道會尋找已經刪除的處理序呢?這完全是因為我們設定 WQLEventQuery 時所要求的內容:

$query = New-Object System.Management.WQLEventQuery `
    ("__InstanceDeletionEvent",$timespan, "TargetInstance ISA 'Win32_Process'" )

知道了吧?我們會尋找 __InstanceDeletionEvent 類別的執行個體,但是只有當這些執行個體剛好是處理序 (亦即 Win32_Process 類別的成員) 時才會這樣做。

請注意:沒錯,這最多只粗略說明了 __InstanceDeletionEventTargetInstance 等詞彙。但是,如需詳細資料,您可以參閱上述-網路廣播 (英文)

建立 System.Management.ManagementEventWatcher 類別的執行個體之後,我們接著要進入另一個無止盡的迴圈並等候處理序終止。每次處理序結束時,我們要使用這行程式碼來回報處理序名稱

$b.TargetInstance.Name

當然,我們可能想得比較複雜,而且只有當終止的處理序剛好是 Calculator 的執行個體時,才會發出警示。但是,這得讓您自行修改。

喔,感謝您的詢問:事實上,Scripting Editor 打算要報告她參加 MMS 的行程內容。該份報告是否會跟本專欄一樣有趣呢?應該不見得。但是,至少每個字都會拼對。

輯小記:Scripting Editor 真的令人覺得吃驚,因為她隨身帶著水晶球 (有時稱為筆記型電腦),讓她能在遙遠的距離之外隨時查看其他 Scripting Guy 的一舉一動。我們將不會討論他們嘗試抵抗 Scripting Editor 的權力的後果,但是請放心,下場絕對悲慘。但是他們永遠學不到教訓...

請隨時留意 Script Center 中 Scripting Editor 對 Microsoft Management Summit 2007 的精彩報導 - 至少對聖地牙哥天氣的報導很精彩。