Blog Post

Failover Clustering
7 MIN READ

EnumPatchesReg.vbs

John Marlin's avatar
John Marlin
Icon for Microsoft rankMicrosoft
Mar 15, 2019
First published on MSDN on Apr 30, 2009

'*************************************************************************
'* 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


Updated Mar 15, 2019
Version 2.0
No CommentsBe the first to comment

Share