25

Right now I use this to list all the applications listed in the registry for 32bit & 64. I have seen the other examples of how to check if an application is installed without any luck.

string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
    foreach (String a in key.GetSubKeyNames())
    {
        RegistryKey subkey = key.OpenSubKey(a);
        Console.WriteLine(subkey.GetValue("DisplayName"));
    }
}

registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
    foreach (String a in key.GetSubKeyNames())
    {
        RegistryKey subkey = key.OpenSubKey(a);
        Console.WriteLine(subkey.GetValue("DisplayName"));
    }
}

So this snippet lists it all in the console window and what I am trying to do is just find one program title out of the list of display names to see if it's installed.

The last thing I tried was

if (subkey.Name.Contains("OpenSSL"))
    Console.Writeline("OpenSSL Found");
else
    Console.Writeline("OpenSSL Not Found");

Anything I tried came back either false or a false positive. Is there anyone that can show me how to just grab a title out of the list?

Please don't post up the well-known private static void IsApplicationInstalled(p_name) function. It does not work for me at all.

Mo Patel
  • 2,321
  • 4
  • 22
  • 37
Hyperion
  • 869
  • 1
  • 8
  • 23
  • 4
    As a side-note: you need to `Close()` the regkeys you've `Opened()`'d.. even though this is managed code, those are unmanaged resources and will leak if you don't close them. – canhazbits May 04 '13 at 21:32
  • This sounds like it could be quite useful. However, depending on what you want to use it for, you may be better off tailoring the is-installed code to the specific application to make it run faster. – Sildoreth Jun 16 '14 at 21:15
  • Also, not all programs get registered in the "uninstall" list. Just FYI. – Sildoreth Jun 16 '14 at 21:17

7 Answers7

31

After searching and troubleshooting, I got it to work this way:

public static bool checkInstalled (string c_name)
{
    string displayName;

    string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey);
    if (key != null)
    {
        foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
        {
            displayName = subkey.GetValue("DisplayName") as string;
            if (displayName != null && displayName.Contains(c_name))
            {
                return true;
            }
        }
        key.Close();
    }

    registryKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
    key = Registry.LocalMachine.OpenSubKey(registryKey);
    if (key != null)
    {
        foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
        {
            displayName = subkey.GetValue("DisplayName") as string;
            if (displayName != null && displayName.Contains(c_name))
            {
                return true;
            }
        }
        key.Close();
    }
    return false;
}

And I simply just call it using

if(checkInstalled("Application Name"))
Mo Patel
  • 2,321
  • 4
  • 22
  • 37
Hyperion
  • 869
  • 1
  • 8
  • 23
  • 1
    This Solution won't always work, because not all Entry are always human readable Names, the most Entries are GUID's, then you have to check what Application this GUID represent – feldeOne Sep 19 '18 at 12:45
  • @feldeOne At the time, this code was only for checking if specific applications installed with provided folder names – Hyperion Sep 20 '18 at 14:17
  • @feldeOne it dont work when I uninstall application. It still show it is on my cpu. – Mat Rat Apr 17 '20 at 08:41
  • Problem is when you run 32 bits application it return 32 bits programs when it should 64 bits. – Silny ToJa May 07 '20 at 12:11
19

This is a clean way to do this without that much code.

    private static bool IsSoftwareInstalled(string softwareName)
    {
        var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") ??
                  Registry.LocalMachine.OpenSubKey(
                      @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");

        if (key == null)
            return false;

        return key.GetSubKeyNames()
            .Select(keyName => key.OpenSubKey(keyName))
            .Select(subkey => subkey.GetValue("DisplayName") as string)
            .Any(displayName => displayName != null && displayName.Contains(softwareName));
    }

Call it with a if-statement:

if (IsSoftwareInstalled("OpenSSL"))
Stellan Lindell
  • 2,605
  • 1
  • 13
  • 11
7

I have checked @Stellan Lindell's code and it doesn't work in all cases. My version should work in all scenarios and checks the specific version of installed programs(x86, x64).

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;

namespace Test
{  
internal class Program
{  
    public enum ProgramVersion
    {
        x86,
        x64
    }

    private static IEnumerable<string> GetRegisterSubkeys(RegistryKey registryKey)
    {
        return registryKey.GetSubKeyNames()
                .Select(registryKey.OpenSubKey)
                .Select(subkey => subkey.GetValue("DisplayName") as string);
    }

    private static bool CheckNode(RegistryKey registryKey, string applicationName, ProgramVersion? programVersion)
    {
        return GetRegisterSubkeys(registryKey).Any(displayName => displayName != null
                                                                  && displayName.Contains(applicationName)
                                                                  && displayName.Contains(programVersion.ToString()));
    }

    private static bool CheckApplication(string registryKey, string applicationName, ProgramVersion? programVersion)
    {
        RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey);

        if (key != null)
        {
            if (CheckNode(key, applicationName, programVersion))
                return true;

            key.Close();
        }

        return false;
    }

    public static bool IsSoftwareInstalled(string applicationName, ProgramVersion? programVersion)
    {
        string[] registryKey = new [] {
            @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
            @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
        };

        return registryKey.Any(key => CheckApplication(key, applicationName, programVersion));
    }

    private static void Main()
    {
        // Examples
        Console.WriteLine("Notepad++: " + IsSoftwareInstalled("Notepad++", null));
        Console.WriteLine("Notepad++(x86): " + IsSoftwareInstalled("Notepad++", ProgramVersion.x86));
        Console.WriteLine("Notepad++(x64): " + IsSoftwareInstalled("Notepad++", ProgramVersion.x64));
        Console.WriteLine("Microsoft Visual C++ 2009: " + IsSoftwareInstalled("Microsoft Visual C++ 2009", null));
        Console.WriteLine("Microsoft Visual C-- 2009: " + IsSoftwareInstalled("Microsoft Visual C-- 2009", null));
        Console.WriteLine("Microsoft Visual C++ 2013: " + IsSoftwareInstalled("Microsoft Visual C++ 2013", null));
        Console.WriteLine("Microsoft Visual C++ 2012 Redistributable (x86): " + IsSoftwareInstalled("Microsoft Visual C++ 2013", ProgramVersion.x86));
        Console.WriteLine("Microsoft Visual C++ 2012 Redistributable (x64): " + IsSoftwareInstalled("Microsoft Visual C++ 2013", ProgramVersion.x64));
        Console.ReadKey();
    }
}
}
Jim Simson
  • 2,774
  • 3
  • 22
  • 30
Mroczny Arturek
  • 419
  • 1
  • 8
  • 22
  • I can detect Notepad++ 32bit on my machine with this code, but not notepad++ 64bit on a different machine. By the way, the code is really hard to read due to the method nestling – Welcor Sep 17 '19 at 20:02
  • ok, found the reason. 32 bit application needs a workaround for searching for installed 64 bit applications – Welcor Sep 18 '19 at 17:34
4

The solution @Hyperion is ok but it has an error because for 32 bit configurations. No 64 bit registers are returned. To receive 64 bit registers, do the following:

string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
Silny ToJa
  • 1,815
  • 1
  • 6
  • 20
1

Solutions above are really good, but sometimes you have to check if product is installed also on another machine. So there is a version based on solutions above from @Stellan Lindell and @Mroczny Arturek

This method works OK for local machine and also remote machine...

    public static bool IsSoftwareInstalled(string softwareName, string remoteMachine = null, StringComparison strComparison = StringComparison.Ordinal)
    {
        string uninstallRegKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";

        RegistryView[] enumValues = (RegistryView[])Enum.GetValues(typeof(RegistryView));

        //Starts from 1, because first one is Default, so we dont need it...
        for (int i = 1; i < enumValues.Length; i++)
        {
            //This one key is all what we need, because RegView will do the rest for us
            using (RegistryKey key = (string.IsNullOrWhiteSpace(remoteMachine))
                ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, enumValues[i]).OpenSubKey(uninstallRegKey)
                : RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, remoteMachine, enumValues[i]).OpenSubKey(uninstallRegKey))
            {
                if (key != null)
                {
                    if (key.GetSubKeyNames()
                        .Select(keyName => key.OpenSubKey(keyName))
                        .Select(subKey => subKey.GetValue("DisplayName") as string)
                        //SomeTimes we really need the case sensitive/insensitive option...
                        .Any(displayName => displayName != null && displayName.IndexOf(softwareName, strComparison) >= 0))
                    { return true; }
                }
            }
        }

        return false;
    }

The registry version is only one from two standard options.. Another option is using WMI, but the registry one is much better due to performance, so take WMI only as a alternative.

    //This one does't have a case sensitive/insesitive option, but if you need it, just don't use LIKE %softwareName%
    //and get all products (SELECT Name FROM Win32_Product). After that just go trough the result and compare...
    public static bool IsSoftwareInstalledWMI(string softwareName, string remoteMachine = null)
    {
        string wmiPath = (!string.IsNullOrEmpty(remoteMachine))
                            ? @"\\" + remoteMachine + @"\root\cimv2"
                            : @"\\" + Environment.MachineName + @"\root\cimv2";

        SelectQuery select = new SelectQuery(string.Format("SELECT * FROM Win32_Product WHERE Name LIKE \"%{0}%\"", softwareName));

        if (SelectStringsFromWMI(select, new ManagementScope(wmiPath)).Count > 0) { return true; }

        return false;
    }

There is my SelectStringsFromWMI method, but you can do this on your own, this is not important part of this solution. But if you are intersted, there it is...

    public static List<Dictionary<string, string>> SelectStringsFromWMI(SelectQuery select, ManagementScope wmiScope)
    {
        List<Dictionary<string, string>> result = new List<Dictionary<string, string>>();
        using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiScope, select))
        {
            using (ManagementObjectCollection objectCollection = searcher.Get())
            {
                foreach (ManagementObject managementObject in objectCollection)
                {
                    //With every new object we add new Dictionary
                    result.Add(new Dictionary<string, string>());
                    foreach (PropertyData property in managementObject.Properties)
                    {
                        //Always add data to newest Dictionary
                        result.Last().Add(property.Name, property.Value?.ToString());
                    }
                }

                return result;
            }
        }
    }

!!UPDATE!!

Due to really bad performance, there is another improvement. Just get values async..

public static bool IsSoftwareInstalled(string softwareName, string remoteMachine = null, StringComparison strComparison = StringComparison.Ordinal)
{
    string uninstallRegKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";

    RegistryView[] enumValues = (RegistryView[])Enum.GetValues(typeof(RegistryView));

    //Starts from 1, because first one is Default, so we dont need it...
    for (int i = 1; i < enumValues.Length; i++)
    {
        //This one key is all what we need, because RegView will do the rest for us
        using (RegistryKey regKey = (string.IsNullOrWhiteSpace(remoteMachine))
                    ? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, enumValues[i]).OpenSubKey(uninstallRegKey)
                    : RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, remoteMachine, enumValues[i]).OpenSubKey(uninstallRegKey))
        {
              if (SearchSubKeysForValue(regKey, "DisplayName", softwareName, strComparison).Result)
              { return true; }
        }
    }

    return false;
}

