Scripting Guy 為您解答問題

Hey, Scripting Guy!

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

資源

如何針對執行 Windows Vista 的電腦判斷寬限期?

Hey, Scripting Guy! Question

嗨,Scripting Guy!如何針對執行 Windows Vista 的電腦判斷剩餘的作業系統寬限期?


-- LD

Hey, Scripting Guy! Answer

LD,您好。您知道嗎?幾年以前,撰寫本專欄的 Scripting Guy 下班後走出公司時發現他的車無法發動。當時,他正擔任體育專欄作家,表示他有時候會工作得很晚。當然,剛好符合下列情況:時間是清晨 2 點、附近沒有任何人,而且他的車無法發動。

更糟糕的是,撰寫本專欄的 Scripting Guy 對車子的瞭解比指令碼還少。在不知所措的情況下,他打開了引擎蓋並看一看裡面。所有零件看起來都很好。不過事實上,就算有東西著了火,他也不會發現事情不對勁。就算是真的著火,他也可能假設:「嗯,那個東西可能本來就會起火」。

但是,Scripting Guy 覺得應該要採取行動,所以我們翻翻電線、關上引擎蓋,然後再次嘗試發動車子。讓他驚訝的是,車子居然發動了。當晚他不僅安全到家,而且發動車子時再也沒有發生問題。問題完全解決了!

與《嗨,Scripting Guy!》中出現的大部分故事不同的是,這個故事確實有個重點:即使您完全不知道自己在做什麼,有時候還是可以修好東西。指令碼和車子的情況都一樣。在查看引擎蓋底下並翻動一些電線之後,我們就能夠想出一種方式來判斷 Windows 產品啟動的剩餘寬限期。不過,我們無法確定地說出如何想出該解決方案,也無法區別所有意義。

我們遇到的問題是我們不完全瞭解 Windows Vista 授權 (沒錯,我們知道:我們可能世界上唯一不完全瞭解 Windows Vista 授權的人)。此外,Windows Vista 具有一項稱為「大量授權」的功能,而這項功能是針對如 Microsoft 之類的大型企業所設計。當我們在測試電腦上檢查授權和寬限期資訊時,得到類似下面內容的報告:



Windows Operating System - Vista, RETAIL channel
Grace period remaining (hours): 0
Licensed

Windows Operating System - Vista, OEM_COA_NSLP channel
Grace period remaining (hours): 0
Unlicensed

Windows Operating System - Vista, OEM_SLP channel
Grace period remaining (hours): 0
Unlicensed

Windows Operating System - Vista, RETAIL channel
Grace period remaining (hours): 0
Licensed

Windows Operating System - Vista, OEM_COA_SLP channel
Grace period remaining (hours): 0
Unlicensed


這是什麼意思呢?老實說,我們也不確定。此外,我們完全不確定在沒有使用「大量授權」的 Vista 電腦上,傳回的資料會呈現什麼內容。但是,編寫指令碼有一半的樂趣就在於閉上眼睛、執行指令碼,然後看看會發生什麼事。

請注意:難道 Scripting Guy 不用努力深入瞭解「大量授權」(尤其因為 Vista 包含一個軟體授權專用的龐大新 WMI 類別) 嗎?當然,我們會的,而且也打算這麼做。不過,在我們開始深入瞭解之前,您可能會想要自行花幾分鐘研讀一下大量授權SoftwareLicensingProduct 類別的相關資訊。


無論如何,在沒有做其他努力的情況下 (主要是因為我們沒有做任何其他努力),下面有一個可在 Windows Vista 電腦上判斷剩餘寬限期的指令碼:


strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colItems = objWMIService.ExecQuery _
    ("Select * From SoftwareLicensingProduct")

For Each objItem in colItems
    Wscript.Echo objItem.Description

    intGracePeriod = Int(objItem.GracePeriodRemaining / 60)
    Wscript.Echo "Grace period remaining (hours): " & intGracePeriod

    Select Case objItem.LicenseStatus
        Case 0 Wscript.Echo "Unlicensed"
        Case 1 Wscript.Echo "Licensed"
        Case 2 Wscript.Echo "Out-Of-Box Grace Period"
        Case 3 Wscript.Echo "Out-Of-Tolerance Grace Period"
        Case 4 Wscript.Echo "Non-Genuine Grace Period"
    End Select

    Wscript.Echo
Next


