Windows
This section summarizes all of the Windows-specific features that InstallBuilder provides as well as describes solutions for commonly-found scenarios when creating Windows installers.
Windows Registry
The Windows Registry is a central hierarchical database in which Windows stores configuration information about the system and information about the installed applications and devices.
It can be manually edited using the command line through the reg.exe
command of executing the graphical registry editor, regedit.exe
. It is organized in keys, which can contain other keys (subkeys) and values, which can have different formats. The root keys on Windows, which contain all of the other subkeys and values are:
-
HKEY_LOCAL_MACHINE (HKLM)
: This key contains information about the configuration of the system that is common for all users. One of its subkeys,HKLM\SOFTWARE
, contains information about the software in the machine organized by vendor (including Microsoft, for Windows itself). This subkey is especially useful to store per-application information such as the version installed and the installation directory. This makes the detection of existing installations of your product a trivial task using InstallBuilder registry actions. -
HKEY_USERS (HKU)
: Contains all the user profiles configuration in the system. -
HKEY_CURRENT_USER (HKCU)
: This key contains information about the current logged-in user. It is not a real key but a link to the appropriate subkey insideHKEY_USERS
. The same information is stored in both keys and writing in one of them automatically updates the other. -
HKEY_CLASSES_ROOT (HKCR)
: Contains information about registered applications such as file associations. From Windows 2000, this key is a mix of the values inHKCU\Software\Classes
andHKLM\Software\Classes
. If a value is defined in both, the one inHKCU\Software\Classes
is used so per-user configuration always takes precedence. -
HKEY_CURRENT_CONFIG
: Contains information about the hardware profile used by the computer at boot time.
These main keys contain many other subkeys, which allow hierarchically organizing the registry. Inside those keys, the data is stored in values, which allow the following types:
-
REG_NONE
: Data without type defined, treated as binary information. -
REG_SZ
: Used for string values, for example paths. -
REG_EXPAND_SZ
: This value is also intended to hold string values but in addition allows them to contain environment variables, which will be expanded when reading the data. For example if the data stored is%TEMP%\myFolder
, it will be automatically expanded toC:\Users\user\AppData\Local\Temp\myFolder
when accessed while a regularREG_SZ
value would have been resolved to just%TEMP%\myFolder
. ThePath
environment variable defined inHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
is a good example of aREG_EXPAND_SZ
value. -
REG_BINARY
: Binary data. -
REG_DWORD
: A 32bit unsigned integer (little-endian) -
REG_DWORD_BIG_ENDIAN
: A 32bit unsigned integer (big-endian) -
REG_LINK
: This is used to create symbolic links to other keys, specifying the root key and the path to the target key. -
REG_MULTI_SZ
: Stores a list of non-empty list of elements, separated by the null character. An example of this key is the valuePendingFileRenameOperations
under the keyHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager
, used to specify files to rename the next time the machine is restarted. -
REG_RESOURCE_LIST
: A resource list. Used to store nested arrays by hardware devices
Managing the Windows Registry From InstallBuilder
Although the registry can be managed using the reg.exe
command line tool using a <runProgram>
action, InstallBuilder includes a set of built-in actions that allow it to easily read, write and even find data in the registry:
-
<registryGet>
: Store the value of a registry key in an installer variable. If the key or name does not exist, then the variable will be created empty.
<registryGet>
<!-- By default, InstallBuilder stores the installation
directory in this key -->
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}</key>
<name>Location</name>
<variable>previousInstallDir</variable>
</registryGet>
-
<registrySet>
: Create a new registry key or modify the value of an existing registry key.
<registrySet>
<!-- Update the installed version -->
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}</key>
<name>Version</name>
<type>REG_SZ</type>
<value>${project.version}</value>
</registrySet>
-
<registryDelete>
: Delete a registry entry. If the entry to delete is only a registry key and it does not exist, the action will be ignored. Deleting a registry value (key + name combination) that does not exist will trigger a regular error.
<!-- Clean installed keys -->
<registryDelete>
<abortOnError>0</abortOnError>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}</key>
<name></name>
</registryDelete>
-
<registryGetKey>
: Store in variable the first registry key that matches the given pattern, or set the variable to empty otherwise. The search is case-sensitive for the whole key provided.
<!-- Gets the first key referencing one of the applications
under ${project.vendor} -->
<registryGetKey>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\*</key>
<variable>application</variable>
</registryGetKey>
-
<registryGetMatch>
: Store the value of the first match of a registry key matching a certain expression in an installer variable. If the key or name does not exist, then the variable will be created empty. The name can contain a wildcard expression (using *)
<!-- Gets the data of the first value in our
application key -->
<registryGetMatch>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}</key>
<name>Loc*</name>
<variable>location</variable>
</registryGetMatch>
-
<registryFind>
: Retrieve the first registry hive and content matching a certain expression and store it as a list in an installer variable. If no match is found the variable will be created empty. This is an extension of the<registryGetMatch>
and<registryGetKey>
actions, and much more powerful. If the<findAll>
tag is set to1
, it will return a space-separated list of all of the matches. The result of this action is intended to be interpreted using a foreach action:
<registryFind>
<dataPattern>*Program Files*</dataPattern>
<findAll>0</findAll>
<keyPattern>*${project.fullName}*</keyPattern>
<namePattern>*</namePattern>
<rootKey>HKEY_LOCAL_MACHINE\SOFTWARE</rootKey>
<searchDepth>2</searchDepth>
<variable>result</variable>
</registryFind>
<foreach>
<variables>key name value</variables>
<values>${result}</values>
<actionList>
<showInfo>
<text>Key="${key}"
name="${name}"
value="${value}"</text>
</showInfo>
</actionList>
</foreach>
A much more complex application of the <registryFind>
action is explained here.
InstallBuilder also provides a <registryTest>
rule:
<!-- Set update mode if we detect that a well-known
key exists -->
<setInstallerVariable>
<name>project.installationType</name>
<value>upgrade</value>
<ruleList>
<registryTest>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}\</key>
<logic>exists</logic>
<name>Location</name>
</registryTest>
</ruleList>
</setInstallerVariable>
<!-- Throw an error if a required product is not installed -->
<initializationActionList>
<throwError>
<text>You need to install "Some Other Product" to install ${project.fullName}</text>
<ruleList>
<registryTest>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Some Vendor\Some Other Product</key>
<logic>exists</logic>
<name></name>
</registryTest>
</ruleList>
</throwError>
</initializationActionList>
It is even possible to check the type of key:
<throwError>
<text>The registry key was corrupted. It exists but it is not a `REG_EXPAND_SZ` name</text>
<ruleList>
<registryTest>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}\</key>
<logic>exists</logic>
<name>myPath</name>
</registryTest>
<registryTest>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\${project.vendor}\${project.fullName}</key>
<logic>is_not_type</logic>
<name>myPath</name>
<type>REG_EXPAND_SZ</type>
</registryTest>
</ruleList>
</throwError>
Note
|
Keys representing a "path" in the registry must be separated by backslashes
When referring to a key of the registry you have to provide a "path" with all of the parent keys as you would do with a real directory. Although InstallBuilder accepts using forward slashes instead of backslashes in Windows paths, backlashes are mandatory when working with the registry. An example of a correct reference to the key
But if you use:
What the installer is going to look for is a key named |
Windows Registry in 64bit Systems
When accessing the registry, 32bit applications running on 64bit Windows are presented with a different view of some of the keys. This process allows the isolation of 32 and 64bit applications. If you take a look to how the registry looks in Windows 64bit with regedit
you will see that some keys include a subkey named Wow6432Node
. This key contains the registry keys corresponding to the 32bit view. For example, if a 32bit application tries to access HKEY_LOCAL_MACHINE\SOFTWARE\Backstaff/InstallBuilder
it will be transparently redirected to HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Backstaff/InstallBuilder
. This process is known as "registry redirection". The special key HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node
is not visible from the 32bit view, but although it is discouraged by Microsoft guidelines, in some Windows versions it can be accessed from the 64bit view.
As InstallBuilder generates 32bit applications, it will by default follow the same registry redirection when using any of its registry actions. This may be convenient if you are bundling a 32bit application or if you are trying to read keys written by other 32bit applications but there are scenarios in which it is desirable to access the 64bit view of the registry (for example if you are bundling a 64bit application).
For these scenarios, InstallBuilder includes two ways of configuring which view of the registry should be used:
-
Project-level configuration
: The easiest way to make your application access the 64bit view of the registry by default is by using the project property<windows64bitMode>
. As explained in the next section, this setting enables much more than just making the 64bit view of the registry visible. Although the registry redirection is just enabled in 64bit OS, this setting can be just always enabled, as it will be ignored in 32bit Windows. This way there is no need to maintain two different projects for the 32 and 64bit versions of your installer or to configure this setting at build-time. This setting is applied at the very beginning of the installation process so it cannot be configured at runtime, it must be set at build-time or hardcoded in the XML project.
<project>
...
<!-- This will be ignored in 32bit
systems without consequences -->
<windows64bitMode>1</windows64bitMode>
...
</project>
Note
|
Is safe to always enable
<windows64bitMode> Even if you are building a 32bit installer, you can keep |
-
Per-action configuration
: All of the registry actions explained in the previous section accept an extra tag,<wowMode>
, which allows configuring the registry view that will be accessed. Its default value isnone
, which allows the action use the default view. Settingnone
when using<windows64bitMode>
will make the actions use the 64bit view on Windows x64. The tag also accepts64
(which selects the 64bit view) and32
as values (selecting the 32bit view) as values. The same way the<windows64bitMode>
tag is ignored in 32bit systems, setting 64 will also be ignored on them.
The <wowMode>
tag takes precedence over the <windows64bitMode>
so it is easy to configure the installer to use the 64bit view by default and just use the 32bit view when needed.
The example below tries to get the installed version of Microsoft SQL Server
in the system, checking in both registry views if the platform is 64bit:
<initializationActionList>
<registryGet>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion</key>
<name>CurrentVersion</name>
<variable>currentVersion</variable>
</registryGet>
<!-- The 64bit version takes precedence so we check it in second place -->
<registryGet>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion</key>
<name>CurrentVersion</name>
<variable>currentVersion</variable>
<wowMode>64</wowMode>
<ruleList>
<platformTest type="windows-x64"/>
</ruleList>
</registryGet>
</initializationActionList>
Or, if you are using <windows64bitMode>
, force checking in the 32bit version:
<project>
...
<windows64bitMode>1</windows64bitMode>
...
<initializationActionList>
<registryGet>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion</key>
<name>CurrentVersion</name>
<variable>currentVersion</variable>
</registryGet>
<!-- If we are using <windows64bitMode> and we couldn't detect
a 64bit version , check the 32bit key -->
<registryGet>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\CurrentVersion</key>
<name>CurrentVersion</name>
<variable>currentVersion</variable>
<wowMode>32</wowMode>
<ruleList>
<platformTest type="windows-x64"/>
<isTrue value="${project.windows64bitMode}"/>
<compareText text="${currentVersion}" logic="equals" value=""/>
</ruleList>
</registryGet>
</initializationActionList>
...
</project>
In some versions of Windows, a 32bit key on Windows x64 can be accessed in two ways:
-
Using the
<wowMode>32</wowMode>
setting (selecting the 32bit view):
<project>
...
<windows64bitMode>1</windows64bitMode>
...
<initializationActionList>
<registryGet>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Backstaff\InstallBuilder Enterprise</key>
<name>Version</name>
<variable>ibVersion</variable>
<wowMode>32</wowMode>
<ruleList>
<platformTest type="windows-x64"/>
</ruleList>
</registryGet>
</initializationActionList>
...
</project>
-
Accessing the redirected key in the 64bit registry:
<project>
...
<windows64bitMode>1</windows64bitMode>
...
<initializationActionList>
<!-- This should be avoided -->
<registryGet>
<key>HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Backstaff\InstallBuilder Enterprise</key>
<name>Version</name>
<variable>ibVersion</variable>
<ruleList>
<platformTest type="windows-x64"/>
</ruleList>
</registryGet>
</initializationActionList>
...
</project>
The latter way of accessing the 32bit key is discouraged by Microsoft guidelines and does not work in some Windows versions. The reason is that Wow6432Node
is a special key and is not intended to be accessed directly.
Note
|
Never access 32bit keys using the 64bit registry view through the
Wow6432Node keyMicrosoft guidelines discourage accessing key under |
InstallBuilder built-in registry keys
By default, all InstallBuilder-generated installers write some values in the registry. These values can be organized in two keys:
InstallBuilder writes some basic information about the installed version of the product under the key:
HKEY_LOCAL_MACHINE\SOFTWARE\${project.windowsSoftwareRegistryPrefix}
Where ${project.windowsSoftwareRegistryPrefix}
resolves to the value of <windowsSoftwareRegistryPrefix>
(${project.vendor}\${product_fullname}
by default).
The values written are:
-
Version
: Configured through the<version>
project property. -
Location
: The installation directory (${installdir}
). -
Language
: The installation language (${installation_language_code}
).
To prevent this key from being created you just have to set <windowsSoftwareRegistryPrefix>
to empty.
Another case in which the key won’t be created is when <installationType>
is set to normal
and <createUninstaller>
is set to 0
. This will also result in no uninstaller being created.
If <installationType>
is set to upgrade
, the installer will automatically update the Language
and Version
values if they exist (written in a previous installation being upgraded), regardless of the value of <windowsSoftwareRegistryPrefix>
or <createUninstaller>
. In addition, when working in upgrade
mode, if the Language
value exists, its value will be used as the default installation language.
Note
|
How to prevent the creation of the Software keys
To prevent the installer from writing the values under the |
The information stored in this key is used to populate the Add/Remove Program Menu. The information is organized in a set of values under the key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${project.windowsARPRegistryPrefix}
Where ${project.windowsARPRegistryPrefix}
resolves to the value of <windowsARPRegistryPrefix>
(${project.fullName} ${project.version}
by default). The values written by InstallBuilder are:
-
DisplayName
: Configured through the<productDisplayName>
project property. -
DisplayVersion
: Configured through the<version>
project property. -
Publisher
: Configured through the<vendor>
project property. -
DisplayIcon
: Configured through the<productDisplayIcon>
project property. -
UrlInfoAbout
: Configured through the<productUrlInfoAbout>
project property. -
Comments
: Configured through the<productComments>
project property. -
Contact
: Configured through the<productContact>
project property. -
HelpLink
: Configured through the<productUrlHelpLink>
project property. -
UninstallString
: Contains the path to the uninstaller. -
InstallLocation
: The installation directory (${installdir}
) -
NoModify
: If set to1
, disables theModify
button in the ARP Menu. -
NoRepair
: If set to1
, disables theRepair
button in the ARP Menu. -
EstimatedSize
: The size of the installed application. This value is calculated at runtime based on the installed files. -
InstallDate
: The installation date.
This key is also just created when <installationType>
is set to normal
and <createUninstaller>
is set to 1
.
If <installationType>
is set to upgrade
, the installer will update the DisplayVersion
and DisplayName
values if they exist (written in a previous installation being upgraded).
Setting <installationType>
to normal
and <createUninstaller>
to 0
will avoid creating or updating any key.
These keys are automatically deleted when uninstalling the product so you don’t have to add any additional logic to the uninstaller for that.
Windows 64bit
Creating native 64bit installers
Starting in version 19.5.0, InstallBuilder support the windows-x64 platform, which allows producing real Windows 64bit runtimes (in contrast to the legacy 64bit-capable installers, which used a 32bit runtime).
When using the new platform, you have to take into account that using windows
in folders will result in no file being packed, as that instructs the builder to use it when building 32-bit installers. The snippet below explains how to define different set of platforms for Windows 32bit and Windows 64bit:
<project>
<shortName>myProject</shortName>
<version>1.4</version>
<componentList>
<component>
<name>windowsx86</name>
...
<folderList>
<folder>
<name>windowsx86</name>
<destination>${installdir}</destination>
<!-- windows means Windows 32bit, and it will be
packed when using 'windows' as build platform -->
<platforms>windows</platforms>
<distributionFileList>
<distributionDirectory>
<origin>path/to/32bit/windows-app</origin>
</distributionDirectory>
</distributionFileList>
</folder>
</folderList>
</component>
<component>
<name>windowsx64</name>
...
<folderList>
<folder>
<name>windowsx64</name>
<destination>${installdir}</destination>
<!-- windows-x64 means Windows b4bit, and it will be
packed when using 'windows-x64' as build platform -->
<platforms>windows-x64</platforms>
<distributionFileList>
<distributionDirectory>
<origin>path/to/64bit/windows-app</origin>
</distributionDirectory>
</distributionFileList>
</folder>
</folderList>
</component>
</componentList>
</project>
You can then build the different architectures by using windows
or windows-x64
as build platform:
$> builder build project.xml windows
$> builder build project.xml windows-x64
Note
|
If you are curious, you can check how to simulate the windows-x64 build platform with older
InstallBuilder versions that only supported |
Note
|
Legacy Windows 64bit capable installers used different InstallBuilder features such as
the |
Note
|
Don’t forget to use the A similar restriction applies to the
|
Migrating From Windows 32bit Installers
As explained in the previous section, Windows x64
(windows-x64
) is a new platform, which was not previously available and is completely independent from the previous windows
target. Because of that, if you open a project with folders specifying windows
in its <platforms>
tag, and you build using windows-x64
target, they won’t be packed. The solution is to choose the proper installer target when building (Windows
from the Builder GUI Build Platform in the Packaging section or windows
as the build platform via command line) or to adapt your installer to support both windows
and windows-x64
:
<project>
<shortName>myProject</shortName>
<version>1.4</version>
<componentList>
<component>
<name>default</name>
<folderList>
<!-- This is packed when selecting "Windows" (windows) -->
<folder>
<name>windowsx86</name>
<destination>${installdir}</destination>
<!-- windows means Windows 32bit, and it will be
packed when using 'windows' as build platform -->
<platforms>windows</platforms>
...
</folder>
<!-- This is packed when selecting "Windows x64" (windows-x64) -->
<folder>
<name>windowsx64</name>
<destination>${installdir}</destination>
<!-- windows-x64 means Windows b4bit, and it will be
packed when using 'windows-x64' as build platform -->
<platforms>windows-x64</platforms>
...
</folder>
<!-- This is packed when selecting any Windows target -->
<folder>
<name>allwindows</name>
<destination>${installdir}</destination>
<!-- This will be packed for all windows targets -->
<platforms>windows-x64 windows</platforms>
...
</folder>
</folderList>
</component>
</componentList>
</project>
If you are just upgrading InstallBuilder and are not interested in the Windows x64 support, you can keep using the Windows
build target as you were previously doing. In addition, if you want to make sure you do not build Windows x64
by mistake, you can add the below snippet to your project, that will refuse to build it:
<project>
...
<preBuildActionList>
<throwError text="Windows x64 build target is not supported">
<ruleList>
<compareText text="${platform_name}" logic="equals" value="windows-x64"/>
</ruleList>
</throwError>
</preBuildActionList>
...
</project>
Note
|
The 32bit version of InstallBuilder is still available for download (and is capable of building Building Sample Project windows-x64 0% ______________ 50% ______________ 100% ######################################### Warning: You are building a 'windows-x64' installer but you are not including any folder containing 'windows-x64' in its <platforms>. However, some of them include 'windows', which won't be packed. You may be building the wrong Windows installer target for your project. If you know what you are doing, please disregard this warning. Check the below article for more information: https://installbuilder.com/docs/installbuilder-userguide.html#migrating_from_windows_32bit_installers This is just a heuristic warning based on the current folders added to your project. If checks if you are building It doesn’t mean you are doing something incorrect if you know what you are doing. It is perfectly valid only including spefic folders for
In the above example, |
Note
|
This warning will be removed from future versions of InstallBuilder but if you are incorrectly receiving it and want to silence it, you can add a dummy
|
Legacy Windows 64bit capable installers
If you are using a version of InstallBuilder older than 19.5.0 you won’t be able to generate pure Windows 64bit installers, but InstallBuilder 32bit installers can be used to properly deploy applications and drivers to 64bit operating systems.
Although 32bit installers are fully compatible with 64bit systems, they are treated differently than native 64bit applications. They most important differences are:
-
When accessing the registry, they are automatically redirected to keys in the 32bit view of the registry. This can be configured using the
<wowMode>
tag in the registry actions or through the<windows64bitMode>
project property. The registry redirection process is explained in detail in the Windows 64bit registry section. -
When executing Windows commands (such as
cmd.exe
) the filesystem redirection provides a 32bit binary version of them. Specifically the below directories are redirected (%windir% usually resolves toc:\Windows
):-
Access to
%windir%\System32
is redirected to%windir%\SysWOW64
-
Access to
%windir%\lastgood\system32
is redirected to%windir%\lastgood\SysWOW64
-
Access to
%windir%\regedit.exe
is redirected to%windir%\SysWOW64\regedit.exe
.With some exceptions, which are not redirected:
-
%windir%\system32\catroot
-
%windir%\system32\catroot2
-
%windir%\system32\drivers\etc
-
%windir%\system32\logfiles
-
%windir%\system32\spool
-
This can be solved by manually disabling the redirection using the <wow64FsRedirection>
action. This action can be used at any point during the installation and allows disabling and enabling the filesystem redirection. For example, you could use it to disable the redirection, copy a binary to %windir%\system32
and enable it again:
<project>
...
<postInstallationActionList>
<wow64FsRedirection>
<action>disable</action>
</wow64FsRedirection>
<copyFile>
<origin>${installdir}/myApp.exe</origin>
<!-- ${windows_folder_system} is a
Built-in variable resolved to %windir% -->
<destination>${windows_folder_system}</destination>
</copyFile>
<wow64FsRedirection>
<action>enable</action>
</wow64FsRedirection>
</postInstallationActionList>
...
</project>
Using <windows64bitMode>
will also disable the filesystem redirection on 64bit Windows.
-
The environment variables presented to the 32bit application are modified. These modifications affect, for example, to the default installation directory, which is configured to be under
C:\Program Files (x86)
instead ofC:\Program Files
. The only way to safely reverse this is to use<windows64bitMode>
.
If you are installing a 32bit application, Microsoft guidelines recommend that you respect the above behavior, as it is used to provide the 32bit application with the appropriate environment. However, if the application bundled is a native 64bit binary, the best way of properly configuring the installer is by enabling the <windows64bitMode>
project property. As explained in the Windows 64bit registry section, the setting is ignored in 32bit systems so it can be safely enabled in a project shared by 32 and 64bit applications.
Note
|
Enable
<windows64bitMode> when packing native 64bit applications for WindowsThe
In addition, it can always be enabled as it will be ignored on 32bit Windows (or non-Windows systems such as Linux and OS X). |
Creating specific Windows 64bit installers
If you want to distribute both 32 and 64bit versions of your installer, the project can still be configured for this purpose. The example below explains how to construct an XML project that will allow building a 32 or 64bit Windows installer on demand. It also includes some validations at runtime to prevent the user from trying to install the wrong binary on each platform.
The first step is to include your files with some "should pack rules" attached. You can find a detailed explanation of the process in the "Custom Build Targets" section:
<project>
<shortName>myProject</shortName>
<version>1.4</version>
...
<windows64bitMode>1</windows64bitMode>
...
<parameterList>
...
<stringParameter name="windowsArchitecture" value="x86" ask="0"/>
...
</parameterList>
<componentList>
<component>
<name>windowsx86</name>
...
<folderList>
<folder>
<name>windowsx86</name>
<destination>${installdir}</destination>
<distributionFileList>
<distributionDirectory>
<origin>path/to/32bit/windows-app</origin>
</distributionDirectory>
</distributionFileList>
</folder>
</folderList>
...
<shouldPackRuleList>
<compareText text="${windowsArchitecture}" logic="equals" value="x86"/>
</shouldPackRuleList>
</component>
<component>
<name>windowsx64</name>
...
<folderList>
<folder>
<name>windowsx64</name>
<destination>${installdir}</destination>
<distributionFileList>
<distributionDirectory>
<origin>path/to/64bit/windows-app</origin>
</distributionDirectory>
</distributionFileList>
</folder>
</folderList>
...
<shouldPackRuleList>
<compareText text="${windowsArchitecture}" logic="equals" value="x64"/>
</shouldPackRuleList>
</component>
</componentList>
</project>
Please note that the above also enables the <windows64bitMode>
to make your installer behave as a native 64bit application on Windows x64.
At this point, you can select whether to build a 32 or a 64bit application by passing the appropriate value when using the command line:
$> builder build project.xml --setvars windowsArchitecture=x64
The next step is to include the validation. You can include it in the components so the code will only be executed when the platform in which the installer is running does not match its bundled files:
<project>
<shortName>myProject</shortName>
<version>1.4</version>
...
<windows64bitMode>1</windows64bitMode>
...
<parameterList>
...
<stringParameter name="windowsArchitecture" value="x86" ask="0"/>
...
</parameterList>
<componentList>
<component>
<name>windowsx86</name>
...
<initializationActionList>
<throwError>
<text>You are trying to install a 32bit application in a
64bit system. Please download the correct binary from our website</text>
<ruleList>
<platformTest type="windows-x64"/>
</ruleList>
</throwError>
</initializationActionList>
<shouldPackRuleList>
<compareText text="${windowsArchitecture}" logic="equals" value="x86"/>
</shouldPackRuleList>
</component>
<component>
<name>windowsx64</name>
...
<initializationActionList>
<throwError>
<text>You are trying to install a 64bit application in a
32bit system. Please download the correct binary from our website</text>
<ruleList>
<platformTest type="windows-x86"/>
</ruleList>
</throwError>
</initializationActionList>
...
<shouldPackRuleList>
<compareText text="${windowsArchitecture}" logic="equals" value="x64"/>
</shouldPackRuleList>
</component>
</componentList>
</project>
This code will prevent the wrong binary from being installed even if the platform supports running the installer.
If you want to relax the validation in the 32bit component running on Windows 64bits because the OS will accept it and just give the the end user the opportunity to continue or abort, you could use the below code instead:
<project>
<version>1.4</version>
...
<windows64bitMode>1</windows64bitMode>
...
<componentList>
<component>
<name>windowsx86</name>
...
<initializationActionList>
<actionGroup>
<actionList>
<showQuestion>
<default>yes</default>
<text>You are trying to install a 32bit application in a
64bit system. A 64bit installer can be downloaded from our website. Do you
want to continue anyway?</text>
<variable>shouldinstall</variable>
</showQuestion>
<exit>
<exitCode>1</exitCode>
<ruleList>
<isFalse>
<value>${shouldinstall}</value>
</isFalse>
</ruleList>
</exit>
</actionList>
<ruleList>
<platformTest>
<type>windows-x64</type>
</platformTest>
</ruleList>
</actionGroup>
</initializationActionList>
...
<shouldPackRuleList>
<compareText text="${windowsArchitecture}" logic="equals" value="x64"/>
</shouldPackRuleList>
</component>
</componentList>
...
</project>
Installing applications in 32bit and 64bit folders
It is also possible to install 32bit and 64bit components into different directories. This example sets up a 32bit application installer that will install additional components - such as 64bit libraries - when executed on Windows 64bit.
To do so, you could create a <parameterGroup>
with two instances of <directoryParameter>
- one for 32bit parts and one for 64bit parts. Next, initialize a default value for them in <initializationActionList>
and pass this value to <default>
tag.
<project>
<windows64bitMode>0</windows64bitMode>
...
<initializationActionList>
<setInstallerVariable>
<name>installationroot32</name>
<value>${platform_install_prefix}</value>
</setInstallerVariable>
<setInstallerVariable>
<name>installationroot64</name>
<value>${platform_install_prefix}</value>
</setInstallerVariable>
<setInstallerVariable>
<name>installationroot64</name>
<value>${env(ProgramW6432)}</value>
<ruleList>
<platformTest>
<type>windows-x64</type>
</platformTest>
</ruleList>
</setInstallerVariable>
</initializationActionList>
...
<componentList>
<component>
...
<folderList>
<folder>
<description>Program Files (32bit)</description>
<destination>${installdir}</destination>
<name>programfileswindows</name>
<platforms>windows</platforms>
<ruleList>
<platformTest type="windows" />
</ruleList>
(...)
</folder>
...
<folder>
<description>Program Files (64bit)</description>
<destination>${installdirx64}</destination>
<name>programfileswindowsx64</name>
<platforms>windows</platforms>
<ruleList>
<platformTest type="windows-x64" />
</ruleList>
...
</folder>
</folderList>
</component>
</componentList>
...
<parameterList>
<parameterGroup>
<name>installdirs</name>
<explanation></explanation>
<value></value>
<default></default>
<parameterList>
<directoryParameter>
<name>installdir</name>
<description>Installer.Parameter.installdir.description</description>
<explanation>Installer.Parameter.installdir.explanation</explanation>
<value></value>
<default>${installationroot32}/${project.shortName}-${project.version}</default>
<allowEmptyValue>0</allowEmptyValue>
<cliOptionName>prefix</cliOptionName>
<mustBeWritable>1</mustBeWritable>
<mustExist>0</mustExist>
<width>40</width>
</directoryParameter>
<!-- folder for 64-bit specific files -->
<directoryParameter>
<name>installdirx64</name>
<description>Installer.Parameter.installdirx64.description</description>
<explanation>Installer.Parameter.installdirx64.explanation</explanation>
<value></value>
<default>${installationroot64}/${project.shortName}-${project.version}</default>
<allowEmptyValue>0</allowEmptyValue>
<cliOptionName>prefix</cliOptionName>
<mustBeWritable>1</mustBeWritable>
<mustExist>0</mustExist>
<width>40</width>
<ruleList>
<platformTest>
<type>windows-x64</type>
</platformTest>
</ruleList>
</directoryParameter>
</parameterList>
</parameterGroup>
</parameterList>
</project>
On 32bit Microsoft Windows operating systems, the user will only be asked about one installation directory. The installer will deploy the 32bit files to that directory while 64bit files will be skipped.
On 64bit systems, the user will have the option of choosing directories for both 32bit and 64bit files. The installer will deploy the 32bit and 64bit files to the appropriate directories. As the installer is running as a 32bit application, certain target directories will point to their 32bit counterparts - such as the system
directory for installing drivers. You must use <wow64FsRedirection>
action to enable/disable this redirection when deploying drivers and/or other files to the WINDOWS
directory.
Note
|
The installer must be running in 32bit mode
The |
Managing Access Control Lists
Access Control Lists (ACLs) allow defining which users or groups can perform certain operations on one or more files. This allows preventing or granting access to reading or writing to files to certain users.
The <setWindowsACL>
action allows configuring the ACLs of the desired files for the specified set of users. For example, to grant all permissions to all users you could use the following code:
<setWindowsACL>
<action>allow</action>
<files>${installdir}/admin;${installdir}/admin/*</files>
<permissions>generic_all</permissions>
<users>S-1-1-0</users>
</setWindowsACL>
The <setWindowsACL>
action supports the following tags:
-
<users>
: Comma separated list of users to set permissions for. -
<action>
: Whether to allow (allow
action) or deny (deny
action). -
<permissions>
: Space-separated list of permissions to set -
<files>
: List of files or file patterns to match; separated by semi-colon or newlines. -
<excludeFiles>
: List of files or file patterns to exclude from the defined<files>
. -
<self>
: Determines if the objects specified in the<files>
tag will be modified or just their children, if the recursion tags are enabled. -
<recurseOneLevelOnly>
: If enabled, the action will only affect the first level of hierarchy if one of the below is enabled. -
<recurseObjects>
: The action will affect to child objects (files) -
<recurseContainers>
: The action will affect to child to containers (folders)
The <clearWindowsACL>
action allows removing all of the ACLs for the specified files or directories.
For example, in order to make sure just the Administrators group can access some files, you should first remove all of the current ACLs (that may be inherited from a parent directory) and then the permissions appropriately:
<clearWindowsACL>
<files>${installdir}/admin;${installdir}/admin/*</files>
</clearWindowsACL>
<setWindowsACL>
<action>allow</action>
<files>${installdir}/admin;${installdir}/admin/*</files>
<permissions>file_all_access</permissions>
<users>S-1-5-32-544</users>
</setWindowsACL>
The <clearWindowsACL>
action supports the following tags:
-
<files>
: List of files or file patterns to match; separated by semi-colon or newlines -
<excludeFiles>
: List of files or file patterns to exclude from the defined<files>
.
It is also possible to retrieve the ACL for a given user over a certain file using the <getWindowsACL>
action. For example, the following will set granted
and denied
variables to the space-separated list of permissions for specified user:
<getWindowsACL>
<deniedPermissions>denied</deniedPermissions>
<file>${installdir}/admin</file>
<grantedPermissions>granted</grantedPermissions>
<username>S-1-1-0</username>
</getWindowsACL>
The <getWindowsACL>
action supports the below tags:
-
<file>
: File to retrieve the list of permissions for. -
<username>
: User or group to retrieve the list of permissions for. -
<grantedPermissions>
: Variable used to store the list of granted permissions. -
<deniedPermissions>
: Variable used to store the list of denied permissions.
When specifying a user for ACL actions, it can either be a user name, group name or a Security Identifier (SID
). User names and group names are names of local or domain users and groups. SIDs are internal identifiers that specify unique user identification as well as several global values that are the same for all Windows based computers - such as Everyone
, which maps to S-1-1-0
and Administrators
which maps to S-1-5-32-544
. Using SIDs is the recommended approach when referring to well known groups as the name of the groups is localized depending on the OS language.
More details on universal well-known SID values can be found on MSDN:
The <permissions>
tag can include any number of permissions, separated by space. The following permissions are allowed in the ACL related actions:
ACL permissions | ||
---|---|---|
permission |
file permission |
directory permission |
file_read_data |
allow reading from file |
allow listing contents of directory |
file_write_data |
allow writing to file |
allow creating files |
file_append_data |
allow appending data to file |
allow creating subdirectory |
file_read_ea |
allow reading extended attributes |
allow reading extended attributes |
file_write_ea |
allow writing extended attributes |
allow writing extended attributes |
file_execute |
allow running a binary |
allow traversing directory |
file_delete_child |
N/A |
allow deleting directory and its children, even if files are read-only |
file_read_attributes |
allow reading attributes |
allow reading attributes |
file_write_attributes |
allow writing attributes |
allow writing attributes |
For setting access, the following generic permissions can also be used:
Generic ACL permissions | |
---|---|
permission |
description |
file_all_access |
allow all available permissions |
file_generic_read |
allow common read permissions for file, directory and its attributes |
file_generic_write |
allow common write permissions for file, directory and its attributes |
file_generic_execute |
allow common execution permissions for file, directory and its attributes |
More details on permissions related to files can be found on MSDN:
Note
|
ACLs are only supported on NTFS file systems.
If the action is used in a non-supported file system, it will silently fail. |
Changing file attributes
File and folder attributes are set using the <changeWindowsAttributes>
action.
For example, the following action can be used to set read-only
and system
attributes for admin
subdirectory and all its child files:
<changeWindowsAttributes>
<files>${installdir}/admin;${installdir}/admin/*</files>
<readOnly>1</readOnly>
<system>1</system>
</changeWindowsAttributes>
It accepts the following tags:
-
<files>
: List of files or file patterns to match; separated by semi-colon or newlines -
<excludeFiles>
: List of files or file patterns to exclude from the defined<files>
. -
<hidden>
: Whether or not the specified files should not be visible in applications such as Windows Explorer. -
<readOnly>
: Whether or not the specified files should allow write access. -
<system>
: Whether or not the specified files must be marked as system files. -
<archive>
: Whether or not the specified files must be marked to be archived. Some applications use this attribute to know which files should be backed up.
Please note that only setting these attributes does not prevent users from modifying the files, as the user can still unset each of these attributes manually. In order to prevent users (such as non-administrators) from modifying or accessing certain files, Access Control Lists should be used instead.
Read-only and system attributes can only be set for files. They are ignored by the operating system if applied to a folder. It is documented in more detail by Microsoft: