List features and components for installed MSI

VB Script

Let’s assume that you have a big vendor MSI with lots of features and components. You apply a transform to it according to the specifications sent by the customer.

So far so good. But how do you test if everything is installed accordingly?

Microsoft offers a VBScript called WiLstPrd.vbs which is present in Windows SDK Components for Windows Installer Developers.

With it you can list products, properties, features, components and much more.

But how do you use it to compare your original MSI and the one with the changes added by you?

Well it’s simple. First, install the original MSI on a clean machine with the desired changes to it.

Copy the WiLstPrd.vbs on a desired location, for example C:\WiLstPrd.vbs.

In the same directory create the following batch file listfeatandcomp.cmd (C:\listfeatandcomp.cmd):

 


cscript "C:\WiLstPrd.vbs" {11111111-2222-3333-4444-555555555555} f > "C:\features.txt"

cscript "C:\WiLstPrd.vbs" {11111111-2222-3333-4444-555555555555} c > "C:\components.txt"

 

Replace {11111111-2222-3333-4444-555555555555} with your MSI product code before executing the batch file.

The “f” parameter outputs the features and the “c” parameter outputs the components. For more details check out the official documentation here.

After you double click the listfeatandcomp.cmd, two txt files will be created in C:\, features.txt and components.txt, each containing the installed and uninstalled features.

 

Now, on a clean machine, install your MSI with self-designed MST and repeat the steps.

After that, with a compare tool of your choice, compare the original features.txt/components.txt and the modified features.txt/components.txt

 

WiLstPrd.vbs contents are:

 


' Windows Installer utility to list registered products and product info
' For use with Windows Scripting Host, CScript.exe or WScript.exe
' Copyright (c) Microsoft Corporation. All rights reserved.
' Demonstrates the use of the product enumeration and ProductInfo methods and underlying APIs
'
Option Explicit

Const msiInstallStateNotUsed = -7
Const msiInstallStateBadConfig = -6
Const msiInstallStateIncomplete = -5
Const msiInstallStateSourceAbsent = -4
Const msiInstallStateInvalidArg = -2
Const msiInstallStateUnknown = -1
Const msiInstallStateBroken = 0
Const msiInstallStateAdvertised = 1
Const msiInstallStateRemoved = 1
Const msiInstallStateAbsent = 2
Const msiInstallStateLocal = 3
Const msiInstallStateSource = 4
Const msiInstallStateDefault = 5

' Connect to Windows Installer object
On Error Resume Next
Dim installer : Set installer = Nothing
Set installer = Wscript.CreateObject("WindowsInstaller.Installer") : CheckError

' If no arguments supplied, then list all installed or advertised products
Dim argCount:argCount = Wscript.Arguments.Count
If (argCount = 0) Then
Dim product, products, info, productList, version
On Error Resume Next
Set products = installer.Products : CheckError
For Each product In products
version = DecodeVersion(installer.ProductInfo(product, "Version")) : CheckError
info = product & " = " & installer.ProductInfo(product, "ProductName") & " " & version : CheckError
If productList <> Empty Then productList = productList & vbNewLine & info Else productList = info
Next
If productList = Empty Then productList = "No products installed or advertised"
Wscript.Echo productList
Set products = Nothing
Wscript.Quit 0
End If

' Check for ?, and show help message if found
Dim productName:productName = Wscript.Arguments(0)
If InStr(1, productName, "?", vbTextCompare) > 0 Then
Wscript.Echo "Windows Installer utility to list registered products and product information" &_
vbNewLine & " Lists all installed and advertised products if no arguments are specified" &_
vbNewLine & " Else 1st argument is a product name (case-insensitive) or product ID (GUID)" &_
vbNewLine & " If 2nd argument is missing or contains 'p', then product properties are listed" &_
vbNewLine & " If 2nd argument contains 'f', features, parents, & installed states are listed" &_
vbNewLine & " If 2nd argument contains 'c', installed components for that product are listed" &_
vbNewLine & " If 2nd argument contains 'd', HKLM ""SharedDlls"" count for key files are listed" &_
vbNewLine &_
vbNewLine & "Copyright (C) Microsoft Corporation. All rights reserved."
Wscript.Quit 1
End If

' If Product name supplied, need to search for product code
Dim productCode, property, value, message
If Left(productName, 1) = "{" And Right(productName, 1) = "}" Then
If installer.ProductState(productName) <> msiInstallStateUnknown Then productCode = UCase(productName)
Else
For Each productCode In installer.Products : CheckError
If LCase(installer.ProductInfo(productCode, "ProductName")) = LCase(productName) Then Exit For
Next
End If
If IsEmpty(productCode) Then Wscript.Echo "Product is not registered: " & productName : Wscript.Quit 2

