Tuesday 23 August 2011

NSIS installer and locked library files

Installer tries to replace some existing library file with a new one (with the same name) but file is locked, some process is using it at the moment and installer brings up nasty dialog:

MyApp Setup
Error opening file for writing:
C:\Program Files\MyApp\a.dll
Click Abort to stop the installation, Retry to try again, or Ignore to skip this file.
Abort Retry Ignore

The solution is to recognize that file is locked, store a copy of the new file in some temporary directory, ask user to reboot machine at the end of installation, and move file to the intended location upon reboot.

Simplified code that tries to place some file to its intended path on a target machine could be like this:

SetOutPath "$SMPROGRAMS\MyApp"
SetOverwrite on
File "lib\a.dll"

If a.dll is locked, we get the dialog above. The solution is to use InstallLibmacro from a Library header which sets installer error flag if copy error occurs. Additionally, provided argument tells this macro whether to set reboot flag or not. At the end of installation, we check the state of the reboot flag and if it is set, we notify user that reboot is required:

${If} ${Installed} == "True"
!insertmacro InstallLib DLL $ALREADY_INSTALLED REBOOT_NOTPROTECTED "lib\a.dll" "$SMPROGRAMS\MyApp\a.dll" $TEMP
Function .onInstSuccess
   WriteRegStr HKLM "${REGKEY}" 'Version' "${PRODUCT_VERSION}"

   IfRebootFlag 0 NoReboot
      MessageBox MB_YESNO|MB_ICONEXCLAMATION "A reboot is required to finish the installation. Do you wish to reboot now?" IDNO NoReboot

NOTE 1: If some DLL is locked and you want to see which process has loaded it, use Process Explorer. Go to Find->Find handle or DLL... and type DLL's name...

NOTE 2: To test installer with locked files, use Easy File Locker, a simple GUI tool which sets Visible/Accessable/Writable/Deletable attributes on file or folder.

No comments: