From MSI to WiX, Part 17 – Windows Installer Automation Interface, Part 2


The main page for the series is here.


 



Introduction


Today we will explore the database of installed products.


In standalone administartive tools scripts you need to create an Installer object using the following commands:


Dim Installer


Set Installer = Wscript.CreateObject(“WindowsInstaller.Installer”)



In scripting custom action MSI engine will make Installer and Session objects available without requiring any action from the user.


 



Querying the database of installed products


We can use the Products property of the Installer object to get the list of the installed or advertised products on the current system.  The following script will print out the names of all installed products.  Use cscript ListProducts.vbs to run the script:


ListProducts.vbs


Option Explicit


 


‘ Connect to Windows Installer object


Dim installer


Set installer = Wscript.CreateObject(“WindowsInstaller.Installer”)


 


Dim product, products


Set products = installer.Products


For Each product In products


    Wscript.Echo installer.ProductInfo(product, InstalledProductName”)


Next


Set products = Nothing


Set installer = Nothing


 


Wscript.Quit 0



Products property of the Installer object returns a StringList object which contains Product ID’s of all installed or advertised products on the current system.  To get the properties of the Product object use the ProductInfo property of the Installer object.


Here is an updated script which shows how to get some properties of the installed product:


Option Explicit


 


Const msiInstallStateBadConfig    = -6


Const msiInstallStateInvalidArg   = -2


Const msiInstallStateUnknown      = -1


Const msiInstallStateAdvertised   =  1


Const msiInstallStateAbsent       =  2


Const msiInstallStateDefault      =  5


 


‘ Connect to Windows Installer object


Dim installer


Set installer = Wscript.CreateObject(“WindowsInstaller.Installer”)


 


Dim product, products


Dim productState


 


Set products = installer.Products


 


For Each product In products


 


    WScript.Echo GenerateProductElement() & vbCrLf


    WScript.Echo GeneratePackageElement() & vbCrLf


    WScript.Echo “</Product>” & vbCrLf


 


Next


 


Set products = Nothing


Set installer = Nothing


 


Wscript.Quit 0


 


Function GenerateProductElement()


    Dim script


    script = “<Product Id=””” & product & “””” & vbCrLf


    script = script & ProductProperty(“InstalledProductName”, “Name”, 9) & vbCrLf


    script = script & ProductProperty(“VersionString”, “Version”, 9) & vbCrLf


    script = script & ProductProperty(“Language”, “Language”, 9) & vbCrLf


    script = script & ProductProperty(“Publisher”, “Manufacturer”, 9)


    script = script & ProductProperty(“InstallLocation”, “InstalledTo”, 9) & vbCrLf


    script = script & ProductProperty(“InstallSource”, “InstalledFrom”, 9) & vbCrLf


    script = script & ProductProperty(“InstallDate”, “InstalledOn”, 9) & vbCrLf


    script = script & ProductProperty(“LocalPackage”, “LocalPackage”, 9) & vbCrLf


    script = script & Space(9) & “State=”””


    Select Case installer.ProductState(product)


        Case msiInstallStateAbsent:


            script = script & “LocalDifferentUser”


        Case msiInstallStateDefault:


            script = script & “LocalCurrentUser”


        Case msiInstallStateAdvertised:


            script = script & “Advertised”


        Case msiInstallStateBadConfig:


            script = script & “Corrupt”


    End Select


    script = script & “”””


   


    GenerateProductElement = script & “>”


End Function


 


Function GeneratePackageElement()


    Dim script


    script =     <Package “ & ProductProperty(“PackageCode”, “Id”, 0) & ” />”


   


    GeneratePackageElement = script


End Function


 


Function ProductProperty(attribute, name, indent)


    ProductProperty = Space(indent) & name & “=””” & installer.ProductInfo(product, attribute) & “”””


End Function


This is how the output might look like:


<Product Id=“{8DE19645-E93C-41AE-8B8F-5A2D00CB5763}”


         Name=“WiX Toolset Visual Studio Package”


         Version=“2.0.5325.0”


         Language=“1033”


         Manufacturer=“Microsoft Corporation”


         InstalledTo=“”


         InstalledFrom=“C:\Documents and Settings\Alex\Local Settings\Temporary Internet Files\Content.IE5\B6JQAD4I\”


         InstalledOn=“20080114”


         LocalPackage=“C:\WINDOWS\Installer\cf306dc.msi”


         State=“LocalCurrentUser”>


 


    <Package Id=“{7400FFBF-8D4D-4AA1-820B-70A3761D5C5E}” />


 


</Product>


 


Products property of the Installer object returns all products currently installed.  You can limit the amount of returned products by using the ProductEx property of the Installer object instead.  This property lets you define an installation context and a security identifier (SID) to narrow the search for installed products.


 


ProductEx property returns collection of Product objects.  For this sample we will be using two of the properties of Product object to get the per-user/per-machine installation context and for per-user installation – the name of the user for which product is installed.


Here is an updated script:


Option Explicit


 


Const msiInstallStateBadConfig    = -6


Const msiInstallStateInvalidArg   = -2


Const msiInstallStateUnknown      = -1


Const msiInstallStateAdvertised   =  1


Const msiInstallStateAbsent       =  2


Const msiInstallStateDefault      =  5


 


Const msiContextUserManaged = 1


Const msiContextUserUnmanaged = 2


Const msiContextMachine = 4


Const msiContextAll = 7


 


‘ Connect to Windows Installer object


Dim installer


Set installer = Wscript.CreateObject(“WindowsInstaller.Installer”)


 


Dim product, products, features, prod


Dim productState


 


Dim useProductsEx


 


useProductsEx = True


 


if useProductsEx = True Then


    ‘ Enumerate all products for all users on the system


    Set products = installer.ProductsEx(“”, “s-1-1-0”, msiContextAll)


 


    For Each prod In products


        product = prod.ProductCode


    


        WScript.Echo GenerateProductElement() & vbCrLf


        WScript.Echo GeneratePackageElement() & vbCrLf


        WScript.Echo “</Product>” & vbCrLf


 


    Next


Else


    Set products = installer.Products


 


    For Each product In products


 


        WScript.Echo GenerateProductElement() & vbCrLf


        WScript.Echo GeneratePackageElement() & vbCrLf


        WScript.Echo “</Product>” & vbCrLf


 


    Next


End If


 


Set products = Nothing


Set installer = Nothing


 


Wscript.Quit 0


 


Function GenerateProductElement()


    Dim script


    script = “<Product Id=””” & product & “””” & vbCrLf


    script = script & ProductProperty(“InstalledProductName”, “Name”, 9) & vbCrLf


    script = script & ProductProperty(“VersionString”, “Version”, 9) & vbCrLf


    script = script & ProductProperty(“Language”, “Language”, 9) & vbCrLf


    script = script & ProductProperty(“Publisher”, “Manufacturer”, 9) & vbCrLf


 


    script = script & ProductProperty(“InstallLocation”, “InstalledTo”, 9) & vbCrLf


    script = script & ProductProperty(“InstallSource”, “InstalledFrom”, 9) & vbCrLf


    script = script & ProductProperty(“InstallDate”, “InstalledOn”, 9) & vbCrLf


    script = script & ProductProperty(“LocalPackage”, “LocalPackage”, 9) & vbCrLf


    script = script & Space(9) & “State=”””


    Select Case installer.ProductState(product)


        Case msiInstallStateAbsent:


            script = script & “LocalDifferentUser”


        Case msiInstallStateDefault:


            script = script & “LocalCurrentUser”


        Case msiInstallStateAdvertised:


            script = script & “Advertised”


        Case msiInstallStateBadConfig:


            script = script & “Corrupt”


    End Select


    script = script & “””” & vbCrLf


    


    Dim findUser


 


    findUser = True


 


    if useProductsEx = True Then


        script = script & Space(9) & “Context=”””


        Select Case prod.Context


            Case msiContextUserManaged:


                script = script & “PerUser”


            Case msiContextUserUnmanaged:


                script = script & “PerUser”


            Case msiContextMachine:


                script = script & “PerMachine”


                findUser = False


        End Select


    End If


   


    If findUser = True Then


        Dim oSid


        Set oSid = GetObject(“winmgmts:!Win32_Sid.SID='” & prod.usersid & “‘”)


       


        script = script & “””” & vbCrLf


        script = script & Space(9) & “User=””” & oSid.ReferencedDomainName & “\” & oSid.AccountName


       


        Set oSid = Nothing


    End If


 


    script = script & “”””


 


    GenerateProductElement = script & “>”


End Function


 


Function GeneratePackageElement()


    GeneratePackageElement =     <Package “ & ProductProperty(“PackageCode”, “Id”, 0) & ” />”


End Function


 


Function ProductProperty(attribute, name, indent)


    ProductProperty = Space(indent) & name & “=””” & installer.ProductInfo(product, attribute) & “”””


End Function


 


Here is what we might get as an output:


 


<Product Id=“{8DE19645-E93C-41AE-8B8F-5A2D00CB5763}”


         Name=“WiX Toolset Visual Studio Package”


         Version=“2.0.5325.0”


         Language=“1033”


         Manufacturer=“Microsoft Corporation”


         InstalledTo=“”


         InstalledFrom=“C:\Documents and Settings\Alex\Local Settings\Temporary Internet Files\Content.IE5\B6JQAD4I\”


         InstalledOn=“20080114”


         LocalPackage=“C:\WINDOWS\Installer\cf306dc.msi”


         State=“LocalCurrentUser”


         Context=“PerMachine”>


 


    <Package Id=“{7400FFBF-8D4D-4AA1-820B-70A3761D5C5E}” />


 


</Product>


 


<Product Id=“{2119B6B3-7738-4F4E-A673-AF0E626CF58E}”


         Name=“Microsoft Best Practices Analyzer”


         Version=“1.0.0”


         Language=“1033”


         Manufacturer=“Microsoft Corporation”


         InstalledTo=“”


         InstalledFrom=“C:\Documents and Settings\Alex\Local Settings\Temporary Internet Files\Content.IE5\1QV9QXXV\”


         InstalledOn=“20071031”


         LocalPackage=“C:\WINDOWS\Installer\a7f1821.msi”


         State=“LocalCurrentUser”


         Context=“PerUser”


         User=“WORKHORSE\Alex”>


 


    <Package Id=“{64B72558-064E-4267-A929-B431163F7568}” />


 


</Product>


 


To get the list of features installed for particular product use Features property of the Installer object.  This property takes one argument, which is a product code and returns a StringList object with the list of feature ID’s.


Feature tree is hierarchical structure and every feature can have a parent.  To get the parent of the feature use FeatureParent property of the Installer object.  To get the state of the feature use the FeatureState property of the Installer object.


Here is an update to the script which shows a feature hierarchy for every installed product:


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 msiInstallStateAbsent       =  2


Const msiInstallStateLocal        =  3


Const msiInstallStateSource       =  4


Const msiInstallStateDefault      =  5


 


Function GenerateFeatureTable(indent)


    Dim feature, script, parent


 


    Set features = installer.Features(product)


   


    For Each feature In features


        ‘ Iterate through every root-level feature


        parent = installer.FeatureParent(product, feature)


        If Len(parent) = 0 Then


            script = script & GenerateFeatureElement(feature, indent)


            script = script & GenerateSubFeatures(feature, indent + 4)


            script = script & Space(indent) & “</Feature>” & vbCrLf


        End If


    Next


   


    Set features = Nothing


   


    GenerateFeatureTable = script


End Function


 


Function GenerateSubFeatures(feature, indent)


    Dim child, script, features


   


    Set features = installer.Features(product)


    script = “”


 


    For Each child In features


        If feature = installer.FeatureParent(product, child) Then


            script = script & GenerateFeatureElement(child, indent)


            script = script & GenerateSubFeatures(child, indent + 4)


            script = script & Space(indent) & “</Feature>” & vbCrLf


        End If


    Next


      


    Set features = Nothing


 


    GenerateSubFeatures = script


End Function


 


Function GenerateFeatureElement(feature, indent)


    Dim script, state


 


    script = Space(indent) & “<Feature Id=””” & feature & “”” State=”””


   


    On Error Resume Next


 


    state = installer.FeatureState(product, feature)


   


    Select Case state


        Case msiInstallStateBadConfig:


            script = script & “Corrupt”


        Case msiInstallStateIncomplete:


            script = script & “InProgress”


        Case msiInstallStateSourceAbsent:


            script = script & “SourceAbsent”


        Case msiInstallStateBroken:


            script = script & “Broken”


        Case msiInstallStateAdvertised:


            script = script & “Advertised”


        Case msiInstallStateAbsent:


            script = script & “Absent”


        Case msiInstallStateLocal:


            script = script & “Local”


        Case msiInstallStateSource:


            script = script & “Source”


        Case msiInstallStateDefault:


            script = script & “Default”


        Case Else


            script = script & “Unknown”


    End Select


    


    GenerateFeatureElement = script & “””>” & vbCrLf


End Function


 


This is how the output might look like:


 


<Product Id=“{85F4CBCB-9BBC-4B50-A7D8-E1106771498D}”


         Name=“Orca”


         Version=“3.1.5299.0000”


         Language=“1033”


         Manufacturer=“Microsoft Corporation”


         InstalledTo=“”


         InstalledFrom=“C:\Program Files\Microsoft SDKs\Windows\V6.0A\Bin\”


         InstalledOn=“20071017”


         LocalPackage=“C:\WINDOWS\Installer\1c618f4a.msi”


         State=“LocalCurrentUser”


         Context=“PerMachine”>


 


    <Package Id=“{FE01B6C5-85BF-4CA3-9B78-B44DEB8A4946}” />


 


    <Feature Id=“Orca” State=“Local”>


        <Feature Id=“OrcaHelp” State=“Local”>


        </Feature>


        <Feature Id=“EvalComServer” State=“Local”>


        </Feature>


        <Feature Id=“MergeModServer” State=“Local”>


        </Feature>


        <Feature Id=“CUBFiles” State=“Local”>


            <Feature Id=“FullCUBFile” State=“Local”>


            </Feature>


            <Feature Id=“LogoCUBFile” State=“Local”>


            </Feature>


            <Feature Id=“XPLogoCUBFile” State=“Local”>


            </Feature>


            <Feature Id=“MMCUBFile” State=“Local”>


            </Feature>


        </Feature>


    </Feature>


 


</Product>


 


To get the list of all components currently installed on the system, use the Components property of the Installer object.  To determine if component belongs to a particular product, use the ComponentClients property.  This property returns a list of product codes of products with which current component is shared.  We can get an installation state of the component by calling ComponentState property of the Product object.  To get he full path where component is installed we need to call ComponentPath method of the Installer object.


 


Warning:  Presented implementation’s performance is very poor.  It was not my goal to create commercial grade application.


 



Function GenerateComponentList(productCode, indent)


    Dim result


    Dim component, components, client, clients


    Dim state


   


    result = Space(indent) & “<Components>” & vbCrLf


    Set components = installer.Components


    For Each component in components


        Set clients = installer.ComponentClients(component)


        For Each client in clients


            If client = productCode Then


                If useProductsEx = True Then


                    result = result & Space(indent + 4) & “<Component Id=””” & component


                    result = result & “”” State=”””


                    state = prod.ComponentState(component)


                    Select Case state


                        Case msiInstallStateLocal:


                            result = result & “Local”


                        Case msiInstallStateSource:


                            result = result & “Source”


                        Case msiInstallStateNotUsed


                            result = result & “NotInstalled”


                        Case Else


                            result = result & state


                    End Select


                    result = result & “””” & vbCrLf


                    result = result & Space(indent + 15) & “ComponentPath=””” & Installer.ComponentPath(productCode, component)


                    result = result & “”” />” & vbCrLf


                Else


                    result = result & Space(indent + 4) & “<Component Id=””” & component & “”” />” & vbCrLf


                End If


                Exit For


            End If


        Next


    Next


    GenerateComponentList = result & Space(indent) & “</Components>” & vbCrLf


End Function


 


This is how the output might look like:


<Product Id=“{8DE19645-E93C-41AE-8B8F-5A2D00CB5763}”


         Name=“WiX Toolset Visual Studio Package”


         Version=“2.0.5325.0”


         Language=“1033”


         Manufacturer=“Microsoft Corporation”


         InstalledTo=“”


         InstalledFrom=“C:\Documents and Settings\Alex\Local Settings\Temporary Internet Files\Content.IE5\B6JQAD4I\”


         InstalledOn=“20080114”


         LocalPackage=“C:\WINDOWS\Installer\cf306dc.msi”


         State=“LocalCurrentUser”


         Context=“PerMachine”>


 


    <Package Id=“{7400FFBF-8D4D-4AA1-820B-70A3761D5C5E}” />


 


    <Feature Id=“Feature_VS2005” State=“Local”>


    </Feature>


 


    <Components>


        <Component Id=“{39CFE791-2BE2-4B1A-852F-6A14E6AC00BC}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\lit.exe.config” />


        <Component Id=“{B9BD83B1-4106-428A-A1C1-7473041B739F}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\mergemod.dll” />


        <Component Id=“{0458E092-F02A-492C-9865-2AAF24251EB4}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\doc\WiX.chm” />


        <Component Id=“{A4473BD2-EC34-4CB4-BBCF-E9F1B03B5462}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\candle.exe” />


        <Component Id=“{40340083-FDB0-437B-B172-F3011F50250F}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\dark.exe.config” />


        <Component Id=“{828C4493-20D4-44DC-8714-9241C2DB017A}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\wixca.dll” />


        <Component Id=“{01D89793-D0CA-40B4-93C6-A6564807E162}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\lit.exe” />


        <Component Id=“{97760B35-5DFA-487B-8664-2E725160E8AF}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\sconce.dll” />


        <Component Id=“{47F88195-5595-40B3-BB32-6837C185AD5A}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\Bitmaps\bannrbmp.bmp” />


        <Component Id=“{96B9F8A5-F52B-4CA6-BFD5-6D943970F137}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\tallow.exe” />


        <Component Id=“{02FE8BB5-85C6-4707-AD72-E5A66CE817AF}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\1033\votiveui.dll” />


        <Component Id=“{A1947047-971D-47DF-81DC-17CA6D95E281}” State=“Local”


                   ComponentPath=“02:\SOFTWARE\Microsoft\VisualStudio\8.0\Editors\{FA3CD31E-987B-443A-9B81-186104E8DAC1}\Extensions\wxs” />


        <Component Id=“{C79B4FD7-9C59-4F5F-A3CF-CB97C15561B2}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\doc\wixloc.xsd” />


        <Component Id=“{4C7E3258-FD99-4084-B8EE-001D3464AC48}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\Templates\Project Items\Code\Code.vsdir” />


        <Component Id=“{41074398-C485-4AA4-BD01-F4EB0E5DCD63}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\Templates\Projects\Projects.vsdir” />


        <Component Id=“{0BCBF6C8-3BCA-41BD-ACDD-B20866F43216}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\Templates\Project Items\Resources\Resources.vsdir” />


        <Component Id=“{647008F8-DF4E-4A8C-8BB0-8EC64CA54F4B}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\Templates\Project Items\ProjectItems.vsdir” />


        <Component Id=“{C1A158F8-1A3D-4439-9FB1-6F78E7201E0A}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\light.exe.config” />


        <Component Id=“{9E3450EA-143F-4FAB-8A10-FEEC5D7A8274}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\winterop.dll” />


        <Component Id=“{95BD12FA-2B78-4125-82C9-9FA82DDF7F61}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\doc\wix.xsd” />


        <Component Id=“{5E2F000C-11C4-40B0-80EA-2FA6748E97F9}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\light.exe” />


        <Component Id=“{E05A990C-7E04-4DF9-BFC2-845D172B7ABA}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\WixUI.wixlib” />


        <Component Id=“{B871A32C-5620-4640-908C-1EB514F701FF}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\CPL.TXT” />


        <Component Id=“{FBE0F9FC-853D-4AB4-B849-284FEC7C48F8}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\tallow.exe.config” />


        <Component Id=“{D5CA815E-0A94-4A91-BE53-8B1ED271DF10}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\dark.exe” />


        <Component Id=“{20B53A0F-BEE4-40DD-A0B3-54F3C16B876F}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\wix.dll” />


        <Component Id=“{DEC3354F-F81E-414D-BC0E-85F507C65EDF}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\candle.exe.config” />


        <Component Id=“{3FC43EEF-C5B8-46E9-9640-36B647C95197}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\bin\scaexec.dll” />


        <Component Id=“{8D3A06FF-7EDB-44D8-AFC0-49FDE3ABBA3D}” State=“Local”


                   ComponentPath=“C:\Program Files\Windows Installer XML\Visual Studio\votive.dll” />


    </Components>


 


</Product>



In next part we will look at how to get the list of patches applied to a product and how to query cached copy of product’s installer package.


Source code is attached.


 

ListProducts.vbs

Comments (3)

  1. ornage26 says:

    I have got an error 0x80004005 at line 34, symbol 5, source Msi API Error.

  2. rkamal says:

    I also get the same error on just one machine.. its so puzzling.

    I narrowed it down to this:

    oMSI.ProductsEx("", "s-1-1-0", 4)  FAILS!

    oMSI.ProductsEx("", "s-1-1-0", 1)  GOOD

    oMSI.ProductsEx("", "s-1-1-0", 2)  GOOD

    Still stuck. if you know a solution for this please let me know rbhkamal@gmail.com

    Thanks!

  3. gbh says:

    It seems its a LUA problem… tough I don't know why you would need Admin privileges to query about the machine state.

    Using msiContextMachine in the "context" Parameter withouth beign an administrator shows the error you described. If you launch the script from an elevated command prompt it works fine.

Skip to main content