Author Topic: Get List of Installed Apps  (Read 5116 times)

Exergist

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 405
  • Ride the lightning
Get List of Installed Apps
« on: January 17, 2018, 11:04:30 AM »
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;
Code: [Select]
// 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
« Last Edit: January 19, 2018, 10:47:42 PM by Exergist »