好吧,讓我們看看是否能稍微釐清這裡的內容。如您所見,我們一開始會連線到本機電腦上的 WMI 服務 (這個指令碼在遠端電腦上也能正常運作)。然後,我們會使用下面這行程式碼來擷取 SoftwareLicensingProduct 類別的所有執行個體 (在這裡稍微解釋一下,該類別會取代在 Windows XP 和 Windows Server 2003 中找到的 Win32_WindowsProductActivation 類別):


Set colItems = objWMIService.ExecQuery _
    ("Select * From SoftwareLicensingProduct")


沒問題吧。這個部分還滿簡單的,對吧?發出查詢之後,我們接著要設定 For Each 迴圈,以便逐一執行傳回集合中的所有項目。在該迴圈內部,第一件要做的事就是回應 Description 屬性的值:


Wscript.Echo "Description: " & objItem.Description


還是很簡單,對吧?好了,讓我們看一下 GracePeriodRemaining 屬性。結果顯示,這個屬性會在寬限期到期之前告知您剩餘時間 (以分鐘為單位) (或者,在大量授權用戶端上,再次以分鐘為單位,告知您需要重新啟動之前的剩餘時間)。因此,我們抓取這個屬性的值並將它除以 60,以便讓我們以小時為單位表示剩餘的寬限期:


intGracePeriod = Int(objItem.GracePeriodRemaining / 60)
Wscript.Echo "Grace period remaining (hours): " & intGracePeriod


您還想讓這個值以天為單位表示嗎?沒問題,只要改為除以 1440 就可以 (一小時 60 分鐘乘以一天 24 小時就會得出一天 1440 分鐘):


intGracePeriod = Int(objItem.GracePeriodRemaining / 1440)
Wscript.Echo "Grace period remaining (hours): " & intGracePeriod


到此為止是 LD 實際詢問的內容,但是我們決定深入一點並回報授權狀態。為了這樣做,我們僅使用一個 Select Case 陳述式,而此陳述式會根據 LicenseStatus 屬性的值採取動作:


Select Case objItem.LicenseStatus
    Case 0 Wscript.Echo "Unlicensed"
    Case 1 Wscript.Echo "Licensed"
    Case 2 Wscript.Echo "Out-of-Box Grace Period"
    Case 3 Wscript.Echo "Out-of-Tolerance Grace Period"
    Case 4 Wscript.Echo "Non-Genuine Grace Period"
End Select


程式碼本身其實不會非常複雜。LicenseStatus 會傳回 0、1、2、3 或 4 的值。我們所做的只是查看此值,然後回應實際的授權狀態而已。例如,如果 LicenseStatus 等於 2,就表示作業系統處於「立即可用寬限期」中。然後,我們就會適當地回應該事實:


Case 2 Wscript.Echo "Out-of-Box Grace Period"


那麼,處於「立即可用寬限期」中是什麼意思?有哪些影響?呃,這個問題在於:我們也不知道。運氣好的話,您可以在這裡找到答案。

LD,我們希望本文內容能指引您正確的方向。當我們今晚一回家,就會開始深入研究 Vista 授權。

當然,這是假設我們能讓車子發動而且可以真的回家的情況。嗯,不知道接下來會發生什麼壞事,如果我們翻動一下電 --

使用 Windows PowerShell 判斷寬限期

雖然我們無法保證,但是會盡可能在《嗨,Scripting Guy!》專欄中提供常用的 Windows PowerShell 解決方案與 VBScript 解決方案。下面是 PowerShell 專家 June Blender 所提供的解決方案範例。

我知道這項工作可以輕易地在 Windows PowerShell 中完成,因為 PowerShell 可以存取任何 WMI 類別(雖然有一些警告,但是我想這個情況不適用)。而且,Scripting Guy 已完成了所有困難的工作:他判斷出要使用哪個 WMI 類別而且發現了要顯示的重要屬性。

下面是範例 Windows PowerShell 命令,而這則命令會顯示 Windows Vista 授權的寬限期:

get-wmiobject -class SoftwareLicensingProduct | format-List -property Name, Description, 'GracePeriodRemaining, LicenseStatus


下面是更複雜的版本,它會以天數而非分鐘來計算寬限期,並將 LicenseStatus 屬性整數值轉譯成它們代表的意義:

$L = get-wmiobject -class SoftwareLicensingProduct
$L | format-list -property Name, Description, `
             @{Label="Grace period (days)"; Expression={ $_.graceperiodremaining / 1440}}, `
             @{Label= "License Status"; Expression={switch (foreach {$_.LicenseStatus}) `
              { 0 {"Unlicensed"} `
                1 {"Licensed"} `
                2 {"Out-Of-Box Grace Period"} `
                3 {"Out-Of-Tolerance Grace Period"} `
                4 {"Non-Genuine Grace Period"} `
              } } }


輸出內容如下。雖然與 VBScript 輸出內容並非完全相同,但是很相似:

Name                : Windows(TM) Vista, Enterprise edition
Description         : Windows Operating System - Vista, VOLUME_KMS channel
Grace period (days) : 0
License Status      : Unlicensed

Name                : Windows(TM) Vista, Enterprise edition
Description         : Windows Operating System - Vista, VOLUME_MAK channel
Grace period (days) : 0
License Status      : Unlicensed

Name                : Windows(TM) Vista, OCUR add-on for Ultimate,HomePremium
Description         : Windows Operating System - Vista, RETAIL channel
Grace period (days) : 0
License Status      : Unlicensed

Name                : Windows(TM) Vista, Enterprise edition
Description         : Windows Operating System - Vista, RETAIL channel
Grace period (days) : 0
License Status      : Unlicensed

Name                : Windows(TM) Vista, Enterprise edition
Description         : Windows Operating System - Vista, VOLUME_KMSCLIENT channe
                      l
Grace period (days) : 173.5
License Status      : Licensed

Name                : Windows(TM) Vista, Enterprise edition
Description         : Windows Operating System - Vista, OEM_SLP channel
Grace period (days) : 0
License Status      : Unlicensed


Windows PowerShell 會讓這項格外複雜的工作變得相當簡單。Get-WmiObject cmdlet 具有一個 Class 參數 (-class),可讓您指定任何 WMI 類別。Scripting Guy 告訴我們要使用 SoftwareLicensingProduct 類別,而且我們永遠遵照他的話。我將輸出儲存在名為 $L (代表「授權」) 的變數中:

$L = get-wmiobject -class SoftwareLicensingProduct


至此,我們將需要的所有資料都儲存在 $L 變數中。接下來要做的就是格式化輸出。我選擇將資料格式化成清單,因為表格會截斷過長的值。

Format-List cmdlet 具有一個 Property 參數 (-property),可讓您選取清單中顯示的屬性。而且,我複製了 Scripting Guy 在他的指令碼中使用的屬性:

format-List -property Name, Description, GracePeriodRemaining, LicenseStatus


雖然這個命令是設計來尋找本機電腦的寬限期,但是我可以輕易地加入 Get-WmiObject 的 ComputerName 參數,以便在遠端電腦上執行相同的參數,例如名為 Vista2 的電腦。

get-wmiobject -class SoftwareLicensingProduct -computername Vista2 | format-List Name, Description, GracePeriodRemaining, LicenseStatus


雖然完成了這項工作,但是我仍然對這個新的 SoftwareLicensingProduct 物件很好奇。或許,只是或許,Scripting Guy 並未展示此類別的所有酷炫屬性。或者,這個類別中的物件可能具有我日後要使用的方法。

因為我擁有物件,而非只是字串,所以不需要再次擷取資料。我只要重新格式化顯示即可。首先,我要查看在這個顯示中沒有使用的屬性。

我可以使用 Get-Member cmdlet,輕易地在 $L 中檢視物件的所有屬性和方法。在這則命令中,我使用了管線運算子 (|),將 $L 變數中的物件傳送至 Get-Member:

$L | get-member


而結果如下:SoftwareLicensingProduct 物件的屬性和方法。

TypeName: System.Management.ManagementObject#root\cimv2\SoftwareLicensingPro
duct

Name                         MemberType   Definition
----                         ----------   ----------
Activate                     Method       System.Management.ManagementBaseOb...
DepositOfflineConfirmationId Method       System.Management.ManagementBaseOb...
UninstallProductKey          Method       System.Management.ManagementBaseOb...
ApplicationID                Property     System.String ApplicationID {get;s...
Description                  Property     System.String Description {get;set;}
EvaluationEndDate            Property     System.String EvaluationEndDate {g...
GracePeriodRemaining         Property     System.UInt32 GracePeriodRemaining...
ID                           Property     System.String ID {get;set;}
LicenseStatus                Property     System.UInt32 LicenseStatus {get;s...
MachineURL                   Property     System.String MachineURL {get;set;}
Name                         Property     System.String Name {get;set;}
OfflineInstallationId        Property     System.String OfflineInstallationI...
PartialProductKey            Property     System.String PartialProductKey {g...
ProcessorURL                 Property     System.String ProcessorURL {get;set;}
ProductKeyID                 Property     System.String ProductKeyID {get;set;}
ProductKeyURL                Property     System.String ProductKeyURL {get;s...
UseLicenseURL                Property     System.String UseLicenseURL {get;s...
__CLASS                      Property     System.String __CLASS {get;set;}
__DERIVATION                 Property     System.String[] __DERIVATION {get;...
__DYNASTY                    Property     System.String __DYNASTY {get;set;}
__GENUS                      Property     System.Int32 __GENUS {get;set;}
__NAMESPACE                  Property     System.String __NAMESPACE {get;set;}
__PATH                       Property     System.String __PATH {get;set;}
__PROPERTY_COUNT             Property     System.Int32 __PROPERTY_COUNT {get...
__RELPATH                    Property     System.String __RELPATH {get;set;}
__SERVER                     Property     System.String __SERVER {get;set;}
__SUPERCLASS                 Property     System.String __SUPERCLASS {get;set;}
ConvertFromDateTime          ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime            ScriptMethod System.Object ConvertToDateTime();
Delete                       ScriptMethod System.Object Delete();
GetType                      ScriptMethod System.Object GetType();
Put                          ScriptMethod System.Object Put();


嗯,Activate 和 UninstallProductKey 方法,以及 OfflineInstallationID 屬性。好吧,這些項目很重要,但是我需要查看範例。

因為 Get-WmiObject cmdlet 會擷取一個以上的物件,所以我實際上擁有一個群組或物件的陣列。我可以使用 . (這是一個點) 屬性運算子和陣列的 Count 屬性來找出我擁有的物件數目。

$L.count
6


而且,我發現我有六個物件。就單一顯示而言,數目有點太多了。

我會選取其中最重要的物件 (含有授權的物件),然後顯示其屬性值。這則命令會使用 Where-Object cmdlet (別名 = "where") 來選取其中一個物件,也就是在說明文字中含有 "client" 一詞的物件。我會再次使用 Format-List cmdlet 並使用其 Property 參數搭配 all (*) 的值。

$L | where {$_.description -like "*client*"} | format-list -property *

__GENUS               : 2
__CLASS               : SoftwareLicensingProduct
__SUPERCLASS          :
__DYNASTY             : SoftwareLicensingProduct
__RELPATH             : SoftwareLicensingProduct.ID="cfd8ff08-c0d7-452b-9f60-ef
                        5c70c32094"
__PROPERTY_COUNT      : 14
__DERIVATION          : {}
__SERVER              : JUNEB-TESTV
__NAMESPACE           : root\cimv2
__PATH                : \\JUNEB-TESTV\root\cimv2:SoftwareLicensingProduct.ID="c
                        fd8ff08-c0d7-452b-9f60-ef5c70c32094"
ApplicationID         : 55c92734-d682-4d71-983e-d6ec3f16059f
Description           : Windows Operating System - Vista, VOLUME_KMSCLIENT chan
                        nel
EvaluationEndDate     : 16010101000000.000000-000
GracePeriodRemaining  : 249840
ID                    : cfd8ff08-c0d7-452b-9f60-ef5c70c32094
LicenseStatus         : 1
MachineURL            : http://go.microsoft.com/fwlink/?LinkId=57203
Name                  : Windows(TM) Vista, Enterprise edition
OfflineInstallationId : 013421174935129263064954303550341742525985120465630192
PartialProductKey     : 4BWMV
ProcessorURL          : http://go.microsoft.com/fwlink/?LinkId=57201
ProductKeyID          : 89579-00142-236-020020-03-1033-6000.0000-0292007
ProductKeyURL         : http://go.microsoft.com/fwlink/?LinkId=57204
UseLicenseURL         : http://go.microsoft.com/fwlink/?LinkId=572


現在,我只要變更 Format-List 命令,就可以顯示某些其他屬性。這次,我要將 $L 變數中的物件傳送至含有不同屬性集合的 Format-List 命令:

$L | format-List Name, Description, GracePeriodRemaining, LicenseStatus, __SERVER, ProductKeyURL


結果如下:

Name                 : Windows(TM) Vista, Enterprise edition
Description          : Windows Operating System - Vista, VOLUME_KMS channel
GracePeriodRemaining : 0
LicenseStatus        : 0
__SERVER             : JUNEB-TESTV
ProductKeyURL        : http://go.microsoft.com/fwlink/?LinkId=57204

Name                 : Windows(TM) Vista, Enterprise edition
Description          : Windows Operating System - Vista, VOLUME_MAK channel
GracePeriodRemaining : 0
LicenseStatus        : 0
__SERVER             : JUNEB-TESTV
ProductKeyURL        : http://go.microsoft.com/fwlink/?LinkId=57204

Name                 : Windows(TM) Vista, OCUR add-on for Ultimate,HomePremium
Description          : Windows Operating System - Vista, RETAIL channel
GracePeriodRemaining : 0
LicenseStatus        : 0
__SERVER             : JUNEB-TESTV
ProductKeyURL        : http://go.microsoft.com/fwlink/?LinkId=57204

Name                 : Windows(TM) Vista, Enterprise edition
Description          : Windows Operating System - Vista, RETAIL channel
GracePeriodRemaining : 0
LicenseStatus        : 0
__SERVER             : JUNEB-TESTV
ProductKeyURL        : http://go.microsoft.com/fwlink/?LinkId=57204

Name                 : Windows(TM) Vista, Enterprise edition
Description          : Windows Operating System - Vista, VOLUME_KMSCLIENT chann
                       el
GracePeriodRemaining : 258900
LicenseStatus        : 1
__SERVER             : JUNEB-TESTV
ProductKeyURL        : http://go.microsoft.com/fwlink/?LinkId=57204

Name                 : Windows(TM) Vista, Enterprise edition
Description          : Windows Operating System - Vista, OEM_SLP channel
GracePeriodRemaining : 0
LicenseStatus        : 0
__SERVER             : JUNEB-TESTV
ProductKeyURL        : http://go.microsoft.com/fwlink/?LinkId=57204


好吧,在這種情況下,這樣做並不會更有用。我必須再次向 Scripting Guy 承認失敗。

最後一個問題是我如何在第二個命令版本中管理複雜的格式:

$L = get-wmiobject -class SoftwareLicensingProduct
$L | format-list Name, Description, `
             @{Label="Grace period (days)"; Expression={ $_.graceperiodremaining / 1440}}, `
             @{Label= "License Status"; Expression={switch (foreach {$_.LicenseStatus}) `
              { 0 {"Unlicensed"} `
                1 {"Licensed"} `
                2 {"Out-Of-Box Grace Period"} `
                3 {"Out-Of-Tolerance Grace Period"} `
                4 {"Non-Genuine Grace Period"} `
              } } }


為了在顯示屬性值之前操作屬性值,我使用了計算的屬性。這會採用「雜湊表」的形式 (正式呼叫關聯的陣列來避免任何不適當的提示)。每份雜湊表都可以具有 Label、Expression、Format 和 Alignment 機碼。您可以使用它們搭配 Select-Object 和格式化 Cmdlet (Format-*)。例如,請參閱 "get-help format-table -examples"。

格式如下:

@{<key>=<value>; [<key>=<value>...]}


例如:

@{Label="Name"; Expression={$_.Property * Int}}


(麻煩的部分是要記住正確的括號和大括號數目...)

在這個情況下,為了將 GracePeriodRemaining 屬性的值從分鐘轉換成天數,我將該值除以 1440 (一天的分鐘數)。此運算式會以 $_ (代表目前物件的符號) 為開頭。每當您顯示一個以上的物件時 (在此情況下,其中 6 個物件),就需要這個符號。

@{Label=" Grace period (days)"; Expression={$_.graceperiodremaining / 1440}}


為了將 LicenseStatus 屬性的整數值變更為有意義的名稱,我在 Expression 機碼值中輸入了 switch 陳述式。Windows PowerShell 會使用相當標準的 switch 陳述式形式。如需 PowerShell 中 switch 陳述式的詳細說明,請在 Windows PowerShell 命令提示字元中,輸入 "get-help about_switch"。

這些簡單命令的功能可能非常強大。再強調一次,技巧在於尋找 WMI 的 SoftwareLicensingProduct 類別。在此情況下,我跟許多人一樣,仰賴令人景仰的 Scripting Guy,但是如果他去參加棒球賽 (我是說,參加重要的會議),您就可以使用 Get-WmiObject 的 List 參數來列出所有可用的 WMI 類別。

get-wmiobject -list


我將繼續仰賴 Scripting Guy。

顯示: