問:
您好,指令碼專家!如何記錄螢幕保護裝置程式的開始時間和結束時間?
-- JS
答:
您好,JS。您知道,一位指令碼專家(嘿,誰說“肯定是 Greg”?)年紀大得記得螢幕保護裝置程式剛出現的日子。那時,這類指令碼毫無意義。畢竟,螢幕保護裝置程式啟動後,每個人都神魂顛倒,從未想過讓它結束。事實上,作為電腦技術服務人員的這位指令碼專家首先必須做的一件事就是在每個人的案頭上建立捷徑,使他們能夠隨時啟動“飛轉的小烤爐”。
那時人們很容易得到快樂。
啊,但是活在過去沒有意義,對吧?在今天的現代社會中,顯然不僅需要停止螢幕保護裝置程式,還需要記錄停止的時間。明確這一點後,讓我們來看一看下面這個 WMI 事件監控指令碼,它會跟蹤螢幕保護裝置程式的每次啟動和停止:
複製代碼 代碼如下:strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objEventSource = objWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceOperationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'")
Do While True
Set objEventObject = objEventSource.NextEvent()
If Right(objEventObject.TargetInstance.Name, 4) = ".scr" Then
Select Case objEventObject.Path_.Class
Case "__InstanceCreationEvent"
Wscript.Echo "Screensaver " & objEventObject.TargetInstance.Name & _
" started: " & Now
Case "__InstanceDeletionEvent"
Wscript.Echo "Screensaver " & objEventObject.TargetInstance.Name & _
" ended: " & Now
End Select
End If
Loop
它看起來的確有點複雜,不是嗎?但是別慌:就設計而言,WMI 事件指令碼總是看起來有點複雜。幸運的是,這些指令碼只是看起來複雜;您會看到,這些指令碼實際上並不那麼難理解。
注意:好吧,我們最好對最後一句陳述加以限定:只要您瞭解 WMI 事件構成的基本思想,就不那麼難理解。如果您還不瞭解,最好花點時間看看指令碼編寫第 2 周網路廣播。這個網路廣播將為您提供了理解今天專欄文章所需的所有背景資訊。
好主意!儘管可能沒有有助於搞清楚我們的某一篇專欄文章的資訊,但至少有助於搞清楚這個指令碼代碼的意思。
這個特定指令碼的開始是以曆史悠久的方式串連到本機電腦上的 WMI 服務。通常到這裡,我們要執行 WMI 查詢以返回資訊。正如您所看到的,在這個指令碼中我們也要這麼做,只是查詢看起來有點不同:
Set objEventSource = objWMIService.ExecNotificationQuery _
("SELECT * FROM __InstanceOperationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'")
不用說,這不是您所習慣編寫的 WMI 查詢類型,因為我們調用的是 ExecNotificationQuery 方法,而不是 ExecQuery。(為什麼呢?因為要監控 WMI 事件,就必須使用 ExecNotificationQuery 方法。)今天我們無法詳細解釋這個查詢,但我們可以說,我們要求 WMI 只要有 WMI 事件(建立、刪除、修改)發生,就立刻通知我們。這裡只有一個問題:我們只想當 TargetInstance(建立、刪除或修改的項目)是 Win32_Process 類的執行個體時才得到通知。
注意:當然,從技術角度來說,還有第二個問題:我們只是每 5 秒鐘檢查一次新事件。如果螢幕保護裝置程式啟動,3 秒鐘之後結束,我們很可能就不會得到通知了。
換句話說,假設建立了一個新檔案。新檔案是 Win32_Process 類的執行個體嗎?不是;它是 CIM_DataFile 類的執行個體。因此,我們不想得到通知。假設修改了一個服務。我們想要得到通知嗎?不想要,因為服務是 Win32_Service 類的執行個體。好了,假設新進程(例如螢幕保護裝置程式)啟動。我們想要得到通知嗎?當然想要。別忘了,新進程可是 Win32_Process 類的執行個體。任何時候如果建立、刪除或修改進程,我們都想得到通知。
不過,這些您已經意識到了,對吧?
為了獲得這些通知,我們建立一個當 True 等於 True 時啟動並執行 Do 迴圈:
Do While True
句子的文法確實有點怪異,但這個文法卻能夠使指令碼不停運行,並且不停監控進程的建立、刪除和修改,直到終止指令碼或重新啟動電腦。如果沒有這樣的迴圈,指令碼會通知我們螢幕保護裝置程式何時啟動,但是,隨後指令碼就會結束。結果,我們永遠也不會得到螢幕保護裝置程式何時結束的通知。
在迴圈內,我們首先要做的就是執行下面這行代碼:
Set objEventObject = objEventSource.NextEvent()
我們所做的是告訴指令碼等待,直到下一個我們所關心的事件發生。換句話說,指令碼將停留在此行代碼上,直到有進程被建立、刪除或修改。假設進程始終不變,假設我們始終不建立、刪除或修改進程。在這種情況下,指令碼就會永遠停在這兒,耐心等待。以防萬一。
現在,我們知道您正在想什麼。您正在想:“嗨,稍等一下。我們只關心螢幕保護裝置程式。Microsoft Word 也在進程中運行。如果我們啟動 Microsoft Word,從而建立 Winword.exe 進程的新執行個體,那不也會觸發通知嗎?”
您說對了:會觸發通知。接下來這行代碼就用來解決這個問題。啟動 Word(或者任何可執行檔,就這一點而言)確實都會發出通知。但我們可以使用下面這行代碼解決這個問題:
If Right(objEventObject.TargetInstance.Name, 4) = ".scr" Then
在這裡,我們使用 Right 函數檢查觸發通知的進程的名稱。如果名稱中最右側的四個字元等於 .scr,我們便假定正在處理的是螢幕保護裝置程式,因為螢幕保護裝置程式的名稱類似 Marquee.scr。如果名稱中的最後四個字元不是 .scr,我們便只是迴圈一次,然後等待下一個事件發生。
那麼,如果最後四個字元是 .scr 會怎樣?在這種情況下,我們只關心兩種可能:螢幕保護裝置程式啟動或螢幕保護裝置程式結束。(我們並不關心是否有人修改螢幕保護裝置程式的屬性。)為處理這兩種可能,我們設定一個 Select Case 塊,用於檢查事件執行個體的 Class:
Select Case objEventObject.Path_.Class
如果 Class 等於 __InstanceCreationEvent,則意味著已建立新進程(即新螢幕保護裝置程式)。在第一個 Case 語句中,我們檢查 Class 是否等於 __InstanceCreationEvent。如果等於,我們便回顯如下事實:特定螢幕保護裝置程式(使用進程名稱表示)在特定時間(使用 VBScript 函數 Now)啟動:
Case "__InstanceCreationEvent"
Wscript.Echo "Screensaver " & objEventObject.TargetInstance.Name & " started: " & Now
意思清楚了,對吧?現在,假設螢幕保護裝置程式已結束,這就會導致刪除螢幕保護裝置程式進程。為處理這種可能,我們檢查 __InstanceDeletionEvent 類是否有新執行個體。如果發生屬於該類的事件(表示已刪除螢幕保護裝置程式進程),我們便回顯如下事實 - 指定的螢幕保護裝置程式在指定時間停止:
Case "__InstanceDeletionEvent"
Wscript.Echo "Screensaver " & objEventObject.TargetInstance.Name & " ended: " & Now
至此您已實現了您的目的。運行此指令碼後,會返回類似下面的資訊:
Screensaver Script Center.scr started: 2/9/2006 9:11:07 AM
Screensaver Script Center.scr ended: 2/9/2006 9:11:17 AM
注意:Script Center.scr 到底是什嗎?下載它,然後自己看。
我們還要補充兩件事。第一,最好在 Cscript 下的命令視窗中運行此指令碼,也就是說,要開始監控,請開啟命令視窗,然後鍵入類似下面的命令(當然,具體內容視指令碼名稱而定):
cscript screensaver_monitor.vbs
第二,正如我們前面所指出的,此指令碼設計為永遠運行。另一方面,什麼事都不會永遠持續下去,是吧?如果要停止監控,我們只需按 Ctrl+C,關閉命令視窗,或者終止 CScript.exe 進程。記住,指令碼專家決不會讓您陷於沒有出口的無限迴圈中。(您知道嗎:這對我們這的工作真是形容得非常恰當。)