I recently had a need to automatically find the installation location of a particular application without forcing the user to have the app running or go through a dialog to navigate to the directory. It's not 100% reliable, but often it appears to be possible to do this by querying the "HKEY_LOCAL_MACHINE" registry at:
- "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
- "SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
So I put together the below C# inline function for querying the above registry keys to obtain the application name (as seen in the registry) and (if possible) the installation location of the application. I figured I'd share it in case others may have some use for it. The function outputs the app name and installation directory for all apps that have installation directory data found in at least one of the above registry keys, and since the output is to the event log you may need to increase the log's maximum displayed lines if you want to see them all (right click anywhere in the log to access this option). If VoiceAttack detects you have a 64-bit operating system the process will go through both above keys and provide each data set in its own color. If you have a 32-bit system you will only loop through one of the keys.
Like I mentioned earlier the below code is set to output only apps that have installation info available in the above two keys. I've left two additional lines of code commented out at around line 97. The first line shows the "DisplayName" for all apps found in the above keys, and the second line shows the key, "DisplayName," and installation directory (each item will return "*" if no match is found) for all apps in the above keys. Just add/remove commenting "//" to output what you want.
It's important to reiterate that finding app install paths via the registry can be unreliable because the quality of the data loaded into the registry and where in the registry that data gets loaded depends on the app's installation process. For instance, on my Windows 7 PC I could not find the Chrome installation directory with the function, but I could find it on my Windows 10 PC. Also please note that I only have access to PCs with 64-bit Windows, so I have not tested this function within a 32-bit environment. I also have not tested this to find apps from the U-Play (Ubisoft) or Origin (EA) platforms, but I was able to achieve my end goal and find the installation paths for Steam apps.
Cheers!
Referenced Assemblies = System.dll;
// Referenced Assemblies = System.dll
using System;
using Microsoft.Win32;
public class VAInline
{
public void main()
{
RegistryKey RootKey = null; // Initialize RegistryKey for containing the root (base) key to evaluate
string myRegistryView = null; // Initialize string variable for storing the type of registry view
string UninstallKey1 = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; // Parent registry key for finding all 32-bit apps in 32-bit Windows and 64-bit apps in 64-bit Windows
string UninstallKey2 = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"; // Parent registry key for finding 32-bit apps in 64-bit Windows
string[] keyList; // Initialize string array for storing registry keys for process looping
if (Environment.Is64BitOperatingSystem) // Check if operating system is 64-bit
{
myRegistryView = "64-bit"; // Store type of registry view
RootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); // Opens registry's LocalMachine key with a 64-bit view
keyList = new string[2]; // Resize keyList array
keyList[0] = UninstallKey1; // Store registry key in string array
keyList[1] = UninstallKey2; // Store registry key in string array
}
else // Operating system is 32-bit
{
myRegistryView = "32-bit"; // Store type of registry view
RootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); // Opens registry's LocalMachine key with a 32-bit view
keyList = new string[1]; // Resize keyList array
keyList[0] = UninstallKey1; // Store registry key in string array
}
VA.WriteToLog("Registry View = " + myRegistryView, "blue"); // Output the registry view to the VoiceAttack event log
string OutputColor = "orange"; // Initialize string variable for storing the color of text outputted to the VoiceAttack event log
foreach (string parentKey in keyList) // Loop through each parentKey in keyList
{
VA.WriteToLog("Registry Key ==> " + parentKey, OutputColor); // Output current parentKey to VoiceAttack event log
using (RegistryKey childKey = RootKey.OpenSubKey(parentKey)) // Open subkey of parentKey and store in childKey. "using" implemented for proper disposal.
{
foreach (string subkeyName in childKey.GetSubKeyNames()) // Loop through each subkeyName in childKey
{
using (RegistryKey subKey = childKey.OpenSubKey(subkeyName)) // Open subkey of childKey (based on subkeyName) and store in mySubKey. "using" implemented for proper disposal.
{
string DisplayName; // Initialize string variable for storing application DisplayName from registry
try { DisplayName = subKey.GetValue("DisplayName").ToString().Trim(); } // Try to retrieve the DisplayName of the current registry subKey
catch { DisplayName = "*"; } // subKey doesn't have a "DisplayName" value, so store empty placeholder character in DisplayName
DisplayName = (DisplayName == "" ? DisplayName = "*" : DisplayName);
string InstallDirectory; // Initialize string variable for storing application installation directory from registry
try // Attempt the following code...
{
InstallDirectory = subKey.GetValue("InstallLocation").ToString(); // Retrieve the "InstallLocation" of the current registry subKey (application)
if (InstallDirectory == "") // Check if InstallDirectory is blank
InstallDirectory = null; // Set InstallDirectory as null
else if (InstallDirectory.EndsWith(@"\") == false) // Check if InstallDirectory does NOT end with "\"
InstallDirectory += @"\"; // Append "\" to InstallDirectory
}
catch { InstallDirectory = null; } // subKey doesn't have an "InstallLocation" value, so set InstallDirectory as null
if (InstallDirectory == null) // Check if InstallDirectory is null
{
try // Attempt the following code...
{
string InnoAppPath = subKey.GetValue("Inno Setup: App Path").ToString(); // Retrieve the "Inno Setup: App Path" of the current registry subKey (application)
if (InnoAppPath == "") // Check if InnoAppPath is blank
InstallDirectory = null; // Set InstallDirectory as null
else if (InnoAppPath.EndsWith(@"\") == false) // Check if InnoAppPath does NOT end with "\"
InstallDirectory = InnoAppPath + @"\"; // Append "\" to InnoPath and store this in InstallDirectory
else
InstallDirectory = InnoAppPath; // Set InstallDirectory equal to InnoAppPath
}
catch { InstallDirectory = null; } // subKey doesn't have an "Inno Setup: App Path" value, so keep InstallDirectory null
if (InstallDirectory == null) // Check if InstallDirectory is null
{
try // Attempt the following code...
{
string DisplayIcon = subKey.GetValue("DisplayIcon").ToString(); // Retrieve the "DisplayIcon" of the current registry subKey (application)
if (DisplayIcon.EndsWith(".exe") == true) // Check if DisplayIcon ends with ".exe"
InstallDirectory = DisplayIcon.Remove(DisplayIcon.LastIndexOf(@"\") + 1); // Trim down DisplayIcon text and store the .exe folder path in InstallDirectory
}
catch { InstallDirectory = null; } // subKey doesn't have a "DisplayIcon" value, so keep InstallDirectory null
if (InstallDirectory == null) // Check if InstallDirectory is null
{
try // Attempt the following code...
{
string InstallDir = subKey.GetValue("InstallDir").ToString(); // Retrieve the "InstallDir" of the current registry subKey (application)
if (InstallDir == "") // Check if InstallDir is blank
InstallDirectory = null; // Set InstallDirectory as null
else if (InstallDir.EndsWith(@"\") == false) // Check if InstallDir does NOT end with "\"
InstallDirectory = InstallDir + @"\"; // Append "\" to InstallDir and store in InstallDirectory
else
InstallDirectory = InstallDir; // Set InstallDirectory equal to InstallDir
}
catch { InstallDirectory = null; } // subKey doesn't have an "InstallDir" value, so keep InstallDirectory null
}
}
}
if (InstallDirectory == null) // Check if InstallDirectory is null
InstallDirectory = "*"; // Set InstallDirectory to placeholder character "*"
else
InstallDirectory = InstallDirectory.Replace(@"""", "").Trim(); // Replace andy double quotes and trim content in InstallDirectory
//VA.WriteToLog(DisplayName, OutputColor); // Output DisplayName value to VoiceAttack event log
//VA.WriteToLog(subkeyName + " = " + DisplayName + " = " + InstallDirectory, OutputColor); // Output subkeyName, DisplayName, and InstallDirectory values to VoiceAttack event log
if (InstallDirectory != "*") // Check if InstallDirectory is NOT "*" (i.e., InstallDirectory has content of interest)
VA.WriteToLog(DisplayName + " = " + InstallDirectory, OutputColor); // Output DisplayName and InstallDirectory values to VoiceAttack event log
}
}
OutputColor = "purple"; // Change the event log output color
}
}
}
}
// References:
// https://stackoverflow.com/questions/908850/get-installed-applications-in-a-system
// https://social.msdn.microsoft.com/Forums/windows/en-US/afb5012a-30f1-4b96-9931-a143fd76bab5/how-to-find-path-of-installed-programs-in-c?forum=winformssetup
// https://social.msdn.microsoft.com/Forums/en-US/94c2f14d-c45e-4b55-9ba0-eb091bac1035/c-get-installed-programs?forum=csharplanguage
// https://stackoverflow.com/questions/24909108/get-installed-software-list-using-c-sharp
// https://stackoverflow.com/questions/974038/reading-64bit-registry-from-a-32bit-application
// https://stackoverflow.com/questions/13030814/reading-value-from-registry
Edit: fixed some code issues and revised summary