26. 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.

26.1. 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 inside HKEY_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 in HKCU\Software\Classes and HKLM\Software\Classes. If a value is defined in both, the one in HKCU\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 to C:\Users\bitrock\AppData\Local\Temp\myFolder when accessed while a regular REG_SZ value would have been resolved to just %TEMP%\myFolder. The Path environment variable defined in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment is a good example of a REG_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 value PendingFileRenameOperations under the key HKEY_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 to 1, 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 InstallBuilder:

HKEY_LOCAL_MACHINE\SOFTWARE\BitRock\InstallBuilder

But if you use:

HKEY_LOCAL_MACHINE\SOFTWARE\BitRock/InstallBuilder

What the installer is going to look for is a key named BitRock/InstallBuilder, which is a perfectly valid key name but not the one you expected.

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\BitRock it will be transparently redirected to HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\BitRock. 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 <windows64bitMode> enabled in you project. On 32bit systems it will just be ignored.

  • 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 is none, which allows the action use the default view. Setting none when using <windows64bitMode> will make the actions use the 64bit view on Windows x64. The tag also accepts 64 (which selects the 64bit view) and 32 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\BitRock\BitRock 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\BitRock\BitRock 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 key

Microsoft guidelines discourage accessing key under Wow6432Node directly from the 64bit view of the registry. It is known to fail in some Windows versions. The correct way of accessing a 32bit key from the 64bit view (enabled using <windows64bitMode>) is setting wowMode="32".

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:

Software Key. 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 HKEY_LOCAL_MACHINE\SOFTWARE key, just set the <windowsSoftwareRegistryPrefix> to empty.

Add/Remove Program Menu Key. 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 to 1, disables the Modify button in the ARP Menu.
  • NoRepair: If set to 1, disables the Repair 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.

26.2. Windows 64bit

InstallBuilder allows you to build installers that are 32bit applications, but 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 to c:\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 of C:\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 Windows

The <windows64bitMode> project property makes an installer behave as a 64bit application by modifying its access to the environment:

  • Disables the filesystem redirection
  • Disables the registry redirection
  • Gives access to the 64bit environment variables

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

Although InstallBuilder does not support a windows-x64 platform, 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 <windows64bitMode> project setting must be set to 0 (the default value) to make the installer run in 32bit mode. If installer were running in 64bit mode, the default installdir would be C:\Program Files, not C:\Program Files (x86).

26.3. 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:

http://msdn.microsoft.com/en-us/library/aa379649.aspx

The <permissions> tag can include any number of permissions, separated by space. The following permissions are allowed in the ACL related actions:

Table 5. ACL permissions

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:

Table 6. Generic ACL permissions

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:

http://msdn.microsoft.com/en-us/library/aa394063.aspx#properties

[Note]ACLs are only supported on NTFS file systems.

If the action is used in a non-supported file system, it will silently fail.

26.4. 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:

http://support.microsoft.com/kb/326549