Reputation: 21
I'd like to do this using the win32_product wmi class.
I need the script to simply count the number of products installed and also output the time taken to execute the script.
What I have atm doesn't seem to work correctly:
$count = 0
$products = get-wmiobject -class "Win32_Product"
foreach ($product in $products) {
if ($product.InstallState -eq 5) {
count++
}
}
write-host count
Upvotes: 2
Views: 2727
Reputation: 36698
Beware! Using WMI's Win32_Product
class, which the question and the previous 2 answers do, is not advised for this purpose.
In a nutshell: using Win32_Product is not an innocuous query because it has side effects. Quoting Microsoft, "[It]... initiates a consistency check of packages installed, verifying and repairing the install." (emphasis mine)
References:
So what is a better (safer) solution?
Marc Carter, writing the guest column in the Hey, Scripting Guy! blog above takes the first volley, providing a custom PowerShell function, back on my system it returned only half as many entries as the Win32_Product invocation. Also, it is a lot of code (3 dozen lines or so). In the comments to his post, however, knutkj offers this much shorter version that does the same thing:
Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
Get-ItemProperty |
Sort-Object -Property DisplayName |
Select-Object -Property DisplayName, DisplayVersion, InstallLocation
But it does, as I said, the same thing: not provide a full list. But it is a start.
Later in the comments Nick W reported that there are actually 3 registry paths of interest, though not all may be present on every system. Further, when looking at those 3 paths, one has to do some additional filtering.
Combining both of those, adding in a few more output fields, and making the code safe to run in strict mode, I arrived at this simple solution:
function Get-InstalledPrograms()
{
$regLocations = (
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\",
"HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
)
Get-ChildItem ($regLocations | Where { Test-Path $_ } ) |
Get-ItemProperty |
Where {
( (Get-Member -InputObject $_ -Name DisplayName) -and $_.DisplayName -ne $Null) -and
(!(Get-Member -InputObject $_ -Name SystemComponent) -or $_.SystemComponent -ne "1") -and
(!(Get-Member -InputObject $_ -Name ParentKeyName) -or $_.ParentKeyName -eq $Null)
} |
Sort DisplayName |
Select DisplayName, DisplayVersion, Publisher, InstallLocation, InstallDate, URLInfoAbout
}
Upvotes: 4
Reputation: 29449
Roman Kuzmin is right about the typo. Correction will solve almost everything.
To make it more powershellish, I would use
get-wmiobject -class "Win32_Product" |
? { $_.InstallState -eq 5 } |
measure-object |
select -exp Count
And considering the time, you can wrap it into measure-command
measure-command {
$count = get-wmiobject -class "Win32_Product" |
? { $_.InstallState -eq 5 } |
measure-object |
select -exp Count
write-host Count: $count
}
Upvotes: 2
Reputation: 199
A bit late on this but the "more powershellish way":
$(Get-WmiObject -Class "Win32_Product" -Filter "InstallState=5").Count
Upvotes: 2