' Check option argument for type of information to display, default is properties
Dim optionFlag : If argcount > 1 Then optionFlag = LCase(Wscript.Arguments(1)) Else optionFlag = "p"
If InStr(1, optionFlag, "*", vbTextCompare) > 0 Then optionFlag = "pfcd"

If InStr(1, optionFlag, "p", vbTextCompare) > 0 Then
message = "ProductCode = " & productCode
For Each property In Array(_
"Language",_
"ProductName",_
"PackageCode",_
"Transforms",_
"AssignmentType",_
"PackageName",_
"InstalledProductName",_
"VersionString",_
"RegCompany",_
"RegOwner",_
"ProductID",_
"ProductIcon",_
"InstallLocation",_
"InstallSource",_
"InstallDate",_
"Publisher",_
"LocalPackage",_
"HelpLink",_
"HelpTelephone",_
"URLInfoAbout",_
"URLUpdateInfo") : CheckError
value = installer.ProductInfo(productCode, property) ': CheckError
If Err <> 0 Then Err.Clear : value = Empty
If (property = "Version") Then value = DecodeVersion(value)
If value <> Empty Then message = message & vbNewLine & property & " = " & value
Next
Wscript.Echo message
End If

If InStr(1, optionFlag, "f", vbTextCompare) > 0 Then
Dim feature, features, parent, state, featureInfo
Set features = installer.Features(productCode)
message = "---Features in product " & productCode & "---"
For Each feature In features
parent = installer.FeatureParent(productCode, feature) : CheckError
If Len(parent) Then parent = " {" & parent & "}"
state = installer.FeatureState(productCode, feature)
Select Case(state)
Case msiInstallStateBadConfig: state = "Corrupt"
Case msiInstallStateIncomplete: state = "InProgress"
Case msiInstallStateSourceAbsent: state = "SourceAbsent"
Case msiInstallStateBroken: state = "Broken"
Case msiInstallStateAdvertised: state = "Advertised"
Case msiInstallStateAbsent: state = "Uninstalled"
Case msiInstallStateLocal: state = "Local"
Case msiInstallStateSource: state = "Source"
Case msiInstallStateDefault: state = "Default"
Case Else: state = "Unknown"
End Select
message = message & vbNewLine & feature & parent & " = " & state
Next
Set features = Nothing
Wscript.Echo message
End If

If InStr(1, optionFlag, "c", vbTextCompare) > 0 Then
Dim component, components, client, clients, path
Set components = installer.Components : CheckError
message = "---Components in product " & productCode & "---"
For Each component In components
Set clients = installer.ComponentClients(component) : CheckError
For Each client In Clients
If client = productCode Then
path = installer.ComponentPath(productCode, component) : CheckError
message = message & vbNewLine & component & " = " & path
Exit For
End If
Next
Set clients = Nothing
Next
Set components = Nothing
Wscript.Echo message
End If

If InStr(1, optionFlag, "d", vbTextCompare) > 0 Then
Set components = installer.Components : CheckError
message = "---Shared DLL counts for key files of " & productCode & "---"
For Each component In components
Set clients = installer.ComponentClients(component) : CheckError
For Each client In Clients
If client = productCode Then
path = installer.ComponentPath(productCode, component) : CheckError
If Len(path) = 0 Then path = "0"
If AscW(path) >= 65 Then ' ignore registry key paths
value = installer.RegistryValue(2, "SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDlls", path)
If Err <> 0 Then value = 0 : Err.Clear
message = message & vbNewLine & value & " = " & path
End If
Exit For
End If
Next
Set clients = Nothing
Next
Set components = Nothing
Wscript.Echo message
End If

Function DecodeVersion(version)
version = CLng(version)
DecodeVersion = version\65536\256 & "." & (version\65535 MOD 256) & "." & (version Mod 65536)
End Function

Sub CheckError
Dim message, errRec
If Err = 0 Then Exit Sub
message = Err.Source & " " & Hex(Err) & ": " & Err.Description
If Not installer Is Nothing Then
Set errRec = installer.LastErrorRecord
If Not errRec Is Nothing Then message = message & vbNewLine & errRec.FormatText
End If
Wscript.Echo message
Wscript.Quit 2
End Sub

Leave a comment

Your email address will not be published. Required fields are marked *

four × two =