And the SearchSubKeysForValue method (can be build as extension method):

    public static async Task<bool> SearchSubKeysForValue(RegistryKey regKey, string valueName, string searchedValue, StringComparison strComparison = StringComparison.Ordinal)
    {
        bool result = false;
        string[] subKeysNames = regKey.GetSubKeyNames();
        List<Task<bool>> tasks = new List<Task<bool>>();

        for (int i = 0; i < subKeysNames.Length - 1; i++)
        {
            //We have to save current value for i, because we cannot use it in async task due to changed values for it during foor loop
            string subKeyName = subKeysNames[i];
            tasks.Add(Task.Run(() =>
            {
                string value = regKey.OpenSubKey(subKeyName)?.GetValue(valueName)?.ToString() ?? null;
                return (value != null && value.IndexOf(searchedValue, strComparison) >= 0);
            }));
        }

        bool[] results = await Task.WhenAll(tasks).ConfigureAwait(false);
        result = results.Contains(true);

        return result;
    }
Blaato
  • 129
  • 10
  • 1
    The original question had nothing to do with remote machines – Hyperion Sep 20 '18 at 14:19
  • @Faded but you can use this solution for local machine and even remote machine so yes, this is a solution for your answer with some improvements. So I dont understand the downvote. – Blaato Sep 21 '18 at 06:18
  • 1
    @Faded maybe you will appreciate this updated improvement more ... :) – Blaato Sep 25 '18 at 06:52
1

Here is my version for 64 bits

  public static string[] checkInstalled(string findByName)
    {
        string[] info = new string[3];

        string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";

        //64 bits computer
        RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
        RegistryKey key = key64.OpenSubKey(registryKey);

        if (key != null)
        {
            foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
            {
                string displayName = subkey.GetValue("DisplayName") as string;
                if (displayName != null && displayName.Contains(findByName))
                {
                    info[0] = displayName;

                    info[1] = subkey.GetValue("InstallLocation").ToString();

                    info[2] = subkey.GetValue("Version").ToString();
                }
            }

            key.Close();
        }

        return info;
    }

you can call this method like this

string[] JavaVersion = Software.checkInstalled("Java(TM) SE Development Kit");

if the array is empty means no installation found. if it is not empty it will give you the original name, relative path, and location which in most cases that is all we are looking to get.

jerryurenaa
  • 3,863
  • 1
  • 27
  • 17
0

I tried the solutions here, but they didnt work in some cases. The reason was, that my programm is 32 bit and runs on 64 bit Windows. With the solutions posted here a 32bit process can not check whether a 64 bit application is installed.

How to access 64 bit registry with a 32 bit process

RegistryKey.OpenBaseKey

I modifed the solutions here to get a working one for this issue:

Usage example

 Console.WriteLine(IsSoftwareInstalled("Notepad++"));

Code

    public static bool IsSoftwareInstalled(string softwareName)
    {
        var registryUninstallPath                = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
        var registryUninstallPathFor32BitOn64Bit = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";

        if (Is32BitWindows())
            return IsSoftwareInstalled(softwareName, RegistryView.Registry32, registryUninstallPath);

        var is64BitSoftwareInstalled = IsSoftwareInstalled(softwareName, RegistryView.Registry64, registryUninstallPath);
        var is32BitSoftwareInstalled = IsSoftwareInstalled(softwareName, RegistryView.Registry64, registryUninstallPathFor32BitOn64Bit);
        return is64BitSoftwareInstalled || is32BitSoftwareInstalled;
    }

    private static bool Is32BitWindows() => Environment.Is64BitOperatingSystem == false;

    private static bool IsSoftwareInstalled(string softwareName, RegistryView registryView, string installedProgrammsPath)
    {
        var uninstallKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView)
                                              .OpenSubKey(installedProgrammsPath);

        if (uninstallKey == null)
            return false;

        return uninstallKey.GetSubKeyNames()
                           .Select(installedSoftwareString => uninstallKey.OpenSubKey(installedSoftwareString))
                           .Select(installedSoftwareKey => installedSoftwareKey.GetValue("DisplayName") as string)
                           .Any(installedSoftwareName => installedSoftwareName != null && installedSoftwareName.Contains(softwareName));
    }
Welcor
  • 2,431
  • 21
  • 32