Getting All Installed Programs From Windows Registry Using Multi-Threading

I’ve wrote a multi-threaded example for you. It does retrieve all programs if those values exist. You will have to append your own filter to the returning functions so they only return the values you want, that is of course if you are looking for specific programs. Currently it will return all programs installed. The code that returns the keys values is this :

 private static bool All_KeyValues_Exist(RegistryKey subkey)
 {
 string app_Name = (string)subkey.GetValue("DisplayName");
 string publisher_Name = (string)subkey.GetValue("Publisher");
 string installLocation = (string)subkey.GetValue("InstallLocation");
 if (string.IsNullOrEmpty(app_Name) || string.IsNullOrEmpty(publisher_Name) || string.IsNullOrEmpty(installLocation))
 return false;
 else
 return true;
 }

If you want to get additional keys, then you can go here : SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall to look at the available values for those keys using regedit.exe. For each value you want to include, you will need to add them, just as I have done below :

 string app_Name = (string)subkey.GetValue("DisplayName");
 string publisher_Name = (string)subkey.GetValue("Publisher");
 string installLocation = (string)subkey.GetValue("InstallLocation");

This piece of code enumerates all the keys in the hive and then iterates over the subkeys to get the values. Note :

if (All_KeyValues_Exist(subkey))
=> If any of the keys names are null or don’t exist, they will not be allowed be entered into the program_Values list, and subsequently won’t be shown in the UI. This next bit of code is responsible for returning a list of the currently found applications :

 public static List GetInstalled_Programs()
 {
 List program_Values = new List();
 program_Values.AddRange(ReadProgram_Instalations(RegistryView.Registry32));
 program_Values.AddRange(ReadProgram_Instalations(RegistryView.Registry64));
 return program_Values;
 }

All of these keys are being pulled from: @”SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall”; And it works by the following code kicking off a background worker. I’ve used a background worker, because this enumeration process can take a few seconds to complete, and if you were to execute this code on the UI thread, it would likely freeze until the process completed. I have added two events as you can see, the DoWork event, and the RunWorkerCompleted event. Once the worker has been executed, the work will begin. See :

private void MainMethod()
{
 bgWorker.DoWork += BgWorker_DoWork;
 bgWorker.RunWorkerCompleted += BgWorker_RunWorkerCompleted;
 bgWorker.RunWorkerAsync();
}

The worker is responsible for getting the necessary registry values from the hive, and then returning them as a list, that list is defined as :

public List result = new List();.

When the worker begins execution :

private void BgWorker_DoWork(object sender, DoWorkEventArgs e)
{
 result = Get_Installed_Software.GetInstalled_Programs();
 if (result != null)
 {
 result.ForEach((string item) => listBox1.Invoke(new DelegateCallback(UpdateUI),
 new string[] { item })); /* Use string.split to split at "," */
 }
}

It will return a result, then ensure the result is not null and begin iterating through the list, and then invokes the control on the UI thread by passing the iterated values through the UpdateUI method which is a thread-safe operation. See :

private void UpdateUI(string item)
{
 listBox1.Items.Add(item);
}

The reason we need to do this and update the UI by invoking the control is because the background worker is currently running on a NON-UI thread, which our control (Listbox) was not created on. The delegate action basically puts us back on “talking terms” with our UI, and allows us to safely add the new value before returning to repeat the process over. The all important line for allowing this to happen is our DelegateCallback:

public delegate void DelegateCallback(string s);

Once our work has completed, the background worker :

public BackgroundWorker bgWorker = new BackgroundWorker(); will ensure the completed events is fired where we then remove our events:

private void BgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
 bgWorker.DoWork -= BgWorker_DoWork;
 bgWorker.RunWorkerCompleted -= BgWorker_RunWorkerCompleted;
}

While I admit, this is obviously not the best place to remove these, but It is safe and it does work. What you may want to do is add some kind of spinning ball animation to let your users know that the UI is currently executing a task and to be patient.

You could also do that from the background workers do work event. Using the template I’ have provided for multithreading, you would simply setup a new thread to execute the animation, so that too does not hog your UI and freeze your application. The finished code assembled should look like this:

using Microsoft.Win32;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
namespace TestCSharpApp
{
 public partial class Form1 : Form
 {
 public Form1()
 {
 InitializeComponent();
 MainMethod();
 }
 public List result = new List();
 public BackgroundWorker bgWorker = new BackgroundWorker();
 public delegate void DelegateCallback(string s);
 private void MainMethod()
 {
 bgWorker.DoWork += BgWorker_DoWork;
 bgWorker.RunWorkerCompleted += BgWorker_RunWorkerCompleted;
 bgWorker.RunWorkerAsync();
 }
 private void BgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
 {
 bgWorker.DoWork -= BgWorker_DoWork;
 bgWorker.RunWorkerCompleted -= BgWorker_RunWorkerCompleted;
 }
 private void BgWorker_DoWork(object sender, DoWorkEventArgs e)
 {
 result = Get_Installed_Software.GetInstalled_Programs();
 if (result != null)
 {
 result.ForEach((string item) => listBox1.Invoke(new DelegateCallback(UpdateUI),
 new string[] { item })); /* Use string.split to split at "," */
 }
 }
 private void UpdateUI(string item)
 {
 listBox1.Items.Add(item);
 }
 }
 public static class Get_Installed_Software
 {
 private const string reg_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
 public static List GetInstalled_Programs()
 {
 List program_Values = new List();
 program_Values.AddRange(ReadProgram_Instalations(RegistryView.Registry32));
 program_Values.AddRange(ReadProgram_Instalations(RegistryView.Registry64));
 return program_Values;
 }
 private static IEnumerable ReadProgram_Instalations(RegistryView registryView)
 {
 List program_Values = new List();
 using (RegistryKey key = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView).OpenSubKey(reg_key))
 {
 for (int i = 0; i < key.GetSubKeyNames().Length; i++)
 {
 using (RegistryKey subkey = key.OpenSubKey(key.GetSubKeyNames()[i]))
 {
 if (All_KeyValues_Exist(subkey))
 {
 program_Values.Add(string.Concat((string)subkey.GetValue("DisplayName"), ", ", (string)subkey.GetValue("Publisher"), ", ", (string)subkey.GetValue("InstallLocation")));
 }
 }
 }
 }
 return program_Values;
 }
 private static bool All_KeyValues_Exist(RegistryKey subkey)
 {
 string app_Name = (string)subkey.GetValue("DisplayName");
 string publisher_Name = (string)subkey.GetValue("Publisher");
 string installLocation = (string)subkey.GetValue("InstallLocation");
 if (string.IsNullOrEmpty(app_Name) || string.IsNullOrEmpty(publisher_Name) || string.IsNullOrEmpty(installLocation))
 return false;
 else
 return true;
 }
 }
}