14. Uninstaller

As part of the installation process, an uninstaller executable is created. In fact, two files are created, an executable runtime, the one that the user should invoke to uninstall the application and a data file (usually named uninstall.dat), containing information data about the installation (installed files, component selected, value of the variables…). Both files must exist to ensure a correct uninstallation.

By default, the uninstaller will remove all files created by the installer, as well as shortcuts and entries in Start Menu and the Add/Remove Programs menu. The default behavior for the uninstaller is to not remove any files that it did not create. This is intended to prevent accidental removal of user-generated content.

Please also note that actions performed during installation such as <addDirectoryToPath> or <registrySet> won’t be automatically reverted by the uninstaller. They must be manually included in the <preInstallationActionList> or <postInstallationActionList>. An exception to this rule is the <createShortcuts> action, which will register the new shortcuts created in the uninstaller.

It is also possible to allow the uninstallation of individual components as explained in the Adding or removing components to existing installations section.

The uninstaller name and location can be also configured:

  <project>
    ...
    <uninstallerDirectory>${installdir}/internals/uninstall</uninstallerDirectory>
    <uninstallerName>UninstallApplication</uninstallerName>
    ...
  </project>

On Windows and OS X, a prefix will be automatically appended to the uninstaller name (.app on OS X and .exe on Windows).

You can even prevent an uninstaller from being created:

  <project>
    ...
    <createUninstaller>0</createUninstaller>
    ...
  </project>

14.1. Uninstaller Action Lists

A number of actions can be run as part of the uninstallation process. They are typically used to revert changes performed at installation time: deleting newly created users, removing environment variables, uninstalling services and so on.

  • <preUninstallationActionList>: Executed when the uninstaller is started. It can be used for example to prompt the end-user to collect information to be used during the uninstall process, stop any running services and so on.
  • <postUninstallationActionList>: Executed after the main uninstallation process has taken place. It is typically used to delete files and directories not covered by the uninstaller or perform any last-minute cleanups.

If an error is found during uninstallation, it is silently ignored and the uninstallation continues, although the rest of actions are not executed. This behavior is necessary because there is nothing more frustrating to end users than having an uninstaller fail for some minor issue and thus, for example, remain in the Add/Remove Program menu.

In case you need to display an error message and abort the uninstaller, one possible workaround is to combine <showWarning> and <exit> actions inside an <actionGroup>, as shown below:

<preUninstallationActionList>
  <actionGroup>
    <actionList>
       <showWarning>
          <text>Program 'foo' is running, aborting uninstallation.</text>
       </showWarning>
       <exit/>
    </actionList>
    <ruleList>
       <processTest name="foo" logic="is_running" />
    </ruleList>
  </actionGroup>
</preUninstallationActionList>

Uninstallers only allow minimal configuration and no customization with parameters, though it is possible to use actions to display dialogs. In any case, it is recommended that you do not include complex logic that could break the uninstallation flow and leave the target machine in an inconsistent state.

14.2. Marking Additional Files for Deletion

The uninstaller automatically removes all of the files unpacked as part of the installation process.

Only files bundled and unpacked directly by InstallBuilder in the installation step will be uninstalled. For example, if during the installation process your installer uncompresses a zip file, or if the user creates new files by hand, those files will not be removed. However, it is possible to address this in different ways, such as adding <deleteFile> actions to the <preUninstallationActionList>:

<preUninstallationActionList>
   <deleteFile path="${installdir}/zipContentsDir"/>
</preUninstallationActionList>

In general, you should not use <postUninstallationActionList> to delete files because at this point, the uninstaller has already tried to delete the installed files. If it found some unexpected files in one of the directories to delete, it will skip deleting that directory, so even after specifically deleting the ${installdir}/zipContentsDir an empty ${installdir} will remain.

[Note]

Sometimes it may be tempting to add something like <deleteFile path="${installdir}"/> to the installer logic, to ensure that all files are removed. This is not recommended. If the user accidentally enters "/" or c: as the installation directory, the installer will basically attempt to delete the entire contents of the filesystem.

As an alternative, InstallBuilder includes a couple of actions, <addFilesToUninstaller> and <addDirectoriesToUninstaller> to specifically register external files in the uninstaller so they will be handled as if they were regularly unpacked files:

<addFilesToUninstaller files="/some/path/to/a/file.txt"/>

The file will now automatically be removed during the uninstallation process. This action can also be used to register directories, but they will be handled as a single file. For example, if you register a directory foo and after the installation the user stores some information there, as the installer just registered the path to delete and not its contents, it will remove the whole directory, independently of the contents.

In some cases, this functionality makes it easier to clean up a directory, without worrying about its contents changing. In other cases a more conservative approach is required, to make sure files are not accidentally removed. In these scenarios you can use the <addDirectoriesToUninstaller> action:

 <addDirectoriesToUninstaller>
    <addContents>1</addContents>
    <files>${installdir}/zipContentsDir</files>
    <matchHiddenFiles>1</matchHiddenFiles>
  </addDirectoriesToUninstaller>

The above will register all of the contents of the ${installdir}/zipContentsDir directory with the uninstaller at the time the action was run. If a new file is created afterwards, it will not be removed.

[Note]

Files need to exist before they can be added to the uninstaller. If you need to add a file to the uninstaller that does not yet exist, you can work around that by first creating an empty placeholder, as shown below.

<touchFile path="/some/path/to/a/file.txt"/>
<addFilesToUninstaller files="/some/path/to/a/file.txt"/>

14.3. Preventing Files from Being Deleted

Conversely there are times when you want to prevent certain files, such as configuration files, from being uninstalled. You can use the <removeFilesFromUninstaller> action for this. The following example will prevent any files under the conf/ directory that exists at the time the action is run from being removed:

<removeFilesFromUninstaller files="${installdir}/conf/*" />
[Note]

The uninstaller is created right after the <postInstallationActionList> so this is the last point in the installation lifecycle at which the files to be uninstalled can be modified.

14.4. Interacting with the End User

An uninstaller cannot contain custom parameters. If you need to request information from the end user, you can use any of the dialogs detailed in the dialogs section. For example, if you need to ask whether certain database data should be deleted as part of the uninstallation, you can do the following:

<preUninstallationActionList>
   <showQuestion text="Do you want the uninstallation to also remove the database data?" variable="remove_mysql" />
   <deleteFile>
      <path>${installdir}/mysql/data</path>
      <ruleList>
         <compareText text="${remove_mysql}" value="yes" logic="equals" />
      </ruleList>
   </deleteFile>
 </preUninstallationActionList>