:::: MENU ::::

How to Convert a Program GUID to its Product Code

In Windows, there are MSI packages that automatically cache themselves on the local machine. Examples include legacy MS Office applications, Adobe Acrobat, and other such big names. But I find that many lesser known applications do not automatically cache their installation source files on the local machine. This is because the program extracts the MSI to the temp folder where it would later be cleaned up by garbage collection. Or the package simply relies on the same directory from which the MSI was last run. This tends to be an unc path and sometimes, repairs have to be done while the share is unavailable. Either way, it makes alterations and repairs to installations difficult (or impossible) if performed without the original installation package(s).

One way to forcibly cache the source MSI files for such programs is to go into the sourcelist registry key and change the registry values to point to a local directory containing the MSI. While I won’t go into much detail here on how to do this, what I will explain is how to find the correct sourcelist directory for a particular program.

The Source Lists are found in either of these registry paths:

HKEY_CLASSES_ROOT\Installer\Products\<product code>\SourceList\Net

HKEY_CURRENT_USER\Software\Microsoft\Installer\Products\<product code>\SourceList\Net

HKEY_LOCAL_MACHINE\Installer\Products\<product code>\SourceList\Net

Where the “<product code>” is a digested version of the application’s GUID. If you are doing things by hand, you may simply search for the application name under each of the reg keys, and you might get lucky. But to automatically redirect msi caches via script requires that you determine the product code of the application you are interested in. We start by finding the GUID.

More information here: https://technet.microsoft.com/en-us/library/bb892810.aspx

You can find the GUID for an application by either:

  1. visiting the uninstalls registry key
  2. running orca.exe to look at the database file
  3. or by running this command-let from PowerShell:

Get-wmiobject win32_product

Once you have the GUID, you can convert it into the Product Code by:

  1. Removing all of the dashes and curly brackets.
  2. Reverse the order of the first 8 characters
  3. Reverse the order of the next four characters
  4. Reverse the order of the next four characters
  5. Then reverse the order of every 2 characters for the rest of the string.

Example:

Original String:

01C5A10F AD9B 405B 85 3A 66 59 84 1A 12 42

Converted String

F01A5C10 B9DA B504 58 A3 66 95 48 A1 21 24

Credit: http://forum.sysinternals.com/finding-all-installed-programs-from-the-registry_topic21312.html

I wrote a function (available here) that can do this for you.

Since GUIDs are standardized in format and length, I decided to statically reassign the position of each character based on their indexed position in the string.

The first task is to use regex to strip away all non-alphanumeric characters, saving the resultant text to the $ProductIDChars variable:

$ProductIDChars = [regex]::replace($ProductID, "[^a-zA-Z0-9]", "")

Then I take the index number for each character in the GUID and list them in order. The order is defined by the relationship of a GUID to a “<product code>” ID. Which is to say, the first 8 char in the GUID (0 to 7) are listed 7, 6, 5, 4, 3, 2, 1, 0 and then next four (8 to 11) will be 11, 10, 9 ,8 etc…. so on as per the list in the instructions above.

$RearrangedCharIndex = 7,6,5,4,3,2,1,0,11,10,9,8,15,14,13,12,17,16,19,18,21,20,23,22,25,24,27,26,29,28,31,30

Lastly, for each number in the $RearrangedCharIndex list, I pull the corresponding character from $ProducIDChars and join them to a single string. The result of this is the product code:

Return -join ($RearrangedCharIndex | ForEach-Object{$ProductIDChars[$_]})

To use this in your own script, just initialize the function, then run the Convert-GUIDtoPID commandlet like so:

GuidtoPID


Script(s) Used in This Post:

Convert Guid to PID (//adameyob.com/2016/03/convert-program-guid-product-code/)


 


Subscribe
Notify of

5 Comments
Inline Feedbacks
View all comments

I did a little PS Golfing and this is the result:
Here’s one that does the same thing, only a little bit (2-5x) faster. Could be shorter… but you lose performance
-join(“{YOUR-GUID-HERE}”-replace”\W”)[7..0+11..8+15..12+17..16+19..18+21..20+23..22+25..24+27..26+29..28+31..30]

….and after golfing a bit we get this horrendous garbage:
-join(($s=$args[0]-replace”\W”)[7..0+11..8+15..12]+(8..15|%{$s[($_*2+1)..($_*2)]}))

You’ll be able to use it by wrapping it in a function (below) or putting it in its own .ps1 file(not shown below):
function f{-join(($s=$args[0]-replace”\W”)[7..0+11..8+15..12]+(8..15|%{$s[($_*2+1)..($_*2)]}))}
f ‘{01C5A10F AD9B 405B 85 3A 66 59 84 1A 12 42}’

There’s a small error in this script which will only show when strictmode is turned-on.
The $RearrangedCharIndex is build for a 33 character guid (the 32 on the end).

When using strictmode this will result in the error: “Index was outside the bounds of the array” because the GUID does not contain a 33th character.
To correct this error just remove the last “32” from the $RearrangedCharIndex.

$guid=[guid]::Parse(‘{23170f69-d0c1-2701-0920-000001000000}’)
$prodid=”
$guid.ToByteArray() | % { $prodid += ($_ -band 15).ToString(‘X’) + ($_ -shr 4).ToString(‘X’) }
$prodid