MSI Package Code Fun
by dwarfsoft on Jun.22, 2010, under Uncategorized
I have recently been building quite a few MSI packages, and as of the end of last week I was required to go back through and rebuild a whole batch of them. Due to the way a lot of deployments work from Novell Zenworks, Product Codes are used quite frequently to stop the MSI from installing more than once (thereby disabling multiple installs). I believe that this is due to the MSI Template that is used corporately having a few Dialogs missing for Repair/Modify. Therefore when the second install occurs the MSI dies a painful death and the user is presented with a number of errors.
So, rather than fix the source of the problem I work within the structure I am given. After my forays in the past couple of days I found it immensely frustrating trying to get the Product Code key to update our documentation for each MSI which had been rebuilt. This is because, up until now, I had been installing the MSI and searching for the MSI name under HKEY_CLASSES_ROOT\Installer\Product\. The code displayed under this key had confused me for quite some time until I found some information regarding a “Packed GUID“.
As it turns out, the code I linked to doesn’t work. It did, however, lead me to figure out HOW to make it work by making me assess each of the “chunks” in the original GUID.
Effectively there are 5 parts to Each GUID
{11111111-2222-3333-4444-555555555555}
The “Packed GUID” is a regrouping of these numbers, removing the dashes and braces. So, for the first group (11111111) the order in the original GUID appears as ABCDEFGH. The order in the Packed GUID is HGFEDCBA. The Second group (2222) is appended in reverse again (from ABCD to DCBA). The third group is also appended in reverse (from ABCD to DCBA).
The last two groups are a little more complex. Each pair is switched such that for group four (4444) the original GUID appears as ABCD but the packed guid appears as BADC. The last group (555555555555) follows this order as well so for the order ABCDEFGHIJKL the packed GUID will be appended reordered as BADCFEHGJILK.
In VBScript Terms this can be expressed as the following:
Function PackGUID(guid) PackGUID = "" '* Dim temp temp = Mid(guid,2,Len(guid)-2) Dim part part = Split(temp,"-") Dim pack pack = "" Dim i, j For i = LBound(part) To UBound(part) Select Case i Case LBound(part), LBound(part)+1, LBound(part)+2 For j = Len(part(i)) To 1 Step -1 pack = pack & Mid(part(i),j,1) Next Case Else For j = 1 To Len(part(i)) Step 2 pack = pack & Mid(part(i),j+1,1) & Mid(part(i),j,1) Next End Select Next '* PackGUID = pack End Function |
So, this was the first step for the time saving measure of extracting the right code. The next issue was how to get the codes in the first place. I knew that there had to be an easier way than opening up InstallShield and Copying out the Codes manually. The first thing that came to mind was the Summary Information Stream of an MSI file (as you can see by right-clicking on an MSI and going to Properties, then Summary). There was a lot of information about this, particularly from MSDN. This lead me to getting the Package Code:
'create installer object Set objDictionary = CreateObject("Scripting.Dictionary") Set oInstaller = CreateObject("WindowsInstaller.Installer") 'open msi in read-only mode Set oDatabase = oInstaller.OpenDatabase(MSIPath, 0) ' Get Package Code from Summary Information Stream Set streamobj = oDatabase.SummaryInformation(0) '0 = read only objDictionary("PackageCode") = streamobj.Property(9) |
This is useful, but the Code that HKCR\Installer\Product\{Packed GUID} references comes from the MSI Product Code. So I went back to the drawing board and researched VBScript Installer Database Product Code some more and found this, followed by this gem. From here it was very easy to pull out GUID’s from the MSI packages, and I didn’t even need to do it on a machine with InstallShield actually Installed. The Windows Installer takes care of everything.
Complete code (from start to finish):
'Created by: Chris Bennett 'Created Date: 22/06/2010 'Description: ' Opens up MSI file(s) Passed as Arguments and returns ProductName, ProductCode, ' The HKCR key created from ProductCode (a Packed GUID of ProductCode), the PackageCode ' and the UpgradeCode of the MSI. Much quicker than getting these out of the MSI's the ' Manual Way. 'References: ' http://msdn.microsoft.com/en-us/library/aa369794%28VS.85%29.aspx ' http://www.eggheadcafe.com/forumarchives/platformsdkmsi/Jan2006/post25948124.asp For Each MSIPath in WScript.Arguments Set MSIDetails = EvaluateMSI(MSIPath) WScript.Echo MSIPath & ": " WScript.Echo " Product Name: " & MSIDetails("ProductName") WScript.Echo " Product Code: " & MSIDetails("ProductCode") WScript.Echo " Product Key : " & "HKCR\Installer\Products\" & PackGUID(MSIDetails("ProductCode")) WScript.Echo " Package Code: " & MSIDetails("PackageCode") WScript.Echo " Upgrade Code: " & MSIDetails("UpgradeCode") WScript.Echo "" Next Function EvaluateMSI(MSIPath) On Error Resume Next 'create installer object Set oInstaller = CreateObject("WindowsInstaller.Installer") 'open msi in read-only mode 'Set oDatabase = oInstaller.OpenDatabase("d:\USERDATA\BennettCm\Desktop\EN-UNHIDE-OUTLOOK-1-01.msi", 0) Set oDatabase = oInstaller.OpenDatabase(MSIPath, 0) Set objDictionary = CreateObject("Scripting.Dictionary") ' Get Package Code from Summary Information Stream Set streamobj = oDatabase.SummaryInformation(0) '0 = read only objDictionary("PackageCode") = streamobj.Property(9) ' Get Product Name from MSI Database Set View = oDatabase.OpenView("Select `Value` From Property WHERE `Property`='ProductName'") View.Execute Set ProductName = View.Fetch objDictionary("ProductName") = ProductName.StringData(1) ' Get Product Code from MSI Database Set View = oDatabase.OpenView("Select `Value` From Property WHERE `Property`='ProductCode'") View.Execute Set ProductCode = View.Fetch objDictionary("ProductCode") = ProductCode.StringData(1) ' Get Upgrade Code from MSI Database Set View = DB.OpenView("Select `Value` From Property WHERE `Property`='UpgradeCode'") View.Execute Set UpgradeCode = View.Fetch objDictionary("UpgradeCode") = UpgradeCode.StringData(1) Set EvaluateMSI = objDictionary On Error Goto 0 End Function Function PackGUID(guid) PackGUID = "" '* Dim temp temp = Mid(guid,2,Len(guid)-2) Dim part part = Split(temp,"-") Dim pack pack = "" Dim i, j For i = LBound(part) To UBound(part) Select Case i Case LBound(part), LBound(part)+1, LBound(part)+2 For j = Len(part(i)) To 1 Step -1 pack = pack & Mid(part(i),j,1) Next Case Else For j = 1 To Len(part(i)) Step 2 pack = pack & Mid(part(i),j+1,1) & Mid(part(i),j,1) Next End Select Next '* PackGUID = pack End Function |
Run from cscript it outputs like:
P:\Folder\To\MSI\MSI Name.msi:
Product Name: MSI Name
Product Code: {A2961B30-4875-4535-AE36-DA064263BA50}
Product Key : HKCR\Installer\Products\03B1692A57845354EA63AD602436AB05
Package Code: {5F735447-A72D-4571-8C8D-AD80E2B30F22}
Upgrade Code: {A2961B30-4875-4535-AE36-DA064263BA50}
I created a Batch File that also makes use of this script to trawl through folders pulling out the information and dumping it into a Codes.txt file.
@ECHO OFF REM Created By: Chris Bennett REM Created Date: 22/06/2010 REM Description: REM Finds MSI''s under the current Directory (or passed Directory) REM Calls GetMSICodes.vbs to extract the MSI Package,Product and Upgrade Codes REM Saves these codes into Codes.txt CD /D %~dp0 IF /I "%~1" NEQ "" CD /D %1 FOR /F "tokens=* delims=" %%A IN ('DIR *.MSI /B /S') DO ( cscript.exe GetMSICodes.vbs "%%A" //Nologo >> Codes.txt ) |
Hope you enjoyed, I certainly did
Cheers, Chris.

hi,
Thank you very for giving a valuable data.
i have question is it possible to change the package code ?
like taking the package code from the old version of the msi and then put in to the new package code for the new msi
is it possible ?
Thanks
Vijay r
The package code cannot be changed directly in one MSI without “upgrading” the original package code. I have done this previously by embedding an “Update” MSI that basically uninstalls the original MSI in my new MSI and run it first. This way the original MSI Package code is “upgraded” and removed from the system.
It’s not entirely pretty, but it is effective
Hope that helps,
Cheers, Chris.