'*************************************************************************
'* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
'* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
'* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
'* PARTICULAR PURPOSE.
'*
'* Note: this is not an unsupported script and has not undergone testing. Please use this script at your own risk.
'* Microsoft’s Customer Support Services (CSS/PSS) will not support issues associated with this
'*
'* Found at:
http://blogs.msdn.com/clustering
'*
'* Copyright (C) 1993 - 2010. Microsoft Corporation. All rights reserved.
'*
'* File: EnumPatchesReg.vbs
'*
'*
'* Version: 090518.003 - Minor fixes to the help/usage
'*
'* Version: 090423.002 - Added enumeration of QFEs for consistency.
'* now all (and not only suspected) patches are
'* enumerated.
'*
'* Version: 090418.001 - Initial version
'*
'* Date: 05/01/2009
'*
'* Author: Guy Teverovsky
'*
'* Purpose: Displays details for installed patches/hotfixes
'*
'* Usage: cscript EnumPatchesReg.vbs [/computer:<NAME>] [/id:<GUID|KB>]
'*
'* /computer:<NAME> The name of the computer to query
'* /id:<GUID|KB> The GUID or KB of the hotfix to query
'*
'* Running the script without parameters will enumerate all
'* the patches installed on computer the script is executed on
'*
'* Example: cscript EnumPatchesReg.vbs /id:kb968220 /computer:myserver"
'* Example: cscript EnumPatchesReg.vbs /id:{47740627-D81D-4A45-A215-03B075A18EC7}
'*
'*
'* Requires: Windows Scripting Host 5.6 or above, Windows 2008/Vista
'*************************************************************************
'========================================================
'Function List:
'
' DecodePatchState()
' EnumerateQFEs
' EnumProductPatches()
' GetPatchInfo()
' IsCscript()
' ShowHelp()
' StringToGUID()
' SwapCharsByPairs()
'========================================================
Option Explicit
'********************************************************************
'Define constants for the registry operations
'********************************************************************
Const HKEY_LOCAL_MACHINE = &H80000002
Const REG_PRODUCTS_SEARCH_BASE = "SOFTWAREMicrosoftWindowsCurrentVersionInstallerUserDataS-1-5-18Products"
Dim g_strComputer
Dim g_strHotfixId
Dim g_objReg
Dim g_arrProductsKeys, g_strProductKey
Dim g_objAllPatches, g_strKey
Dim g_objWMIService
'********************************************************************
'* Identify if we are using CScript as the Host.
'********************************************************************
If Not IsCscript Then
Wscript.Echo "Please use Cscript as the script host."
Wscript.Echo "Cscript " & WScript.ScriptName
Wscript.Quit
End If
'********************************************************************
'* Display Usage if called with /? parameter
'********************************************************************
If WScript.Arguments.Count = 1 Then
If WScript.Arguments(0) = "/?" Then
ShowHelp
WScript.Quit 0
End If
End If
'********************************************************************
'* Determine the target computer
'********************************************************************
If WScript.Arguments.Named.Item("computer") <> "" Then
g_strComputer = WScript.Arguments.Named.Item("computer")
Else
g_strComputer = "."
End If
'********************************************************************
'* Connect to the registry WMI provider on target computer
'********************************************************************
Set g_objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\" &_
g_strComputer & "rootdefault:StdRegProv")
Set g_objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\" & _
g_strComputer & "rootcimv2")
'********************************************************************
'* Initialize dictionary object to store the found patches
'********************************************************************
Set g_objAllPatches = CreateObject("Scripting.Dictionary")
'********************************************************************
'* Enumerate the products registry key and loop through it
'********************************************************************
g_objReg.EnumKey HKEY_LOCAL_MACHINE, REG_PRODUCTS_SEARCH_BASE, g_arrProductsKeys
For Each g_strProductKey In g_arrProductsKeys
EnumProductPatches g_strProductKey
Next
'********************************************************************
'* Enumerate installed QFEs
'********************************************************************
EnumerateQFEs
'********************************************************************
'* Provide results
'********************************************************************
If WScript.Arguments.Named.Item("id") <> "" Then
g_strHotfixId = UCase(WScript.Arguments.Named.Item("id"))
If Len(g_strHotfixId) = 8 And UCase(Left(g_strHotfixId,2)) = "KB" Then
g_strHotfixId = Mid(g_strHotfixId,3)
End If
If g_objAllPatches.Exists(g_strHotfixId) Then
WScript.Echo
WScript.Echo g_objAllPatches.Item(g_strHotfixId)
ElseIf g_objAllPatches.Exists("KB" & g_strHotfixId) Then
WScript.Echo
WScript.Echo g_objAllPatches.Item("KB" & g_strHotfixId)
Else
WScript.Echo "Failed to locate patch with ID " & g_strHotfixId
End If
Else
WScript.Echo
For Each g_strKey In g_objAllPatches.Keys
WScript.Echo String(79,"*")
WScript.Echo g_objAllPatches.Item(g_strKey)
Next
End If
'********************************************************************
'* Finalize
'********************************************************************
Set g_objAllPatches = Nothing
Set g_objReg = Nothing
Set g_objWMIService = Nothing
'********************************************************************
'*
'* Function EnumerateQFEs()
'*
'* Purpose: Enumerate all installed QFEs and add those that have
'* not been eumerated already using registry
'*
'* Input: None
'*
'* Output: None
'*
'********************************************************************
Function EnumerateQFEs
Dim colQFEs
Dim objQFE
Dim strQFEInfo
Set colQFEs = g_objWMIService.ExecQuery("SELECT * FROM Win32_QuickFixEngineering")
For Each objQFE In colQFEs
If Not g_objAllPatches.Exists(objQFE.HotfixID) Then
strQFEInfo = "HotfixId: " & objQFE.HotfixId & VbCrLf & _
"Caption: " & objQFE.Caption & VbCrLf & _
"Description: " & objQFE.Description
g_objAllPatches.Add objQFE.HotfixId, strQFEInfo
End If
Next
End Function
'********************************************************************
'*
'* Function EnumProductPatches()
'*
'* Purpose: Find patches for a given product
'*
'* Input: strProductKey - name of product key in the registry
'*
'* Output: None
'*
'********************************************************************
Function EnumProductPatches(strProductKey)
Dim arrPatchKeys, strPatchKey
Dim strPatchRegKey
strPatchRegKey = REG_PRODUCTS_SEARCH_BASE & "" & strProductKey & "Patches"
g_objReg.EnumKey HKEY_LOCAL_MACHINE, strPatchRegKey, arrPatchKeys
If Not IsNull(arrPatchKeys) Then
For Each strPatchKey In arrPatchKeys
GetPatchInfo strProductKey, strPatchKey
Next
End If
End Function
'********************************************************************
'*
'* Function GetPatchInfo()
'*
'* Purpose: Pulls information from registry for a specific patch
'*
'* Input: strProductKey - name of product key in the registry
'* strPatchKey - name of patch key in the registry
'*
'* Output: Patch details as string
'*
'********************************************************************
Function GetPatchInfo(strProductKey, strPatchKey)
Dim strPatchInfoKey, strProductInfoKey, strPatchGUID, strProductGUID
Dim strPatchName, strPatchMoreInfoURL, strPatchInstallDate
Dim strPatchType, strPatchState, strProductName
Dim dwPatchState
Dim strPatchInfo
strPatchGUID = StringToGUID(strPatchKey)
strProductGUID = StringToGUID(strProductKey)
strProductInfoKey = REG_PRODUCTS_SEARCH_BASE & "" & _
strProductKey & "InstallProperties"
strPatchInfoKey = REG_PRODUCTS_SEARCH_BASE & "" & _
strProductKey & "Patches" & strPatchKey
g_objReg.GetStringValue HKEY_LOCAL_MACHINE, strPatchInfoKey, "DisplayName", strPatchName
g_objReg.GetStringValue HKEY_LOCAL_MACHINE, strPatchInfoKey, "MoreInfoURL", strPatchMoreInfoURL
g_objReg.GetStringValue HKEY_LOCAL_MACHINE, strPatchInfoKey, "Installed", strPatchInstallDate
g_objReg.GetDWORDValue HKEY_LOCAL_MACHINE, strPatchInfoKey, "State", dwPatchState
g_objReg.GetStringValue HKEY_LOCAL_MACHINE, strProductInfoKey, "DisplayName", strProductName
strPatchInfo = "Patch Name: " & strPatchName & VbCrLf & _
"Patch Code: " & strPatchGUID & VbCrLf & _
"More Info URL: " & strPatchMoreInfoURL & VbCrLf & _
"Patch State: " & DecodePatchState(dwPatchState) & VbCrLf & _
"Install Date: " & strPatchInstallDate & VbCrLf & _
"Product Name: " & strProductName & VbCrLf & _
"Product Code: " & strProductGUID
If Not g_objAllPatches.Exists(strPatchGUID) Then
g_objAllPatches.Add strPatchGUID, strPatchInfo
End If
End Function
'********************************************************************
'*
'* Function DecodePatchState()
'*
'* Purpose: Translate patch state code to human readable form
'*
'* Input: state - numeric representation of the patch state
'*
'* Output: Patch state as string (Installed/Superseded/Obsolete)
'*
'********************************************************************
Function DecodePatchState(state)
Select Case state
Case 1: DecodePatchState = "Installed"
Case 2: DecodePatchState = "Superseded"
Case 4: DecodePatchState = "Obsolete"
Case Else: DecodePatchState = cstr(state)
End Select
End Function
'********************************************************************
'*
'* Function StringToGUID()
'*
'* Purpose: Converts the GUID as it is represented in registry
'* to the form we are used to deal with
'*
'* Input: strString - GUID as it is represented in registry
'*
'* Output: GUID in its standard form
'* i.e.: {47740627-D81D-4A45-A215-03B075A18EC7}
'*
'********************************************************************
Function StringToGUID(strString)
Dim arrGUIDParts(6)
Dim strGUID
If Len(strstring) <> 32 Then
StringToGUID = ""
Exit Function
End If
arrGUIDParts(0) = Mid(strString,1,8)
arrGUIDParts(1) = Mid(strString,9,4)
arrGUIDParts(2) = Mid(strString,13,4)
arrGUIDParts(3) = Mid(strString,17,4)
arrGUIDParts(4) = Mid(strString,21,12)
arrGUIDParts(0) = StrReverse(arrGUIDParts(0))
arrGUIDParts(1) = StrReverse(arrGUIDParts(1))
arrGUIDParts(2) = StrReverse(arrGUIDParts(2))
arrGUIDParts(3) = SwapCharsByPairs(arrGUIDParts(3))
arrGUIDParts(4) = SwapCharsByPairs(arrGUIDParts(4))
StringToGUID = "{" & arrGUIDParts(0) & "-" & _
arrGUIDParts(1) & "-" & _
arrGUIDParts(2) & "-" & _
arrGUIDParts(3) & "-" & _
arrGUIDParts(4) & "}"
End Function
'********************************************************************
'*
'* Function SwapCharsByPairs()
'*
'* Purpose: Helper function for operations on strings
'*
'* Input: strString - some text
'*
'* Output: text after some logic applied
'*
'********************************************************************
Function SwapCharsByPairs(strString)
Dim i, arrChars()
Dim intStrLen : intStrLen = Len(strString)
If intStrLen < 2 Then
SwapCharsByPairs = strString
Exit Function
End If
ReDim arrChars(intStrLen - 1)
For i = 0 To intStrLen - 2
arrChars(i) = Mid(strString,i+2,1)
arrChars(i+1) = Mid(strString,i+1,1)
i = i + 1
Next
SwapCharsByPairs = Join(arrChars,"")
End Function
'********************************************************************
'*
'* Function ShowHelp()
'*
'* Purpose: Shows script usage/help
'*
'* Input: None
'*
'* Output: None
'*
'********************************************************************
Function ShowHelp
WScript.Echo
WScript.Echo " Displays details for installed patches/hotfixes"
WScript.Echo
WScript.Echo " Usage: cscript " & WScript.ScriptName & " [/computer:<NAME>] [/id:<GUID|KB>]"
WScript.Echo
WScript.Echo " /computer:<NAME> The name of the computer to query. If the parameter"
WScript.Echo " is not specified, the script will default to localhost"
WScript.Echo " /id:<GUID|KB> The GUID or KB of the hotfix to query."
WScript.Echo " If parameter is not specified, the script will enumerate"
WScript.Echo " all the patches installed."
WScript.Echo
WScript.Echo " Running the script without parameters will enumerate all"
WScript.Echo " the patches installed on computer the script is executed on."
WScript.Echo
WScript.Echo " Example: cscript EnumPatchesReg.vbs /id:kb968220 /computer:myserver"
WScript.Echo " Example: cscript EnumPatchesReg.vbs /id:{47740627-D81D-4A45-A215-03B075A18EC7}"
WScript.Echo
End Function
'********************************************************************
'*
'* Function IsCscript()
'*
'* Purpose: Determines which program is used to run this script.
'*
'* Input: None
'*
'* Output: Return True if the script host is Cscript.
'*
'********************************************************************
Function IsCscript()
On Error Resume Next
'********************************************************************
'Define constants
'********************************************************************
CONST CONST_ERROR = 0
CONST CONST_WSCRIPT = 1
CONST CONST_CSCRIPT = 2
CONST CONST_SHOW_USAGE = 3
CONST CONST_LIST = 4
Dim strFullName, strCommand, i, j, intStatus
Err.Clear
strFullName = WScript.FullName
If Err.Number then
Call Wscript.Echo( "Error 0x" & CStr(Hex(Err.Number)) & " occurred." )
If Err.Description <> "" Then
Call Wscript.Echo( "Error description: " & Err.Description & "." )
End If
intStatus = CONST_ERROR
End If
i = InStr(1, strFullName, ".exe", 1)
If i = 0 Then
intStatus = CONST_ERROR
Else
j = InStrRev(strFullName, "", i, 1)
If j = 0 Then
intStatus = CONST_ERROR
Else
strCommand = Mid(strFullName, j+1, i-j-1)
Select Case LCase(strCommand)
Case "cscript"
intStatus = CONST_CSCRIPT
Case "wscript"
intStatus = CONST_WSCRIPT
Case Else 'should never happen
Call Wscript.Echo( "An unexpected program was used to " _
& "run this script." )
Call Wscript.Echo( "Only CScript.Exe or WScript.Exe can " _
& "be used to run this script." )
intStatus = CONST_ERROR
End Select
End If
End If
If intStatus <> CONST_CSCRIPT Then
IsCscript = False
Else
IsCscript = True
End If
On Error GoTo 0
End Function