Search code examples
installationnsisuninstallation

Handling NSIS Uninstall.dat and the AdvUninstLog plugin


Scenario

An application is being installed via NSIS. As is required by NSIS, an uninstaller with an Uninstaller.dat is provided as well.

It is desired to collect all files that are to be removed during deinstallation using

  • !insertmacro UNINSTALL.LOG_OPEN_INSTALL
  • !insertmacro UNINSTALL.LOG_CLOSE_INSTALL

which are both provided by the AdvUninstLog plugin. However this is apparently impossible. If an uninstallation already exists on a machine and the plugin is active, the installation process starts taking an absurd amount of time, i.e. 5-10 minutes. That is because the ${Locate} "${TargetDir}" "/L=FD" "${UnLog_Install_Func_CallBack}" calls of the plugin begin searching the installation directory for files, endlessly. I am not clear as to why this behavior occurs. My suspicion is, that the Uninstall.dat contains files, that do not exists anylonger and have been deleted by users, causing long searches for the file. I am not certain of this, however.

Be that as it may, fact is, these calls cause lengthy waiting times for us and on client machines as well.

Attempts to deal with this

  • Omit !insertmacro UNINSTALL.LOG_OPEN_INSTALL and corresponding closing call. This will result in an empty Uninstall.dat, therefore the uninstaller becomes mostly useless.
  • Check if Uninstall.exe exists, if so assume a previous installation being present in target folder and omit !insertmacro UNINSTALL.LOG_OPEN_INSTALL and corresponding closing call. This works quite well, but will only collect all files once upon initial installation. Files that are added as part of updates, i.e. when users install over an existing installation, will no longer be recognized in the Uninstall.dat.
  • Attempt to edit Uninstall.dat during installation. Impossible, because apparently this file is read before installer sections and written after installer sections, so whatever I write to it will be wiped once the installer finishes, for my convenience.
  • Attempt to trigger an uninstallation before installing, using nsisExec. This is nonsense, because it will open a new uninstaller window above the installer window and take focus, as would be expected. This simply looks awful to the user, because suddenly there are two installation windows competing for their focus.
  • Possibly attempt to call uninstall before actual installation if a prior installation is detected. This, however, requires me to rewrite AdvUninstLog, because uninstallation is achieved using its macros and these are only valid for uninstall sections.

Questions

  1. Why does it take forever to use !insertmacro UNINSTALL.LOG_OPEN_INSTALL in the first place?
  2. What is the appropriate way to handle this problem?
  3. I tried using RMDir /r $path, yet this had the exact same effect as just RMDir without /r, i.e. the folder is being deleted, however only once empty. What is going on there?

There are threads describing these issues since 2004, here are some examples:

I am interested in finding a procedure to keep Uninstall.dat up-to-date, yet prevent absurdly long calls caused by the AdvUninstLog plugin. How can I achieve that?


Solution

  • I prefer the method used by another header (and the AdvUninstLog page also mentions this as an alternative): https://nsis.sourceforge.io/Uninstall_only_installed_files

    Like AdvUninstLog, an uninstall log is used to back out only the files and registry entries installed, though this header requires you to use macros that wrap your File, WriteRegStr, etc. calls.

    I have found it's a good way to allow a cancelled installation to roll back its changes in conjunction with this header: https://nsis.sourceforge.io/InstFiles_Cancel_-_Allowing_a_user_to_cancel_installation_during_InstFiles (though note that if you need to be able to roll back an update, it's a bit harder as you'd need to make a backup of the existing install first).

    This header has a lot of the same benefits of AdvUninstLog, but the benefit of AdvUninstLog over this one is that even if you're using wildcards with your File calls, your uninstall will only remove the files that were actually installed, but the cost is the potential for the slow performance that you've observed. In both cases you need to figure out how you want to deal with files added after the fact.

    Re: Questions #1 and #2, even the AdvUninstLog page mentions this drawback (though not why). As for question #3, it's unclear, but perhaps you were trying to do it before closing the Uninstall.dat. In a pinch, you could always use "RMDir /r /REBOOTOK" and allow Windows to finish the cleanup after rebooting.