Check for prerequisite from a .NET Windows Form application - c#

I want to check for the installation of a third party application during the installation of my .NET Windows Form application or when it is opened. My Windows Form application doesn't require the third party application to run but it does need it for a feature to work. For example, My Windows Form application opens up the third party application such as a mail program.
I don't know if Click Once is the right strategy for this? I would need it to check for the prerequisite during installation and if it's not there notify the user to install it first. If Click Once isn't the right strategy for this is there another way? Maybe I need to install my Windows Form application first then when it is opened it checks for the third party application? The problem with that is, the installation path could vary from machine to machine. I'm not really sure how to go about this.
This link explains how to include prerequisites in Click Once but that's not what I want to do.
Another link that talks about including the prerequisites but not just detecting them.

One possible solution is to check registry with this method that returns bool value indicating whether the registry record with application name exists :
public static bool IsApplictionInstalled(string p_name)
{
string displayName;
RegistryKey key;
// search in: CurrentUser
key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (String keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (p_name.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
// search in: LocalMachine_32
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (String keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (p_name.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
// search in: LocalMachine_64
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (String keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (p_name.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
// NOT FOUND
return false;
}

Related

C# Rename Registry value errors "returned null"

So to my final programming project I need to rename a value inside Registry through C#, this is what I wrote so far:
public bool RenameSubKey(string parentKey, string subKeyName, string newSubKeyName)
{
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
RegistryKey rk64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
rk64.OpenSubKey(parentKey);
//copy all the values
foreach (string valueName in rk64.OpenSubKey(subKeyName).GetValueNames())
{
object objValue = rk64.OpenSubKey(subKeyName).GetValue(valueName);
RegistryValueKind valKind = rk64.OpenSubKey(subKeyName).GetValueKind(valueName);
rk64.CreateSubKey(newSubKeyName).SetValue(valueName, objValue, valKind);
}
rk64.DeleteSubKeyTree(subKeyName); // Deletes old value
return true;
}
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(parentKey, false))
{
rk.OpenSubKey(parentKey);
//copy all the values
foreach (string valueName in rk.OpenSubKey(subKeyName).GetValueNames())
{
object objValue = rk.OpenSubKey(subKeyName).GetValue(valueName);
RegistryValueKind valKind = rk.OpenSubKey(subKeyName).GetValueKind(valueName);
rk.CreateSubKey(newSubKeyName).SetValue(valueName, objValue, valKind);
}
rk.DeleteSubKeyTree(subKeyName); // Deletes old value
return true;
}
}
It needs to be able to rename a 32-bit Registry value as well as a 64-bit Registry value (the application is 32-bit). Rename function simply means to create new value with the new name, copy all the data from the old one to the new one and delete the old one.
Let's take an example: There's a ListView presents all the installed application on the PC and I want to change the value UninstallString to !UninstallString with the press of a button so the users won't be able to uninstall the selected application in the ListView. The app might be 64-bit (meaning the Registry values are 64-bit and it requires a little different approach) or it can be 32-bit - Hence comes the if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess).
So in the example I place SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\7-Zip as my path and I want to rename the value UninstallString to !UninstallString, but it quits on the foreach with an error says System.NullReferenceException: 'Object reference not set to an instance of an object.' Microsoft.Win32.RegistryKey.OpenSubKey(...) returned null.
First of all, let me say, this requieres elevated privileges, the changes are because you want to enter the registry and write in them so you need to actually tell that when you are opening the sub key. Either in 64 or 32 bit. Im not sure about the If, as pointed out before by the general, but this will work at least for 32 bits path
public static bool RenameSubKey(string parentKey, string key, string newValue)
{
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
RegistryKey rk64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(parentKey, RegistryKeyPermissionCheck.ReadWriteSubTree);
rk64.SetValue(key, newValue);
return true;
}
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(parentKey, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
rk.SetValue(key, newValue);
return true;
}
}
Thanks to #nalnpir I fixed the rename Registry value function:
public static bool RenameSubKey(string path, string keyName, string newKeyName)
{
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
RegistryKey rk64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree);
object objValue = rk64.GetValue(keyName);
RegistryValueKind valKind = rk64.GetValueKind(keyName);
rk64.SetValue(newKeyName, objValue, valKind);
rk64.DeleteValue(keyName);
return true;
}
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
object objValue = rk.GetValue(keyName);
RegistryValueKind valKind = rk.GetValueKind(keyName);
rk.SetValue(newKeyName, objValue, valKind);
rk.DeleteValue(keyName);
return true;
}
}
Now I see that I didn't access it the right way (as he enlightened me) and thus was the error.

Can't open registry subkey

Can't open registry subkey even if I try to open in both - x64 and x32 registry. I'm trying to get username of logged in visual studio user.
#"Software\Microsoft\VisualStudio\12.0\ConnectedUser\IdeUser\Cache"
Can't see IdeUser, but Cache returns NULL.
const string SubKey = #"Software\Microsoft\VisualStudio\12.0\ConnectedUser\IdeUser\Cache";
const string EmailAddressKeyName = "EmailAddress";
const string UserNameKeyName = "DisplayName";
RegistryKey localMachineRegistry64 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64);
RegistryKey reg64 = localMachineRegistry64.OpenSubKey(SubKey, false);
if (reg64 != null)
{
return reg64.GetValue(SubKey, true).ToString();
}
//Check the 32-bit registry for "HKEY_LOCAL_MACHINE\SOFTWARE" if not found in the 64-bit registry:
RegistryKey localMachineRegistry32 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32);
RegistryKey reg32 = localMachineRegistry32.OpenSubKey(SubKey, false);
if (reg32 != null)
{
return reg32.GetValue(SubKey, true).ToString();
}
I have tested you code and it works as I would expect (to a point). I can access the SubKey. *Note I am running VS as administrator. (right click on the VS exe and run as admin, I am also a local admin my dev machine, so I have un-restricted registry access).
However your code is a little interesting as your not actually accessing the values in the registry by calling GetValue(SubKey,true), in my example I altered accordingly.
const string SubKey = #"Software\Microsoft\VisualStudio\12.0\ConnectedUser\IdeUser\Cache";
const string EmailAddressKeyName = "EmailAddress";
const string UserNameKeyName = "DisplayName";
RegistryKey localMachineRegistry64 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64);
RegistryKey reg64 = localMachineRegistry64.OpenSubKey(SubKey, false);
if (reg64 != null)
{
//******** This is just an example of getting the user name and email.
//get user name and email from the selected sub key.
var userName = reg64.GetValue(UserNameKeyName, String.Empty).ToString();
var emailAddress = reg64.GetValue(EmailAddressKeyName, String.Empty).ToString();
//return a bool and pass back the userName and email as out parms?
return true;
}
Hope this helps.
EDIT -
If registry access continues to be an issue you can monitor registry access via ProcMon from sysinternals.

How to check other software is installed when installing my software

I want to create a setup to deploy software.My first window of installation is to check master software(other software) is installed in that computer,because i need to add a supporting file to master software's installation folder.
Is that possible in visual studio setup deployment project?
Every software you install, must create entry in Registry.So you can read the particular entry in registry from Visual studio Setup project.
Retrieve a Value from the Registry
how to retrieve the MediaPath value for your computer from the
registry,
The MediaPath value for your computer is located under the following registry subkey:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion
You can retrieve this value by using a launch condition. To do this, follow these steps:
In Visual Studio .NET, point to Editor on the View menu, and then
click Launch Conditions.
In the Launch Conditions Editor, click Search Target Machine.
On the Action menu, click Add Registry Search.
By default, Search for RegistryEntry1 is added.
On the View menu, click Properties Window to select the Properties
window for the registry search that you created in previous step.
Change the Name property value to Search for MediaPath.
Change the Property property value to MEDIA_PATH.
When you run the setup project, the MediaPath registry value is retrieved to your
MEDIA_PATH property.
To search HKEY_LOCAL_MACHINE in the registry, click the Root
property, and then click vsdrrHKLM in the drop-down list box for the
property value.
To search the SOFTWARE\Microsoft\Windows\CurrentVersion subkey, type
SOFTWARE\Microsoft\Windows\CurrentVersion in the RegKey property
value text box.
To search for the MediaPath value, type MediaPath in the Value
property value text box.
For More See Hare
Below code is working fine for me
/// <summary>
/// To check software installed or not
/// </summary>
/// <param name="controlPanelDisplayName">Display name of software from control panel</param>
private static bool IsApplictionInstalled(string controlPanelDisplayName)
{
string displayName;
RegistryKey key;
// search in: CurrentUser
key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
if (null != key)
{
foreach (string keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (controlPanelDisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
}
// search in: LocalMachine_32
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
if (null != key)
{
foreach (string keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (controlPanelDisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
}
// search in: LocalMachine_64
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
if (null != key)
{
foreach (string keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (controlPanelDisplayName.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
}
// NOT FOUND
return false;
}

get installed version of an application using c#

I would to get installed version of an application (say, MyApp) using C#.
I will do this much,
1. Create a 'Set Up' for MyApp of version 5.6
2. Install MyApp.
I will create another application (say VersionTracker)to get the version of installed applications. So if I pass the name 'MyApp' I would like to get the version as '5.6'. If another application say Adobe Reader is installed in my system, I want to get the version of Adobe Reader if I pass 'Adobe Reader'.
I need to know how to build 'VersionTracker'
The first and the most important thing is that not all applications do save their version somewhere in the system. To be honest, only a few of them do that. The place where you should look are the Windows Registry. Most of installed applications put their installation data into the following place:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
However, it's not that easy - on 64bit Windows, the 32bit (x86) applications save their installation data into another key, which is:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
In these keys there are many keys, some of them have got "easy-readable" name, such as Google Chrome, some of them got names such as {63E5CDBF-8214-4F03-84F8-CD3CE48639AD}. You must parse all these keys into your application and start looking for the application names. There are usually in DisplayName value, but it's not always true. The version of the application is usually in DisplayVersion value, but some installers do use another values, such as Inno Setup: Setup Version, ... Some application do have their version written in their name, so it's possible that the application version is already in the DisplayName value.
Note: It's not easy to parse all these registry keys and values and to "pick" the correct values. Not all installers save the application data into these keys, some of them do not save the application version there, etcetera. However, it's usual that the application use these registry keys. [Source: StackOverflow: Detecting installed programs via registry, browsing my own registry]
Alright, so now when you know where you should look, you have to program it all in C#. I won't write the application for you, but I'll tell you what classes you should use and how to. First, you need these:
using System;
using Microsoft.Win32;
To get to your HKEY_LOCAL_MACHINE, create a RegistryKey like this:
RegistryKey baseRegistryKey = Registry.LocalMachine;
Now you need to define subkeys:
string subKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// or "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
Now you need to go to the subkey, so create a new RegistryKey:
RegistryKey uninstallKey = baseRegistryKey.OpenSubKey(subKey);
Now you need to go thru all the subkeys that are there, so first we get the names of all the subkeys:
string[] allApplications = uninstallKey.GetSubKeyNames();
Now you must go thru all the subkeys yourself, one by one, by creating a new registry key (you don't have to, but I'll do it):
RegistryKey appKey = baseRegistryKey.OpenSubKey(subKey + "\\" + applicationSubKeyName);
where applicationSubKeyName is the name of the subkey you're currently checking. I recommend foreach statement, which helps you (you must however have some experience with C# already, I'm not going to tell you how to use foreach here).
Now check the application's name and compare it with name of your desired application (you cannot rely on the subkey name, because, as I already said, they can be called for example {63E5CDBF-8214-4F03-84F8-CD3CE48639AD}, so you must check the name here):
string appName = (string)appKey.GetValue("DisplayName");
If it's the correct application (you must check it yourself), find the version:
string appVersion = (string)appKey.GetValue("DisplayVersion");
Et voilĂ , you have the version. At least there's like a 60 - 80% chance you have...
Remember! If some key or value doesn't exist, the method returns null. Remember to check if the returned value is null everytime, otherwise your application will crash.
Where to find more? The Code Project: Read, write and delete from registry with C#
I really hope I helped you. And if you wanted to know something else and I didn't understand your question, then, please, ask better next time. :)
///
/// Author : Muhammed Rauf K
/// Date : 03/07/2011
/// A Simple console application to create and display registry sub keys
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// it's required for reading/writing into the registry:
using Microsoft.Win32;
namespace InstallationInfoConsole
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Registry Information ver 1.0");
Console.WriteLine("----------------------------");
Console.Write("Input application name to get the version info. (for example 'Nokia PC Suite'): ");
string nameToSearch = Console.ReadLine();
GetVersion(nameToSearch);
Console.WriteLine("----------------------------");
Console.ReadKey();
}
///
/// Author : Muhammed Rauf K
/// Date : 03/07/2011
/// Create registry items
///
static void Create()
{
try
{
Console.WriteLine("Creating registry...");
// Create a subkey named Test9999 under HKEY_CURRENT_USER.
string subKey;
Console.Write("Input registry sub key :");
subKey = Console.ReadLine();
RegistryKey testKey = Registry.CurrentUser.CreateSubKey(subKey);
Console.WriteLine("Created sub key {0}", subKey);
Console.WriteLine();
// Create two subkeys under HKEY_CURRENT_USER\Test9999. The
// keys are disposed when execution exits the using statement.
Console.Write("Input registry sub key 1:");
subKey = Console.ReadLine();
using (RegistryKey testKey1 = testKey.CreateSubKey(subKey))
{
testKey1.SetValue("name", "Justin");
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static void GetVersion(string nameToSearch)
{
// Get HKEY_LOCAL_MACHINE
RegistryKey baseRegistryKey = Registry.LocalMachine;
// If 32-bit OS
string subKey
//= "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// If 64-bit OS
= "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
RegistryKey unistallKey = baseRegistryKey.OpenSubKey(subKey);
string[] allApplications = unistallKey.GetSubKeyNames();
foreach (string s in allApplications)
{
RegistryKey appKey = baseRegistryKey.OpenSubKey(subKey + "\\" + s);
string appName = (string)appKey.GetValue("DisplayName");
if(appName==nameToSearch)
{
string appVersion = (string)appKey.GetValue("DisplayVersion");
Console.WriteLine("Name:{0}, Version{1}", appName, appVersion);
break;
}
}
}
static void ListAll()
{
// Get HKEY_LOCAL_MACHINE
RegistryKey baseRegistryKey = Registry.LocalMachine;
// If 32-bit OS
string subKey
//= "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// If 64-bit OS
= "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
RegistryKey unistallKey = baseRegistryKey.OpenSubKey(subKey);
string[] allApplications = unistallKey.GetSubKeyNames();
foreach (string s in allApplications)
{
RegistryKey appKey = baseRegistryKey.OpenSubKey(subKey + "\\" + s);
string appName = (string)appKey.GetValue("DisplayName");
string appVersion = (string)appKey.GetValue("DisplayVersion");
Console.WriteLine("Name:{0}, Version{1}", appName, appVersion);
}
}
}
}
Next code base on similar solution is working for me:
var version = GetApplicationVersion("Windows Application Driver");
string GetApplicationVersion(string appName)
{
string displayName;
// search in: CurrentUser
var key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (var keyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(keyName);
displayName = subKey.GetValue("DisplayName") as string;
if (appName.Equals(displayName, StringComparison.OrdinalIgnoreCase))
return subKey.GetValue("DisplayVersion").ToString();
}
// search in: LocalMachine_32
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (var keyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(keyName);
displayName = subKey.GetValue("DisplayName") as string;
if (appName.Equals(displayName, StringComparison.OrdinalIgnoreCase))
return subKey.GetValue("DisplayVersion").ToString();
}
// search in: LocalMachine_64
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (var keyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(keyName);
displayName = subKey.GetValue("DisplayName") as string;
if (appName.Equals(displayName, StringComparison.OrdinalIgnoreCase))
return subKey.GetValue("DisplayVersion").ToString();
}
// NOT FOUND
return null;
}

Get installed applications in a system

How to get the applications installed in the system using c# code?
Iterating through the registry key "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" seems to give a comprehensive list of installed applications.
Aside from the example below, you can find a similar version to what I've done here.
This is a rough example, you'll probaby want to do something to strip out blank rows like in the 2nd link provided.
string registry_key = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using(Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach(string subkey_name in key.GetSubKeyNames())
{
using(RegistryKey subkey = key.OpenSubKey(subkey_name))
{
Console.WriteLine(subkey.GetValue("DisplayName"));
}
}
}
Alternatively, you can use WMI as has been mentioned:
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach(ManagementObject mo in mos.Get())
{
Console.WriteLine(mo["Name"]);
}
But this is rather slower to execute, and I've heard it may only list programs installed under "ALLUSERS", though that may be incorrect. It also ignores the Windows components & updates, which may be handy for you.
I wanted to be able to extract a list of apps just as they appear in the start menu. Using the registry, I was getting entries that do not show up in the start menu.
I also wanted to find the exe path and to extract an icon to eventually make a nice looking launcher. Unfortunately, with the registry method this is kind of a hit and miss since my observations are that this information isn't reliably available.
My alternative is based around the shell:AppsFolder which you can access by running explorer.exe shell:appsFolder and which lists all apps, including store apps, currently installed and available through the start menu. The issue is that this is a virtual folder that can't be accessed with System.IO.Directory. Instead, you would have to use native shell32 commands. Fortunately, Microsoft published the Microsoft.WindowsAPICodePack-Shell on Nuget which is a wrapper for the aforementioned commands. Enough said, here's the code:
// GUID taken from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
// The friendly app name
string name = app.Name;
// The ParsingName property is the AppUserModelID
string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
// You can even get the Jumbo icon in one shot
ImageSource icon = app.Thumbnail.ExtraLargeBitmapSource;
}
And that's all there is to it. You can also start the apps using
System.Diagnostics.Process.Start("explorer.exe", #" shell:appsFolder\" + appModelUserID);
This works for regular Win32 apps and UWP store apps. How about them apples.
Since you are interested in listing all installed apps, it is reasonable to expect that you might want to monitor for new apps or uninstalled apps as well, which you can do using the ShellObjectWatcher:
ShellObjectWatcher sow = new ShellObjectWatcher(appsFolder, false);
sow.AllEvents += (s, e) => DoWhatever();
sow.Start();
Edit: One might also be interested in knowing that the AppUserMoedlID mentioned above is the unique ID Windows uses to group windows in the taskbar.
2022: Tested in Windows 11 and still works great. Windows 11 also seems to cache apps that aren't installed per se, portable apps that don't need installing, for example. They appear in the start menu search results and can also be retrieved from shell:appsFolder as well.
I agree that enumerating through the registry key is the best way.
Note, however, that the key given, #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", will list all applications in a 32-bit Windows installation, and 64-bit applications in a Windows 64-bit installation.
In order to also see 32-bit applications installed on a Windows 64-bit installation, you would also need to enumeration the key #"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall".
You can take a look at this article. It makes use of registry to read the list of installed applications.
public void GetInstalledApps()
{
string uninstallKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(uninstallKey))
{
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
lstInstalled.Items.Add(sk.GetValue("DisplayName"));
}
catch (Exception ex)
{ }
}
}
}
}
While the accepted solution works, it is not complete. By far.
If you want to get all the keys, you need to take into consideration 2 more things:
x86 & x64 applications do not have access to the same registry.
Basically x86 cannot normally access x64 registry. And some
applications only register to the x64 registry.
and
some applications actually install into the CurrentUser registry instead of the LocalMachine
With that in mind, I managed to get ALL installed applications using the following code, WITHOUT using WMI
Here is the code:
List<string> installs = new List<string>();
List<string> keys = new List<string>() {
#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
#"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
};
// The RegistryView.Registry64 forces the application to open the registry as x64 even if the application is compiled as x86
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64), keys, installs);
FindInstalls(RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64), keys, installs);
installs = installs.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();
installs.Sort(); // The list of ALL installed applications
private void FindInstalls(RegistryKey regKey, List<string> keys, List<string> installed)
{
foreach (string key in keys)
{
using (RegistryKey rk = regKey.OpenSubKey(key))
{
if (rk == null)
{
continue;
}
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
installed.Add(Convert.ToString(sk.GetValue("DisplayName")));
}
catch (Exception ex)
{ }
}
}
}
}
}
it's worth noting that the Win32_Product WMI class represents products as they are installed by Windows Installer. not every application use windows installer
however "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" represents applications for 32 bit. For 64 bit you also need to traverse "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" and since not every software has a 64 bit version the total applications installed are a union of keys on both locations that have "UninstallString" Value with them.
but the best options remains the same .traverse registry keys is a better approach since every application have an entry in registry[including the ones in Windows Installer].however the registry method is insecure as if anyone removes the corresponding key then you will not know the Application entry.On the contrary Altering the HKEY_Classes_ROOT\Installers is more tricky as it is linked with licensing issues such as Microsoft office or other products.
for more robust solution you can always combine registry alternative with the WMI.
string[] registryKeys = new string[] {
#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" };
public class InstalledApp
{
public string DisplayName { get; set; }
public string DisplayIcon { get; set; }
public string Version { get; set; }
public string InstallLocation { get; set; }
}
private void AddInstalledAppToResultView(RegistryHive hive, RegistryView view, string registryKey,Dictionary<string,InstalledApp> resultView)
{
using (var key = RegistryKey.OpenBaseKey(hive, view).OpenSubKey(registryKey))
{
foreach (string subKeyName in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subKeyName))
{
var displayName = subkey.GetValue("DisplayName");
var displayIcon = subkey.GetValue("DisplayIcon");
if (displayName == null || displayIcon == null)
continue;
var app = new InstalledApp
{
DisplayName = (string)displayName,
DisplayIcon = (string)displayIcon,
InstallLocation = (string)subkey.GetValue("InstallLocation"),
Version = (string)subkey.GetValue("DisplayVersion")
};
if(!resultView.ContainsKey(app.DisplayName))
{
resultView.Add(app.DisplayName,app);
}
}
}
}
}
void Main()
{
var result = new Dictionary<string,InstalledApp>();
var view = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32;
AddInstalledAppToResultView(RegistryHive.LocalMachine, view, registryKeys[0],result);
AddInstalledAppToResultView(RegistryHive.CurrentUser, view, registryKeys[0],result);
AddInstalledAppToResultView(RegistryHive.LocalMachine, RegistryView.Registry64, registryKeys[1],result);
Console.WriteLine("==============" + result.Count + "=================");
result.Values.ToList().ForEach(item => Console.WriteLine(item));
}
Iterate through "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" keys and check their "DisplayName" values.
Use Windows Installer API!
It allows to make reliable enumeration of all programs. Registry is not reliable, but WMI is heavyweight.
The object for the list:
public class InstalledProgram
{
public string DisplayName { get; set; }
public string Version { get; set; }
public string InstalledDate { get; set; }
public string Publisher { get; set; }
public string UnninstallCommand { get; set; }
public string ModifyPath { get; set; }
}
The call for creating the list:
List<InstalledProgram> installedprograms = new List<InstalledProgram>();
string registry_key = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subkey_name))
{
if (subkey.GetValue("DisplayName") != null)
{
installedprograms.Add(new InstalledProgram
{
DisplayName = (string)subkey.GetValue("DisplayName"),
Version = (string)subkey.GetValue("DisplayVersion"),
InstalledDate = (string)subkey.GetValue("InstallDate"),
Publisher = (string)subkey.GetValue("Publisher"),
UnninstallCommand = (string)subkey.GetValue("UninstallString"),
ModifyPath = (string)subkey.GetValue("ModifyPath")
});
}
}
}
}
As others have pointed out, the accepted answer does not return both x86 and x64 installs. Below is my solution for that. It creates a StringBuilder, appends the registry values to it (with formatting), and writes its output to a text file:
const string FORMAT = "{0,-100} {1,-20} {2,-30} {3,-8}\n";
private void LogInstalledSoftware()
{
var line = string.Format(FORMAT, "DisplayName", "Version", "Publisher", "InstallDate");
line += string.Format(FORMAT, "-----------", "-------", "---------", "-----------");
var sb = new StringBuilder(line, 100000);
ReadRegistryUninstall(ref sb, RegistryView.Registry32);
sb.Append($"\n[64 bit section]\n\n{line}");
ReadRegistryUninstall(ref sb, RegistryView.Registry64);
File.WriteAllText(#"c:\temp\log.txt", sb.ToString());
}
private static void ReadRegistryUninstall(ref StringBuilder sb, RegistryView view)
{
const string REGISTRY_KEY = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view);
using var subKey = baseKey.OpenSubKey(REGISTRY_KEY);
foreach (string subkey_name in subKey.GetSubKeyNames())
{
using RegistryKey key = subKey.OpenSubKey(subkey_name);
if (!string.IsNullOrEmpty(key.GetValue("DisplayName") as string))
{
var line = string.Format(FORMAT,
key.GetValue("DisplayName"),
key.GetValue("DisplayVersion"),
key.GetValue("Publisher"),
key.GetValue("InstallDate"));
sb.Append(line);
}
key.Close();
}
subKey.Close();
baseKey.Close();
}
Your best bet is to use WMI. Specifically the Win32_Product class.
Might I suggest you take a look at WMI (Windows Management Instrumentation).
If you add the System.Management reference to your C# project, you'll gain access to the class `ManagementObjectSearcher', which you will probably find useful.
There are various WMI Classes for Installed Applications, but if it was installed with Windows Installer, then the Win32_Product class is probably best suited to you.
ManagementObjectSearcher s = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
I used Nicks approach - I needed to check whether the Remote Tools for Visual Studio are installed or not, it seems a bit slow, but in a seperate thread this is fine for me. - here my extended code:
private bool isRdInstalled() {
ManagementObjectSearcher p = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject program in p.Get()) {
if (program != null && program.GetPropertyValue("Name") != null && program.GetPropertyValue("Name").ToString().Contains("Microsoft Visual Studio 2012 Remote Debugger")) {
return true;
}
if (program != null && program.GetPropertyValue("Name") != null) {
Trace.WriteLine(program.GetPropertyValue("Name"));
}
}
return false;
}
My requirement is to check if specific software is installed in my system. This solution works as expected. It might help you. I used a windows application in c# with visual studio 2015.
private void Form1_Load(object sender, EventArgs e)
{
object line;
string softwareinstallpath = string.Empty;
string registry_key = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (var baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
using (var key = baseKey.OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
using (var subKey = key.OpenSubKey(subkey_name))
{
line = subKey.GetValue("DisplayName");
if (line != null && (line.ToString().ToUpper().Contains("SPARK")))
{
softwareinstallpath = subKey.GetValue("InstallLocation").ToString();
listBox1.Items.Add(subKey.GetValue("InstallLocation"));
break;
}
}
}
}
}
if(softwareinstallpath.Equals(string.Empty))
{
MessageBox.Show("The Mirth connect software not installed in this system.")
}
string targetPath = softwareinstallpath + #"\custom-lib\";
string[] files = System.IO.Directory.GetFiles(#"D:\BaseFiles");
// Copy the files and overwrite destination files if they already exist.
foreach (var item in files)
{
string srcfilepath = item;
string fileName = System.IO.Path.GetFileName(item);
System.IO.File.Copy(srcfilepath, targetPath + fileName, true);
}
return;
}

Categories

Resources