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
Related
I have a Powershell script on Win10 that needs to start multiple other programs, and finding that in a lot of cases I have to go looking for the full path to the executable file, and potentially hard-code that path in the script, which is not great for robustness or portability between machines.
On *nix systems, it is expected that most programs can be started from the command line, so each application will generally either add its install folder to the PATH environment variable, or be inserted (e.g. via symlinks) into a directory like /usr/bin that's already in the PATH.
Microsoft discourages the use of the PATH variable. Instead applications are SUPPOSED to insert a key for their main .exe file into one of two places in the registry, depending on whether the app is installed for one user or all users-
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths
The (default) value under that key is the full path to the executable file.
(ref. https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#finding-an-application-executable)
Except- it seems an awful lot of applications don't follow this recommendation. (Micrsoft's own PowerToys, Keepass2, FreeFileSync, Free Download Manager are just some examples).
These programs do (when not installed as portable apps) have a presence in the Registry, it's just not in the officially recommended places.
So, my question is- Is there an effective way to get a good (not necessarily perfect) general answer to the question "what is the full path to installed program X.exe" on Windows? at least where that program has saved a path somewhere in the registry.
This is similar to the question asked here- Equivalent of *Nix 'which' command in PowerShell? but with the important difference that I want to include "badly behaved" programs that don't correctly configure the PATH or the two registry keys described above.
Ideally this would be a function that could be called on the fly with the program name as a parameter that returns the pathname, but worst case if it requires a brute force search across the whole registry to find and then de-duplicate all the paths to .exe files under the %PROGRAMFILES% folders (64 and 32 bit), I guess I could run this once and save the result in a file as a hash table.
For MSI packages you could do:
$software = Get-CimInstance -Query "select Vendor,Name,Version,InstallLocation,Language from win32_product" | Select-Object -Property Vendor,Name,Version,InstallLocation,Language
Output:
This gives you at least the install location.
You may also take a look at get-StartApps, e.g.:
Get-StartApps | ?{$_.appid -match "\.exe"}
I have my own installer program which I use to install several applications I have written.
I have been updating this program to avoid the application's data file updates going to the user's VirtualStore, as I read this was a "bad thing". I am doing this by storing the program and common data files separately in their correct locations, instead of sticking everything in Program Files like we used to do in the days of XP.
I am also now using SHGetFolderPath (yes, it's deprecated, but I still need to support my XP users), to get known folders instead of trawling the registry, which is another "bad thing" (I read).
The next thing I was trying to do was rewrite the installer code to avoid registry redirection to Wow6432Node when writing stuff to HKLM, as I thought this was another "bad thing".
However, although I could put the application specific stuff that goes there (like the install folder, if the user decides to install in other than the default), the killer is the need to put the uninstall info in HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall. For 32-bit stuff running on a 64-bit system, this is redirected to Wow6432Node. I don't see any way round this - is it in fact possible?
UAC registry redirection could reasonably be described as a "bad thing" because it is designed specifically to provide compatibility with improperly written software, i.e., software that assumes it is going to be run with administrator privilege.
WOW64 registry redirection is a different beast; it's designed to provide compatibility with properly written 32-bit software. If your software is 32-bit, and has no specific need to be 64-bit aware, there's nothing wrong with letting it run in the emulation environment as-is, including allowing registry settings to be redirected to Wow6432Node.
You can bypass WOW64 registry redirection if necessary, but you shouldn't do this arbitrarily, only if there is a specific reason. If WOW64 redirection worries you, the only good alternative is to provide a 64-bit version of your program.
This is a very "BAD THING" !
Microsoft solves a security problem by hiding thing at another place !
I've created a programm to ask the user for some additional parameters (language, directory for lessons) during installation of the app. I tested the programm profoundly, and the registry entries were made perfectly. However during setup the entries were hidden at some strange place!
If Microsoft wants to redirect these enties it should at least be some option to be set.
Never hide things, and think they will not notice, because it's transparant.
Doing this properly would involve an option to be set, so users are aware of the redirection !
I'm working on extending an existing USB device driver, and have been struggling learn how the .INF file works. I believe that I have it right now, the driver installs and works. It also shows up in the "Add/Remove Programs" list.
I find that when I uninstall the driver from Add/Remove, it seems to do little if anything. This was in the .INF as I inherited it:
[myUninstall]
DelReg = myDelReg
[MyDelReg]
HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\myName"
I can find no other references, not even where "myName" is installed in the registry Uninstall.
I've changed the .INF to have this:
[DefaultUninstall]
myUninstall
[myUninstall]
DelFiles = ... a list of files ...
DelDirs = UNINST.Dirs
DelReg = UNINST.Regs
Cleanup = 1
[UNINST.Dirs]
%16422%\%InstallDir%
[UNINST.Regs]
myDev.AddReg
myDev.CommonRegs
[myDev.AddReg] ; same definition used for installing
HKR,,Drivers,Subclasses,,"wave,midi,mixer,aux"
...
[myDev,CommonRegs] ; same definition used for installing
HKR, Parameters, BufferSize, 0x0010001, 256
...
Still, whether I uninstall from the Control Panel Add/Remove programs or do it from the device manager, the system doesn't complain and nothing seems to happen. I've looked at the output of "dpinst /c" and the setupapi.log, and neither tell me much. dpinst shows no error but is clearly not even trying to execute my uninstall section.
I find a lot of verbiage online and examples of uninstall sections, but very little in the way of explanation. I did stumble across one sentence in 1 forum today that implied that PnP device .INF's don't use Uninstall. Perhaps this is my issue?
am I wrong in thinking that I can have Uninstall in this .INF?
is there some other way to tell what is happening?
is my only recourse to write a separate uninstaller?
AFIK, the INF files do not participate in uninstallation.
You will need to write a separate uninstaller if you want to perform proper cleanup.
Further note that with Vista and beyond, Windows also includes a Driver Store, where it silently copies the driver package (inf and files references from the inf). If you want to perform a full cleanup, the driver store should also be cleaned up.
I don't think DefaultUninstall is a standard section in the way DefaultInstall is. Often you will see the UninstallString ARP entry just calling DefaultUninstall by name.
The documentation in this area is pretty bad and there is a split between the basic SetupAPI/SetupX INF files and the Advanced INF files (AdvPack).
The only 3rd-party exception I know of is TweakUI.inf from the Windows 95 PowerToys.
It has parts that look like this:
...
[Optional Components]
TweakUIInstall
[TweakUIInstall]
CopyFiles = ...
AddReg = ...
Ini2Reg = ...
...
InstallType = 10 ;Typical, Custom.
Uninstall = DefaultUnInstall
[DefaultUnInstall]
DelFiles = ...
DelReg = ...
But I think that only works with the optional components feature, not basic uninstallation. TweakUI v1.33 (Tweakui.exe SFX archive) has some more NT related entries and I believe the way it writes the UninstallString for NT even writes the wrong INF section. Wrong or not, it manually has to specify a INF section for normal uninstall.
I have been trying to find a way to "defragment" the registry on my Windows machine. Firstly, does this make sense? Any benefits in doing this? (Not much love on superuser.com) Secondly, I am looking for a way to rewrite the registry using C/C++ with Windows API. Is there a way to read the registry and write it to a new file getting rid of unused bytes along the way? (I might have to write the new file and then boot into another OS/disk before I can overwrite the original... but I am willing to take that risk.)
Microsoft's PageDefrag does exactly this, as it states on its page "PageDefrag uses the standard file defragmentation APIs to defragment the files."
(A copy of the linked article is here because in typical MSDN style their link is dead.)
http://www.larshederer.homepage.t-online.de/erunt/ - NTREGOPT NT Registry Optimizer
Similar to Windows 9x/Me, the registry files in an NT-based system
can become fragmented over time, occupying more space on your hard
disk than necessary and decreasing overall performance. You should
use the NTREGOPT utility regularly, but especially after installing
or uninstalling a program, to minimize the size of the registry files
and optimize registry access.
The program works by recreating each registry hive "from scratch",
thus removing any slack space that may be left from previously
modified or deleted keys.
http://reboot.pro/index.php?showtopic=11212 - Offreg.dll MS WDK Offline Registry Library
The offline registry library (Offreg.dll) is used to modify a registry hive outside the active system registry. This library is intended for registry update scenarios such as servicing an operating system image. The library supports registry hive formats starting with Windows XP.
Developer Audience
http://reboot.pro/topic/11312-offline-registry/ - Offline Registry MS WDK Command-Line Tool
A command line tool that will allow one to read and write to an offline registry hive.
Reading the values should be possible.
But I've never seen any spec for how the registry files are written to disk, and unless you could find one you'd have to reverse engineer those files in your OS (might be differences between XP and 7 etc). Then you have to remember that the registry isn't just one file, it's multiple files and some of them belongs to certain users and I think they use SIDs rather than user names so even if you move them to a new computer, you have to be sure it's the same OS version with the same users with the same SIDs set up on it.
All this for little or no gain so I'd agree with the superuser users that it wouldn't make sense.
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