Trying to use WMI to obtain a list of installed programs for Windows XP. Using wmic, I tried:
wmic /output:c:\ProgramList.txt product get name,version
and I get a listing of many of the installed programs, but after scrubbing this list against what "Add/Remove Programs" displays, I see many more programs listed in the GUI of Add/Remove Programs than with the WMI query. Is there another WMI query I need to use to get the rest of the programs installed? Or is there some other place I need to look for the rest?
Also, there are two installed programs that are listed in the WMI query that aren't in Add/Remove programs. Any idea why?
I believe your syntax is using the Win32_Product Class in WMI. One cause is that this class only displays products installed using Windows Installer (See Here). The Uninstall Registry Key is your best bet.
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
UPDATE FOR COMMENTS:
The Uninstall Registry Key is the standard place to list what is installed and what isn't installed. It is the location that the Add/Remove Programs list will use to populate the list of applications. I'm sure that there are applications that don't list themselves in this location. In that case you'd have to resort to another cruder method such as searching the Program Files directory or looking in the Start Menu Programs List. Both of those ways are definitely not ideal.
In my opinion, looking at the registry key is the best method.
All that Add/Remove Programs is really doing is reading this Registry key:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall
Besides the most commonly known registry key for installed programs:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall
wmic command and the add/remove programs also query another registry key:
HKEY_CLASSES_ROOT\Installer\Products
Software name shown in the list is read from the Value of a Data entry within this key called: ProductName
Removing the registry key for a certain product from both of the above locations will keep it from showing in the add/remove programs list. This is not a method to uninstall programs, it will just remove the entry from what's known to windows as installed software.
Since, by using this method you would lose the chance of using the Remove button from the add/remove list to cleanly remove the software from your system; it's recommended to export registry keys to a file before you delete them. In future, if you decided to bring that item back to the list, you would simply run the registry file you stored.
I have been using Inno Setup for an installer. I'm using 64-bit Windows 7 only. I'm finding that registry entries are being written to
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
I haven't yet figured out how to get this list to be reported by WMI (although the program is listed as installed in Programs and Features). If I figure it out, I'll try to remember to report back here.
UPDATE:
Entries for 32-bit programs installed on a 64-bit machine go in that registry location. There's more written here:
http://mdb-blog.blogspot.com/2010/09/c-check-if-programapplication-is.html
See my comment that describes 32-bit vs 64-bit behavior in that same post here:
http://mdb-blog.blogspot.com/2010/09/c-check-if-programapplication-is.html?showComment=1300402090679#c861009270784046894
Unfortunately, there doesn't seem to be a way to get WMI to list all programs from the add/remove programs list (aka Programs and Features in Windows 7, not sure about Vista). My current code has dropped WMI in favor of using the registry. The code itself to interrogate the registry is even easier than using WMI. Sample code is in the above link.
Not the best, but whether it is practical method:
Use HijackThis. (Old version: HijackThis)
Run hijack this, click the "Open the Misc Tools section" button
click "Open Uninstall Manager"
click save list (*.txt), yes to the prompts, notepad will open with your add/remove programs list.
Source
You can get it in one line with powershell and batch file :
#echo off
Powershell /command "Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Format-List"
Pause
Installed products consist of installed software elements and features so it's worth checking wmic alias's for PRODUCT as well as checking SOFTWAREELEMENT and SOFTWAREFEATURE:
wmic product get name,version
wmic softwareelement get name,version
wmic softwarefeature get name,version
You can use the script from http://technet.microsoft.com/en-us/library/ee692772.aspx#EBAA to access the registry and list applications using WMI.
Hope this helps somebody: I've been using the registry-based enumeration in my scripts (as suggested by some of the answers above), but have found that it does not properly enumerate 64-bit software when run on Windows 10 x64 via SCCM (which uses a 32-bit client). Found something like this to be the most straightforward solution in my particular case:
Function Get-Programs($Bits) {
$Result = #()
$Output = (reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall /reg:$Bits /s)
Foreach ($Line in $Output) {
If ($Line -match '^\s+DisplayName\s+REG_SZ\s+(.+?)$') {
$Result += New-Object PSObject -Property #{
DisplayName = $matches[1];
Bits = "$($Bits)-bit";
}
}
}
$Result
}
$Software = Get-Programs 32
$Software += Get-Programs 64
Realize this is a little too Perl-ish in a bad way, but all other alternatives I've seen involved insanity with wrapper scripts and similar clever-clever solutions, and this seems a little more human.
P.S. Trying really hard to refrain from dumping a ton of salt on Microsoft here for making an absolutely trivial thing next to impossible. I.e., enumerating all MS Office versions in use on a network is a task to make a grown man weep.
With time having moved on quite a bit since this question was asked...
There's a WMI class available these days for the Uninstall entries in the registry. This is much quicker to reference than Win32_Product, which I think also runs verification on the list and can take a while to enumerate. The below Powershell code (possibly requires Powershell 3 or later) will list all entries (The Out-Gridview part is just for a pretty display).
Get-CimInstance Win32Reg_AddRemovePrograms | Out-gridview
Add/Remove Programs also has to look into this registry key to find installations for the current user:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall
Applications like Google Chrome, Dropbox, or shortcuts installed through JavaWS (web start) JNLPs can be found only here.
In order to build a more-or-less reliable list of applications that appear in the "Programs and Feautres" in the Control Panel, you have to consider that not all applications were installed using MSI. WMI only provides the ones installed with MSI.
Here is a short summary of what I've found out:
MSI applications always have a Product Code (GUID) subkey under HKLM\...\Uninstall and/or under HKLM\...\Installer\UserData\S-1-5-18\Products. In addition, they may have a key that looks like HKLM\...\Uninstall\NotAGuid.
Non-MSI applications do not have a product code, and therefore have keys like HKLM\...\Uninstall\NotAGuid or HKCU\...\Uninstall\NotAGuid.
I adapted the MS-Technet VBScript for my needs. It dumps Wow6432Node as well as standard entries into "programms.txt"
Use it at your own risk, no warranty!
Save as dump.vbs
From command line type: wscript dump.vbs
Const HKLM = &H80000002
Set objReg = GetObject("winmgmts://" & "." & "/root/default:StdRegProv")
Set objFSO = CreateObject("Scripting.FileSystemObject")
outFile="programms.txt"
Set objFile = objFSO.CreateTextFile(outFile,True)
writeList "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\", objReg, objFile
writeList "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", objReg, objFile
objFile.Close
Function writeList(strBaseKey, objReg, objFile)
objReg.EnumKey HKLM, strBaseKey, arrSubKeys
For Each strSubKey In arrSubKeys
intRet = objReg.GetStringValue(HKLM, strBaseKey & strSubKey, "DisplayName", strValue)
If intRet <> 0 Then
intRet = objReg.GetStringValue(HKLM, strBaseKey & strSubKey, "QuietDisplayName", strValue)
End If
objReg.GetStringValue HKLM, strBaseKey & strSubKey, "DisplayVersion", version
objReg.GetStringValue HKLM, strBaseKey & strSubKey, "InstallDate", insDate
If (strValue <> "") and (intRet = 0) Then
objFile.Write strValue & "," & version & "," & insDate & vbCrLf
End If
Next
End Function
I had the same issue with the WMIC command only showing a subset of all installed programs. I needed a command that would output a CSV file of all the installed programs in Windows 10 and I wanted to make it easy for colleagues to run. The following Powershell command seems to work well, running much quicker than the WMIC command; it will generate a CSV in the user's Documents folder called AppsInstalled.csv with programs sorted by name, and with empty lines removed (numerous blank lines were returned in my tests for some reason?).
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate| Where-Object DisplayName -ne $null | Sort-Object -Property DisplayName | Export-Csv "$($env:USERPROFILE)\Documents\AppsInstalled.csv" -NoTypeInformation
Related
I want to programmatically get a list of all installed KBs. This could be python code, WinAPI (which I will call from python), running another process and parsing its output, etc. What interests me are security updates, I don't care whether or not other updates are returned or not. However, I want updates for software as well, not just for Windows - anything that shows up in the control panel's "Installed Updates" window.
It's also important that this code will run on all versions of windows, not just a single version (I don't mind having an if-else in the code, with different behavior for different Windows versions - it's just important that eventually it works).
I tried wmic qfe, systeminfo and PowerShell's get-hotfix, all of which return only OS updates.
I have a Windows 10 myself, and I couldn't find a single place in the registry or in the file system where all KBs are listed together. Couldn't make sense of Procmon's output (after recording opening "Installed Updates") either - too large and not focused enough. It seems like different updates are listed in different places, but nothing I could easily understand how to extend/generalize.
EDIT:
I found this code: https://codereview.stackexchange.com/questions/135648/find-installed-and-available-windows-updates
I tried running it on my computer, and it found some KBs that didn't appear neither in the commandline commands I ran, nor in "Installed Updates". On the other hand, there are also KBs that don't appear there but do appear in the other locations..
Thanks!
The problem with qfe is that newer Windows versions have updates for componentes which aren't CBS related, hence wmic path Win32_QuickFixEngineering will not show them.
The trick is to use a COMObject to the updater system.
I've written a small package that does the job, and checks for updates via COM, WMI and registry.
Talking to windows update via COM gives the most information.
WMI gives some information, and registry of course only gives the KB and install date.
Install with
pip install windows_tools.updates
Use with
from windows_tools.updates import get_windows_updates
for update in get_windows_updates(filter_duplicates=True):
print(update)
The duplicate filter is enabled because of AV definition updates that show alot.
I'm new to puppet and ruby, and just tried to write custom fact but ... Having the following issue
Facter.add("vsphere_installed") do
confine :operatingsystem => :windows
setcode do
if Facter::Util::Resolution.exec('c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe -NonInteractive -NoProfile -ExecutionPolicy Bypass -Command "Get-WmiObject -Class Win32_Product | Select-Object -DisplayName | ? {$_.DisplayName -Match "vsphere"}"') = true
result = "vSphere installed"
else
result = "false"
end
end
end
I don't know how exactly to do this, I want to list installed programs and search for one and if true(find) to return that it's installed.
This example so far returns only false ....
Puppet is about Desired State - not Procedural
It feels like you are treating Puppet as procedural at the moment and Puppet is more about the desired state. You determine what is installed, you shouldn't necessarily ask.
So on certain server roles you would say the end state is that you need vSphere and also other software.
You get to make those decisions, you shouldn't use Puppet to discover the state, but to tell it the state and let it do what it does best.
Discovery is something you can do out of band with the tool exploring a machine, try puppet resource package and you will see what I mean.
Custom Facts
But to answer your question, you should probably use a custom executable fact and just use PowerShell directly, because the command string still needs to be escaped in the double quotes (and may also need to be escaped in the way that you used apostrophe then double quotes) - the docs also point to using Facter::Core::Execution.exec and not Facter::Util::Resolution.exec.
Use Custom Executable Facts instead.
Also don't use Win32_Product - Win32_Product class can trigger Windows Installer to do a repair on all MSI installed software as a consistency check. It can really cause a machine to do a lot of unnecessary work - its just not a good idea to use it. I'd suggest querying the uninstaller registry keys directly instead.
In SCCM 2007, there were several "Right Click Tools", and with their help it was possible to "reinstall" a package. In SCCM 2012 I still couldn't find a way, how could I reinstall an application?
Let me explain:
I created an installation package from a software, then distributed it as an "Application". Installation finished successfully. One week later a user calls, he is having trouble with this application. The package I created supports the reinstallation(either by removing the software and installing it again, or with a repair functionality). But, in SCCM I have no option(neither found a right click tool which could do that), to reinstall the package. I have to remove it, and then install it again.
I thought I could write a program to that, and create my own "Right Click Tool", but I can't find any information what exactly should I do?
So my questions are:
- Is there a Right Click Tool which can reinstall an application somehow?
- Is there some documentation, where I could get some information about this issue?
I am sure, many others have the same problem.
Or I didn't find a way, because there is none? :(
Thanks in advance!
In our company we would also have to liked this and did some basic research but as it seems no one has done it so far (doesn't mean it's impossible). We also talked to two Microsoft SCCM consultants about it and both said there is currently no way.
The thing is applications are all about the detection method. It is the only thing that will trigger the setup. So if you have a software, and you don't want to deploy it as package (this is still possible and they can still be rerun with tools like Roger Zanders client center), what you can do is use a detection method that you can influence. Like a file or reg key, which you can remove remotely. The application deployment evaluation cycle can be triggered remotely just like all other client actions so this would not be a problem.
Sadly this is only a workaround and I would very much like if someone proves me wrong but so far this is the best we could come up with.
Edit:
So you motivated me to dig a little deeper and I also got some really good slides from Microsoft and I found some possible approach:
As the application is all about detection all the time my idea is to fake it.
As far as I can tell detection methods are saved in some crazy xml format in the WMI in root\ccm\CIModels in the Class Local_Detect_Synclet. So I wrote a script that goes there and replaces the detection method with an empty detection method that checks for a file. It has no properties so it can never work. After that I call the enforce method on my application using the class CCM_AppDeliveryType in the same namespace. It takes an AppdeliveryTypeID and the current revision but both of those can be seen in CCM_AppDeliveryTypeSynclet. After the enforce reinstalled the program I reset the detection method to the old one and trigger a second enforce which will do nothing but tell the system the app is properly detected. The Vbscript that does all this looks like this:
computername = "WS0000xxxx"
Set wmiCIModels = GetObject("winmgmts:\\" & computername & "\root\ccm\CIModels")
Set wmiAppDeliveryType = GetObject("winmgmts:\\" & computername & "\root\ccm\CIModels:CCM_AppDeliveryType")
deploymentTypeID = "ScopeId_79903130-730F-48B7-8165-6088B83359BE/DeploymentType_68a80836-208e-401b-a69f-ae4c184b9f85"
Set installedDelTypes = wmiCIModels.ExecQuery("select * from Local_Detect_Synclet where AppDeliveryTypeId = '" & deploymentTypeID & "'" )
For Each instDelType In installedDelTypes
strOldDetectionMethod = instDelType.ExpressionXml
instDelType.ExpressionXml = "<LocalDetectionMethod><Settings><File></File></Settings><Rule xmlns=""http://schemas.microsoft.com/SystemsCenterConfigurationManager/2009/06/14/Rules""><Expression><Operator>Equals</Operator><Operands><SettingReference AuthoringScopeId="""" DataType=""Version"" SettingLogicalName=""X"" Method=""Value""/><ConstantValue DataType=""Version""/></Operands></Expression></Rule></LocalDetectionMethod>"
instDelType.Put_ 0
wmiAppDeliveryType.EnforceApp deploymentTypeID, "4", "", "Install", "", "1"
instDelType.ExpressionXml = strOldDetectionMethod
instDelType.Put_ 0
wmiAppDeliveryType.EnforceApp deploymentTypeID, "4", "", "Install", "", "1"
Next
The ExpressionXML is horribly long but I deleted everything from it by trial and error that seems possible.
For simplicity I hardcoded the AppdeliveryTypeID and revision but you can get a list of those, including nice names, with the following query:
Set wmiCIModels = GetObject("winmgmts:\\" & computername & "\root\ccm\CIModels")
Set delTypes = wmiCIModels.ExecQuery("select * from CCM_AppDeliveryTypeSynclet" )
For Each delType In delTypes
WScript.Echo "Deployment Name: " & delType.AppDeliveryTypeName & VbCrLf & "AppdeliveryTypeID: " & delType.AppDeliveryTypeId & VbCrLf & "Revision: " & delType.Revision & VbCrLf
Next
This also presents on of the biggest downsides I found so far. It is only possible to easily get the names of the AppDeliveries. These are not the names the application will show in the Software Center for example. I looked into translating this but the only method I found so far is to query the SCCM DB itself and the query is horribly complicated and needs of course some SCCM admin to execute it.
As with all WMI scripts you can execute it remotely by specifying a computer name or locally if you use '.', so you could use the SCCM Console Extension system and build your own right-click tool based on this.
This will work even on Applications that are not installed similar to the rerun of packages. If you do not want this you can check against CCM_AppDeliveryType to only see the installed applications.
Within VB6 I have used the following code to add to Registry,
Dim x As Object
x = CreateObject("WScript.Shell")
x.RegWrite "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell", "MADNESS"
It creates a key, however in the following location:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
Instead of:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
Any help is appreciated.
It looks like it's because you have a 64-bit OS and you're running a 32-bit (x86) application which is handled by Windows'"Windows On Windows" (WOW) feature. Basically, it allows you to run an x86 program on an x64 Windows OS. x86 programs usually have their own registry key structure (ie. the Wow6432Node) and their own program files folder (ie. Program Files (x86)). You can try using something like this which uses WinAPI hooks. This may allow you to bypass the issue and write directly to the key you want. Though, I'm not sure if Windows has security measures in place that prevent x86 applications to write to x64 parts of the registry. (I can't see why it would.)
You can shell C:\Windows\System32\cscript.exe which is the x64 version.
FYI: The x86 version is C:\Windows\SysWOW64\cscript.exe
First off, I would stop using WScript.Shell and switch to this registry access class: http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=70915&lngWId=1
It's a little confusing to use at first, but it's solid and provides built-in support for accessing 64-bit registry entries rather than the re-directed Wow6432Node. You can do so simply by setting the desired access in the .Path method of the class. Here's an untested example:
Dim objRegistry as New UniRegistry
Dim objHKLMStartup as New UniRegistry
Set objHKLMStartup = objRegistry.Path([hKey Local Machine], "SOFTWARE\Microsoft\Windows\CurrentVersion\Run", [Registry: Read] + [Registry: WOW64 64-bit])
You can then use a For Each variant In objHKLMStartup to extract any/all values in the given registry path.
I need to develop a process that will detect if the users computer has certain programs installed and if so, what version. I believe I will need a list with the registry location and keys to look for and feed it to the program which is not a problem. Is there a better way to accomplish this?
My first thought was to check in the registry in the uninstallation entries but it seems one of the apps I wish to detect does not have one. What is the standard location for all registry using applications to make an entry in?
On 64-bit systems the x64 key is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
Most programs are listed there. Look at the keys:
DisplayName
DisplayVersion
Note that the last is not always set!
On 64-bit systems the x86 key (usually with more entries) is:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
User-specific settings should be written to HKCU\Software, machine-specific settings to HKLM\Software. Under these keys, structure [software vendor name]\[application name] (e.g. HKLM\Software\Microsoft\Internet Explorer) may be the most common, but that's just a convention, not a law of nature.
Many (most?) applications also add their uninstall entries to HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\[app name], but again, not all applications do this.
These are the most important keys; however, contents of the registry do not have to represent the installed software exactly - maybe the application was installed once, but then was manually deleted, or maybe the uninstaller didn't remove all traces of it. If you want to be sure, check the filesystem to see if the application still exists where its registry entries say it is.
Edit:
If you're a member of the group Administrators, you can check the HKEY_USERS hive - each user's HKCU actually resides there (you'll need to know the user SID, or go through all of them).
Note: As #Brian Ensink says, "installed" is a bit of a vague concept - are we trying to find what the user could run? Some software doesn't even write to the Registry at all: search for "portable apps" to see apps that have been specifically modified to run directly from media (CD/USB) and not to leave any traces on the computer. We may also have to scan the disks, and network disks, and anything the user downloads, and world-accessible Windows shares in the Internet (yes, such things exist legitimately - \\live.sysinternals.com\tools comes to mind). In this direction, there's no real limit of what the user can run, unless prevented by system policies.
You could use MSI API to enumerate everything installed by Windows Installer but that won't list all the software available on a machine. Without knowing more about what you need I think the concept of "installed" is a little vague. There are many ways to deploy software to a system ranging from big complicated installers to ZIP files and everything in between.
An application does not need to have any registry entry. In fact, many applications do not need to be installed at all. U3 USB sticks are a good example; the programs on them just run from the file system.
As noted, most good applications can be found via their uninstall registry key though. This is actually a pair of keys, per-user and per-machine (HKCU/HKLM - Piskvor mentioned only the HKLM one). It does not (always) give you the install directory, though.
If it's in HKCU, then you have to realise that HKEY_CURRENT_USER really means "Current User". Other users have their own HKCU entries, and their own installed software. You can't find that. Reading every HKEY_USERS hive is a disaster on corporate networks with roaming profiles. You really don't want to fetch 1000 accounts from your remote [US|China|Europe] office.
Even if an application is installed, and you know where, it may not have the same "version" notion you have. The best source is the "version" resource in the executables. That's indeed a plural, so you have to find all of them, extract version resources from all and in case of a conflict decid on something reasonable.
So - good luck. There are dozes of ways to fail.
You can use a PowerShell script to look at registers and get the installed program details. The script bellow will generate a file with the complete list of installed programs. Save it with ".ps" extension and double click the file.
#
# Generates a full list of installed programs.
#
# Temporary auxiliar file.
$tmpFile = "tmp.txt"
# File that will hold the programs list.
$fileName = "programas-instalados.txt"
# Columns separator.
$separator = ","
# Delete previous files.
Remove-Item $tmpFile
Remove-Item $fileName
# Creates the temporary file.
Create-Item $tmpFile
# Searchs register for programs - part 1
$loc = Get-ChildItem HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall
$names = $loc |foreach-object {Get-ItemProperty $_.PsPath}
foreach ($name in $names)
{
IF(-Not [string]::IsNullOrEmpty($name.DisplayName)) {
$line = $name.DisplayName+$separator+$name.DisplayVersion+$separator+$name.InstallDate
Write-Host $line
Add-Content $tmpFile "$line`n"
}
}
# Searchs register for programs - part 2
$loc = Get-ChildItem HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
$names = $loc |foreach-object {Get-ItemProperty $_.PsPath}
foreach ($name in $names)
{
IF(-Not [string]::IsNullOrEmpty($name.DisplayName)) {
$line = $name.DisplayName+$separator+$name.DisplayVersion+$separator+$name.InstallDate
Write-Host $line
Add-Content $tmpFile "$line`n"
}
}
# Sorts the result, removes duplicate lines and
# generates the final file.
gc $tmpFile | sort | get-unique > $filename
Seems like looking for something specific to the installed program would work better, but HKCU\Software and HKLM\Software are the spots to look.
In addition to all the registry keys mentioned above, you may also have to look at HKEY_CURRENT_USER\Software\Microsoft\Installer\Products for programs installed just for the current user.
Win32_Product never shows everything, only software installed via an MSI installer (as far as I can tell.)
There are lots of software packages that get installed via other installers that don't show up in there. another way is needed.
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Compatibility Assistant\Persisted