Win32_Product

Introduction

One of the easiest ways to get a list of installed applications on a machine is to use the WMI class Win32_Product.  This enumerates every application.  For example, the following code will display a table with the name of the installed application and it’s version number:

Get-WmiObject -Class Win32_Product | Select Name, Version

Great, now let’s filter it to find a specific application.  I’ll search for any application I have installed with ‘zip’ in the name:

Get-WmiObject -Class Win32_Product -Filter { Name like "%zip%" } | Select Name, Version

Name                                       Version
----                                       -------
7-Zip 9.38 (x64 edition)                   9.38.00.0

Just what we wanted, but there is a problem.

Problem

If you now look at your Application event log, you will notice a huge list of entries that say something like

Windows Installer reconfigured the product. Product Name: [name]. Product Version: [version]. Product Language: 1033. Manufacturer: [manufacturer]. Reconfiguration success or error status: 0.

You will see one entry for every single application you have installed (and a few more).  You may also notice that the command above took quite a long time to run.  In some cases I have seen it take up to 5 minutes to return just one entry.

This is a well known and blogged about topic, Microsoft even has a Knowledge Base article on it: KB974524.

 

Solution

My solution to this problem is a PowerShell function that searches the registry for the same information.  This has the benefit of being incredibly fast.

It works by searching the two locations where applications put their uninstall details.

HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall

HKLM\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

The first is for 32-bit applications, the second for 64-bit applications.

To use my function simply call it with the following command.  Again, searching for any application with ‘zip’ in the name:

Win32_Product -serverName $env:computername -displayName 'zip'

This will return just the version number : 9.38.00.0

My function will only return the version number of the first application it finds that matches the input value.  You could expand on this and return all application names and version numbers using a hashtable or an array.  I’ll leave that up to you.

 

$regSearch = 'Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
Function Win32_Product
{
    Param ([string]$serverName, [string]$displayName)
    Try
    {
        $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $serverName)
        $regKey = $reg.OpenSubKey($regSearch)
        If ($regKey) { [array]$keyVal = $regKey.GetSubKeyNames() }
    }
    Catch { Return $null }

    $found = $false
    If (($regKey) -and ($keyVal.Count -gt 0)) {
        ForEach ($app In $keyVal) {
            $appKey = $regKey.OpenSubKey($app).GetValue('DisplayName')
            If ($appKey -like ('*' + $displayName + '*')) {
                $found = $true
                [string]$verCheck = $regKey.OpenSubKey($app).GetValue('DisplayVersion')
                If (-not $verCheck) { $verCheck = '0.1' } }
        }
        If ($found -eq $false) {
            If ($regSearch -like '*Wow6432Node*') {
                $regSearch = $regSearch.Replace('Wow6432Node', '')
                $verCheck = Win32_Product -serverName $serverName -displayName $displayName
            }
            Else { $verCheck = $null } }
    }
    Else { $verCheck = $null }
    $regKey.Close()
    $reg.Close()
    Return $verCheck
}

 

Leave a Reply

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