EnumPatchesReg.vbs

EnumPatchesReg.vbs

  • Comments 22

'*************************************************************************
'* 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 = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products"


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 & "\root\default:StdRegProv")

Set g_objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
     g_strComputer & "\root\cimv2")
     

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

Leave a Comment
  • Please add 1 and 2 and type the answer here:
  • Post
Page 1 of 2 (22 items) 12