Ahhhh. A lovely topic.
A couple of weeks ago, I needed to scan an MSI for 64-bit components. I knew each component had an ‘attribute’ value (which is actually a bit flag), but I needed to figure out a way to extract each bit flag so that I could find out how the component was configured and which attributes were set.
Here’s how I did it….
Firstly, let’s list down the component attributes and their corresponding bit flags:
|Bit Flag Description||Decimal Value|
|Component cannot be run from source||0|
|Component can only be run from source||1|
|Component can run locally or from source||2|
|Has a Registry Keypath||4|
|Increments Shared DLL Registry Count||8|
|Permanent Component (doesn’t remove on uninstall)||16|
|Has an ODBC Data Source Keypath||32|
|Re-evaluates Condition on Reinstall||64|
|Do Not Overwrite if Component Exists||128|
|Disable Registry Reflection||512|
|Prevent Leaving Orphan Components||1024|
When we want to set multiple bit flags on a component, we add the decimal values together. For example, let’s say we have a component which contains a 64-bit DLL as the keypath, and we want to incrememnt the shared DLL reference count. The attribute of our component would be:
(Increments Shared DLL Registry Count + 64-Bit Component)
= 8 + 256
Now let’s reverse things. If we have a value of 264 in our attribute column, how do we work out which decimal values were added together to make this up?
The answer is that we need to convert the decimal value to a binary value. Here goes a quick binary lesson:
A 16-bit binary value would consist of 16 1’s and 0’s. For example, 0010101000101101. Each ‘bit’ flag has a corresponding decimal value. In the table below, we’ll use our 16-bit shared DLL component as an illustrated example:
The MSI component attribute only uses the first 13 bits. The last 3 bits will always be zero. By converting the decimal value of 264 in to binary, we can see which bit flags are set and therefore decipher the attribute which has been applied to the component.
Now let’s see it in code….
'Function to convert decimal to 16-bit binary Function DecToBin(intDec) dim strResult dim intValue dim intExp strResult = "" intValue = intDEC intExp = 32768 while intExp >= 1 if intValue >= intExp then intValue = intValue - intExp strResult = strResult & "1" Else strResult = strResult & "0" end if intExp = intExp / 2 Wend DecToBin = strResult End Function Dim componentBinary : componentBinary = 0 Dim componentName : componentName = "" Dim isComponent64Bit : isComponent64Bit = 0 Dim isComponentSharedDLL : isComponentSharedDLL = 0 'Select the name and attribute for each component Set View = Database.OpenView("SELECT Component, Attributes FROM `Component`") View.Execute Set rec = View.Fetch Do While Not rec Is Nothing 'get component name componentName = rec.StringData(1) 'get the attribute (decimal value) and convert it to 16-bit binary componentBinary = DecToBin(Cint(rec.StringData(2))) 'get bit flag for shared dll (4th bit) - remember vbscript arrays start at 0! isComponentSharedDLL = Mid(componentBinary,3,1) 'get bit flag for 64-bit (9th bit) - remember vbscript arrays start at 0! isComponent64Bit = Mid(componentBinary,8,1) If isComponentSharedDLL = "1" Then MsgBox "Component '" & componentName & "' is a shared DLL resource!" End If If isComponent64Bit = "1" Then MsgBox "Component '" & componentName & "' is a 64-bit component!" End If Set rec = View.Fetch Loop View.Close Set View = Nothing Set rec = Nothing