How to find an EXE's install location - the proper way?
I am making a software in C# and MATLAB that calls another software (CMG) to do some processing. My problem is that the address of the software I have put in my program is only correct on my personal computer and not on the customers' computers (I don't know what would be the path to CMG software on their computer).
How can I provide a general form of the address in order to make it work on every computer?
The following is the path I call from my MATLAB software:
C:\Program Files (x86)\CMG\STARS\2011.10\Win_x64\EXE\st201110.exe
As you see it is in drive C and the version is 2011.10. So if customer's version is something else and it is installed on other drives, this path makes no sense.
Solution 1:
Method 1
The registry keys SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall provides a list of where most applications are installed:
Note: It doesn't list all EXE applications on the PC as some dont require installation.
In your case I am pretty sure that CMG STARS will be listed and you will be able to search for it by iterating over all subkeys looking at the DisplayName value and fetching the InstallLocation.
Also note that this Uninstall registry key exists in 3 places in the registry:
1. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall inside CurrentUser
2. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall inside LocalMachine
3. SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall in LocalMachine
Here is an class that returns the installed location of an application:
using Microsoft.Win32;
public static class InstalledApplications
{
public static string GetApplictionInstallPath(string nameOfAppToFind)
{
string installedPath;
string keyName;
// search in: CurrentUser
keyName = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.CurrentUser, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
// search in: LocalMachine_32
keyName = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.LocalMachine, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
// search in: LocalMachine_64
keyName = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.LocalMachine, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
return string.Empty;
}
private static string ExistsInSubKey(RegistryKey root, string subKeyName, string attributeName, string nameOfAppToFind)
{
RegistryKey subkey;
string displayName;
using (RegistryKey key = root.OpenSubKey(subKeyName))
{
if (key != null)
{
foreach (string kn in key.GetSubKeyNames())
{
using (subkey = key.OpenSubKey(kn))
{
displayName = subkey.GetValue(attributeName) as string;
if (nameOfAppToFind.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return subkey.GetValue("InstallLocation") as string;
}
}
}
}
}
return string.Empty;
}
}
Here is how you call it:
string installPath = InstalledApplications.GetApplictionInstallPath(nameOfAppToFind);
To get the nameOfAppToFind you'll need to look in the registry at the DisplayName:
REF: I modified the above code from here to return the install path.
Method 2
You can also use the System Management .Net DLL to get the InstallLocation although it is heaps slower and creates "Windows Installer reconfigured the product" event log messages for every installed product on your system.
using System.Management;
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject mo in mos.Get())
{
Debug.Print(mo["Name"].ToString() + "," + mo["InstallLocation"].ToString() + Environment.NewLine);
}
Getting the EXE's name
Neither of the above methods tell you the name of the executable, however it is quite easy to work out by iterating over all the files in the install path and using a technique I discuss here to look at file properties to detect the EXE with the correct File Description, eg:
private string GetFileExeNameByFileDescription(string fileDescriptionToFind, string installPath)
{
string exeName = string.Empty;
foreach (string filePath in Directory.GetFiles(installPath, "*.exe"))
{
string fileDescription = GetSpecificFileProperties(filePath, 34).Replace(Environment.NewLine, string.Empty);
if (fileDescription == fileDescriptionToFind)
{
exeName = GetSpecificFileProperties(filePath, 0).Replace(Environment.NewLine, string.Empty);
break;
}
}
return exeName;
}
Either method (1 or 2) you use I recommend that you save the location of exe name so you only do this operation once. In my opinion its better to use Method 1 as its faster and doesn't create all the "Windows Installer reconfigured the product." event logs.
Alternate Method using an Installer
If your application is being installed you could find out where CMG STARS is located during installation Using Windows Installer to Inventory Products and Patches:
Enumerating Products
Use the MsiEnumProductsEx function to enumerate Windows Installer applications that are installed in the system. This function can find all the per-machine installations and per-user installations of applications (managed and unmanaged) for the current user and other users in the system. Use the dwContext parameter to specify the installation context to be found. You can specify any one or any combination of the possible installation contexts. Use the szUserSid parameter to specify the user context of applications to be found.
During installation you would find the exe path to CMG STARS and save a registry key with the value.
I discuss using this approach of saving an EXE's install path in the registry for updating applications here.
Tip
As mentioned in the comments, it is worthwhile you do a search in the registry for the EXE's name st201110.exe and see if the authors of the CMG STAR application already provide this information in a registry key you can access directly.
Plan B
If all else fails present the user with a FileOpenDialog and get them to specify the exe's path manually.
What if the 3rd party application is uninstalled or upgraded?
I mentioned to store the install path and exe name in the registry (or database, config file, etc) and you should always check the exe file exists before making any external calls to it, eg:
if (!File.Exists(installPath + exeName))
{
//Run through the process to establish where the 3rd party application is installed
}