In certain cases the need to retrieve MSI upgrade codes for deployed packages can arise.
Common scenarios:
- I took over someone else's MSI project, and I need to determine what upgrade codes were used for previous versions that are already in the wild. This is necessary to handle upgrade scenarios. I have no archive of releases anywhere.
- I accidentally changed the upgrade code for my WiX package several times during development and I need to find all Upgrade Code versions "in the wild". I was not aware that Upgrade Codes should remain stable between versions.
This is a Q/A style question.
This question has come up before in various incarnations, but this is not a duplicate. I am posting a way to do it that uses the main MSI automation interface (or strictly speaking WMI). It should be more reliable than registry based approaches from previous answers. This answers also tries to summarize other retrieval approaches.
MSI Upgrade Code Retrieval (via PowerShell / WMI)
The PowerShell script below should retrieve all related product codes, upgrade codes and product names installed on your machine (table output).
Screenshot of output (full script below):
These are the real, live values directly from the Windows Installer database on the machine in question. There is no need for any conversion or interpretation. We are going through the proper APIs.
Technical note!: Be aware that checking properties directly in your original MSI file (property table) or WiX source file, may not match actual installed values since properties can be overridden at install time via transforms (more info below) - or property values specified at the command line. The moral of the story: retrieve property values directly from the system when you can.
As a digression, there is also a one-line PowerShell command which will retrieve product codes and upgrade codes only - without the package name included. This might actually suffice for some users (I would recommend the full script below however). There is a screenshot of the output of this one-liner in a section below. Note: this command appears a lot faster than the larger script (the "Value" field is the upgrade code). Also note: product codes without associated upgrade codes will not show up as far as I can tell - they will in the larger script:
To run the full PowerShell script below:
Running on Remote Machines
netsh advfirewall firewall set rule group="windows management instrumentation (wmi)" new enable=yes
(source - see this link for command line to disable this new rule again if you are just testing. Essentially just set enable=no). See the linked source for potentially more restrictive rules that could also work.HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ LocalAccountTokenFilterPolicy = 1
(source - mid page, latter half). I set a 32-bit DWORD.With those changes in place on the remote system, I also added user credentials to each call by prompting the user
$Cred = Get-Credential
. There are also more advanced options for defining the user credentials, as explained here: Pass password into -credential (and here). To test run, here is a little test script. Copy all lines below, modify the remote machine name and paste into PowerShell by right clicking (you will be prompted for credentials):For the large PowerShell script above, the basic additions for remote running on several machines in a Windows domain, could be something like this (I won't update the above script since I can't really test this properly). Remember to update the list of remote computer names at the top of the script and run with a domain admin account:
To adapt the same machine loop for a non-domain network you can add credentials to the WMI calls. Something like this (you will be prompted for credentials for each machine - which might be confusing). Remember to update the list of remote computer names at the top of the script and use an account with local admin rights on the target box:
The real answer ends here. I believe the above newer script should cover most use-cases, but I will leave the content below as well since it is not obsolete, just probably less efficient than the above script. Reading it will probably be repetitious.
The scripts below for retrieval of single upgrade codes rather than the whole list, could be of interest if you want to retrieve a single upgrade code from within your own application at run-time. I'll leave that older content in.
Retrieving Upgrade Codes For MSI Files That Are Not Installed
If you need the upgrade code for an MSI package that is not installed on your machine, please read the "Manual Retrieval of Upgrade Codes" section towards the bottom for several options (essentially look in the MSI file itself, or its source file used to compile it).
It is not safe to get the upgrade code for installed packages from the original MSI install file itself or from the (WiX) sources used to compile the MSI, because upgrade codes can be overridden at install time using transforms (details in text below - transforms are little database fragments applied at install time, see that Symantec link for details).
The programmatic retrieval of upgrade codes relies on WMI, and you can use either PowerShell or VBScript to invoke WMI. Both methods are presented below. Essentially the following WMI query is run to retrieve the upgrade code for a specified product code:
It is the same query used for both VBScript and PowerShell. You can also run it as a straight WMI query using a tool such as as
WMIExplorer.exe
. A very useful tool - highly recommended. I believe this is their site: https://github.com/vinaypamnani/wmie2/releasesRetrieve Single Upgrade Code Via PowerShell / WMI
Rather than outputting a whole table with all product codes and upgrade codes, you can retrieve a single upgrade code for a specified product code. This is good if you are trying to do the retrieval from inside your own application code (then it is just a standard WMI query and has nothing to do with PowerShell).
Below is the single upgrade code retrieval done via PowerShell (to launch PowerShell: hold down the Windows key, tap R, release the Windows key, type in "powershell" and press OK or hit enter):
The output should be something like this (maybe a little hard to read, I should have used larger fonts):
The product code specified in the query above is for "Windows SDK Intellidocs". You must obviously replace it with your own product code guid. To find the product code you need to pass in, you can also use a PowerShell query as described here: How can I find the product GUID of an installed MSI setup?
The returned upgrade code is coming straight from the real Windows Installer registry database. It requires no further processing or interpretation or manual conversion steps. It will also be correct, even if a transform changed the original upgrade code when the MSI was installed (details on transform issues below).
Update, special notice: Without complicating things unnecessarily, I believe I have found a bug in WMI that is very specific. When an original MSI has no upgrade code set, and you add one via a transform, then WMI does not seem to report the upgrade code at all. However: if the original MSI has an upgrade code, and you override it in a transform, WMI reports the transform's upgrade code (which is expected). I definitely saw this, but will need to verify with one more test package to be sure. The moral of the story: always set an upgrade code in your MSI! Then you avoid the whole issue permanently. And don't auto-generate it - hard code it (read "Manual Retrieval of Upgrade Codes" below for an explanation).
Retrieve Single upgrade code using VBScript / WMI (Legacy Approach)
There is nothing wrong with the VBScript solution found below - it even has some benefits over PowerShell - despite VBScript being a legacy technology by now. The benefits are that it should work on all machines, even when the .NET framework is missing (or locked), and on machines where PowerShell is missing (or locked). It is a dated, but viable solution that is quite flexible (unless VBScript is also locked, but all modern OS versions fully support VBScript).
In order to make it as simple as possible to retrieve your upgrade code, I have created a "bare-bone VBScript" which should do the trick. It has not been tested for targeting remote computers, even if WMI should be able to do so by design. The script is intended to be run on the system where your mystery MSI with the unknown upgrade code is installed.
This VBScript requires an input product code (input dialog shown when script is run), and it will then proceed to look up the corresponding upgrade code (if any). As stated above, to locate the product code for your MSI, you can use this approach: How can I find the product GUID of an installed MSI setup?. Once you have the product code (guid), you can run this VBScript on the target machine and you should get the upgrade code returned to you in a few seconds. WMI retrieval can be very slow.
Retrieving All Upgrade Codes and Product Code on a Machine
There is a one-line PowerShell command to retrieve all product codes and related upgrade codes, but this output fill lack the name of the products. I include it here for completeness:
The output will be similar to this (the "Value" field is the upgrade code - product codes without associated upgrade codes will not show up as far as I can tell):
Manual Retrieval of Upgrade Codes
This section list some "manual ways" to retrieve upgrade codes that don't need any coding or command lines. These manual approaches are not the recommended ones. I include them only because this attempts to be a "reference answer". Several different options should be provided. My recommendation is to use the PowerShell or VBScript provided above.
That being said, upgrade codes should generally never change across versions of your product, so chances are you can try the one you find in the MSI file itself, or in the source used to compile it as described below. The problem that has already been mentioned several times is that a transform can change upgrade codes at install time, so you need to retrieve the upgrade code programatically if you want to be sure you find the correct one. Unless you are trying to get the upgrade code from an MSI that is not installed on your system. Then you just need a MSI file viewer as described below in bullet point 1.
A transform is just a database fragment with changes that are applied to the original MSI at install time. It is a tool mostly used for corporate application packaging to modify installers without modifying MSI files directly. Transforms have the extension
.mst
. Changing the upgrade code via a transform is unusual, but not unheard of - especially for corporate repackaging. In rare cases application packagers may intentionally change the upgrade guid to enable them to deliver their own upgrades to the packages installed (instead of relying on the vendor updates directly). Rare, but I have seen it done. Whether this is a good thing or not is highly debatable.Easy, manual ways to find MSI upgrade codes:
Though offensively obvious, the easiest way to find the upgrade code is to open the original MSI used to install the product and find the upgrade code in the Property table. All you need is a tool capable of opening MSI files. Here are some tools: What installation product to use? InstallShield, WiX, Wise, Advanced Installer, etc. Your fastest bet is probably Orca if you have Visual Studio installed (search for
Orca-x86_en-us.msi
and install it - this is Microsoft's own, official MSI viewer and editor), or Super Orca if you don't have Visual Studio installed (follow the above link to find it).If you are a developer using WiX (or any other deployment tool), you can obviously find the upgrade code easily in your WiX source file that you used to compile your MSI (or Installshield source, Advanced Installer source, or whatever deployment tool you are using).
Even if you don't have the original MSI, it is even possible to locate the cached MSI from the original install in the
%SystemRoot%\Installer
folder. The MSI files here have a mysterious hex-name, but they are just copies of the original MSI files used to install the different products - cached in a safe place to be available for modify, repair and uninstall operations. Whatever you do, don't mess around in this folder. Never, ever delete anything. You can find the MSI that installed your product by selecting the first MSI file, and checking the Windows Explorer status bar what the product name is for older Windows version. In Windows 10 it seems you can hover over an MSI with the pointer and you get a pop-up with some MSI details. You then just click through the list until you find the right product and open the MSI and find the upgrade code in the Property table.Some people use the registry to read the upgrade codes: How can I find the upgrade code for an installed application in C#?. In my opinion this is not a good approach, there are better ways - such as just using PowerShell as explained above. There is no need for all this conversion and interpretation of packed GUIDs (which is the GUID format used in the Windows Installer registry database).
That should complete the primary "manual methods" to retrieve an upgrade code quickly. Just some methods for the arsenal that are sometimes good enough. There are probably several more ways that I have forgotten.
Do prefer the programmatic approaches, but if you are in a rush and working without all your tools available some manual options are good. However some of these manual methods require more tools than the PowerShell command line (you need an MSI file viewer which is not always available on the box if you are on a "support mission" to someone's machine). The time has come to use PowerShell (yes, I feel outdated too).
Incidentally, MSI files are essentially stripped down SQL Server databases stored as COM-structured storage files (MS Office file format). Essentially a file system within a file with storage streams of various types.
If you are stuck on a machine without an MSI viewer, you can query cached MSI databases directly from PowerShell:
To satisfy your requirements for using WMI directly, or for those times you just need a one-off without Powershell (or need to use .bat or whatever), use wmic:
There are multiple formatting and output options.