I have a C# program where I have to get the product code of an installed msi. I have only the msi name as the input. Can this be done programmatically?
-
Did you use Regedit to search the registry on a machine with the software installed? – agent-j Jun 14 '11 at 14:46
-
any final solution with full source code about it ? – Kiquenet Jul 27 '12 at 06:40
-
**[DTF (Deployment Tools Foundation)](https://serverfault.com/questions/577969/is-it-possible-to-disable-msiexec-help-gui/596519#596519)** is a bunch of .NET classes delivered with the WiX download which will give you an easy way to access and modify MSI files without any COM interop or clunk. See the information in the link provided for some more context. **[Download WiX](http://wixtoolset.org/)** to get hold of the DTF components. – Stein Åsmul Jul 31 '17 at 00:22
5 Answers
Do the answers to this question help? They want to get the product name, but maybe it works for the product code, too?
EDIT
If you do not have the MSI file itself to access the database (as suggested by the above link to the other question), you may try to search the following registry path for the name of your MSI file:
HKEY_CLASSES_ROOT\Installer\Products\*\SourceList
There are many entries under the Products
branch. Each of them is a product key. Every branch should contain the SourceList
node, which in turn should contain the value PackageName
. That value holds the name of the MSI file.
So what I'd do is:
for each key in Products
{
open SourceList subkey
read PackageName value
if name equals my msi file name
{
return key-name formatted as GUID
}
}

- 1
- 1

- 55,956
- 8
- 91
- 139
-
However, the key name is actually a scrambled version of the product code. See here for help going from a ProductCode to the registry key format (http://stackoverflow.com/questions/1881643/msi-product-code-from-product-id/1881818#1881818). You'll just need to reverse that to descramble the key name. – Todd Kobus Jul 05 '14 at 15:17
This is the code I used to get the UninstallString
of any MSI.
private string GetUninstallString(string msiName)
{
Utility.WriteLog("Entered GetUninstallString(msiName) - Parameters: msiName = " + msiName);
string uninstallString = string.Empty;
try
{
string path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products";
RegistryKey key = Registry.LocalMachine.OpenSubKey(path);
foreach (string tempKeyName in key.GetSubKeyNames())
{
RegistryKey tempKey = key.OpenSubKey(tempKeyName + "\\InstallProperties");
if (tempKey != null)
{
if (string.Equals(Convert.ToString(tempKey.GetValue("DisplayName")), msiName, StringComparison.CurrentCultureIgnoreCase))
{
uninstallString = Convert.ToString(tempKey.GetValue("UninstallString"));
uninstallString = uninstallString.Replace("/I", "/X");
uninstallString = uninstallString.Replace("MsiExec.exe", "").Trim();
uninstallString += " /quiet /qn";
break;
}
}
}
return uninstallString;
}
catch (Exception ex)
{
throw new ApplicationException(ex.Message);
}
}
This will give a result like this:
MsiExec.exe /I{6BB09011-69E1-472F-ACAD-FA0E7DA3E2CE}
From this string, you can take the substring within the braces {}, which will be 6BB09011-69E1-472F-ACAD-FA0E7DA3E2CE
. I hope this might be the product code.
-
6This will fail if you use this code on a 64bit machine and your application is 32bit, because it will redirect you to the `Wow6432Node` in software. Use `RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(path)` instead. – Martin Braun Aug 04 '16 at 09:07
There is most fast and simply way - to use WMI with conditional query string.
public string GetProductCode(string productName)
{
string query = string.Format("select * from Win32_Product where Name='{0}'", productName);
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
{
foreach (ManagementObject product in searcher.Get())
return product["IdentifyingNumber"].ToString();
}
return null;
}

- 1,236
- 16
- 18
-
about [`ManagementObjectSearcher`](https://learn.microsoft.com/en-us/dotnet/api/system.management.managementobjectsearcher?view=netframework-4.5.1) – T.Todua Dec 05 '20 at 10:18
This code obtains the product code directly from an MSI file. So this allows reading the code without installing the file.
class MsiHandle : SafeHandleMinusOneIsInvalid
{
public MsiHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{
return NativeMethods.MsiCloseHandle(handle) == 0;
}
}
class NativeMethods
{
const string MsiDll = "Msi.dll";
[DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
public extern static uint MsiOpenPackageW(string szPackagePath, out MsiHandle product);
[DllImport(MsiDll, ExactSpelling=true)]
public extern static uint MsiCloseHandle(IntPtr hAny);
[DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern uint MsiGetProductPropertyW(MsiHandle hProduct, string szProperty, StringBuilder value, ref int length);
[DllImport(MsiDll, ExactSpelling = true)]
public static extern int MsiSetInternalUI(int value, IntPtr hwnd);
public static uint MsiGetProductProperty(MsiHandle hProduct, string szProperty, out string value)
{
StringBuilder sb = new StringBuilder(1024);
int length = sb.Capacity;
uint err;
value = null;
if(0 == (err = MsiGetProductPropertyW(hProduct, szProperty, sb, ref length)))
{
sb.Length = length;
value = sb.ToString();
return 0;
}
return err;
}
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static int Main(string[] args)
{
string msiFile = args[0];
NativeMethods.MsiSetInternalUI(2, IntPtr.Zero); // Hide all UI. Without this you get a MSI dialog
MsiHandle msi;
uint err;
if (0 != (err = NativeMethods.MsiOpenPackageW(args[0], out msi)))
{
Console.Error.WriteLine("Can't open MSI, error {0}", err);
return 1;
}
// Strings available in all MSIs
string productCode;
using (msi)
{
if (0 != NativeMethods.MsiGetProductProperty(msi, "ProductCode", out productCode))
throw new InvalidOperationException("Can't obtain product code");
Console.WriteLine(productCode);
return 0;
}
}
}
Full example in Subversion on http://ankhsvn.open.collab.net/svn/ankhsvn/trunk/src/tools/Ankh.Chocolatey/ Use username 'guest' and no password.

- 19,525
- 4
- 57
- 73
private static bool GetUninstallString(string ProductName)
{
try
{
RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64);
var key = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") ??
localKey.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(ProductName));
}
catch
{
// Log message
return false;
}
}
This is very usefull for search string by productname

- 1,727
- 1
- 19
- 27

- 11
- 1