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
strResult = ""
intValue = intDEC
intExp = 32768
while intExp >= 1
if intValue >= intExp then
intValue = intValue - intExp
strResult = strResult & "1"
strResult = strResult & "0"
intExp = intExp / 2
DecToBin = strResult
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`")
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!"
If isComponent64Bit = "1" Then
MsgBox "Component '" & componentName & "' is a 64-bit component!"
Set rec = View.Fetch
Set View = Nothing
Set rec = Nothing