Archive: Files get Deleted on Reboot after Uninstall / Install


Files get Deleted on Reboot after Uninstall / Install
If you use the /reboot option for deleting files on uninstall you can run into the following confusion.

1. Uninstall app, even though a DLL is in use to be deleted. (Uninstall instructions say delete the DLL when rebooted.
2. Reinstall the app.
3. Reboot the computer.
4. The DLL is deleted, even though a new install wants the DLL.

Is there an option to clear Delete on Reboot files?

Is the best way to work around this by renaming the file to delete and then deleting the renamed version?
Thanks.
P.S. I really love this product.


Renaming is also not possible when the file is in use.

You should write a key/file to prevent the installer from running when an uninstallation is pending.


Your message implies that I must require a reboot after uninstall to avoid this. I'm trying to avoid a mandatory reboot. I guess I could have a message that asks if the person wants to reboot before a reinstall.

BTW, The windows explorer let's you rename a dll while it is in use. I would assume your APIs would do the same. Are you saying they won't?


If you can rename it the DLL is not in use.

You can remove the registry key to delete the file on reboot, but it has a bit of a special format so it's not that easy.


So I tried my hack with the rename and it works fine. It will rename it even if it is in use.


Can someone help with plugin coding?
I tried to address the problem.

Idea: Remove those bad "Delete on reboot" entries in Windows just when installing the new file.

Approach: delete all "Delete/Rename after Reboot" entries in Windows (wininit.ini or Registry key) just before the File command (or before !insertmacro InstallLib). So never a file should be removed/renamed after next reboot and break the installation.

I'm not fit enough in pure C to finish the plugin. I just packed some code snippets together and wrote some comments, what to do. Maybe someone here can help to finish this.


What if DLL files that need to be deleted on reboot would be first moved to $TEMP with a temporary name? Files in use can be moved, but just can't be deleted. This will solve the reinstall issue and will also make sure unregistered DLL files don't stay on the system.


hi kichik,

that's a great idea and I think it will work. Do you think this should be and NSIS built-in feature?

I can see two issues we should take care of:

- This will only work if $TEMP is the same drive as the file to delete. (do not "rename" across drives (i.e. move))

- The file could be locked so it cannot be renamed.

Both issues are rare, but should be addressed. So if drive-internal renaming results in error the current method should be done.


GetTempFileName knows how to get a temporary file name on another folder than $TEMP, so the first problem can easily be solved. The second can be solved with a simple IfErrors, reverting back to the old method.

The change would be done in the Library macros, as usual.

I'd like to take some time and think about it, to make sure no problems should arise. Are there any other problem you can see but those two?


You're right. But GetTempFileName may fail. :-)

a) You take the root of the dll drive as temp base dir ("d:\" for "d:\program files\prog\test.dll"). Problem: This dir could be write-protected. Think of a network share, so this scenario is not so weird. Of course every file or dir could have this problem. But root drive write access is something I would not rely in practise.

b) You take the original dir as temp base dir ("d:\program files\prog\"). This is bad because an uninstaller would like to delete the "prog" dir if possible. At least directory cannot be removed in this session (on reboot, too). Hmm. "b)" sounds better to me.

What about RMDIR /Rebootok? I don't see any problems. RMDIR /r /rebootok seems to be quite dangerous in general but this should not be any problem here.


The problem with (b) is already present with the current solution, so it wouldn't be a bad start. To get to an even better solution, an attempt to move to $TEMP could be made before using $INSTDIR.

RMDir /REBOOTOK will not work on Windows 9x without an undocumented patch. On Windows NT it already works.


Sorry, the RMDIR issue has nothing to do with your DLL/library.nsh solution. So this was just an extra question for other use cases of your strategy (rename in case directory is in use).

To address your question: I think the solution will work, don't see any problems and it's a real improvement.

One issue not addressed by your solution is this:

Another installer (non-NSIS or NSIS <= 2.22) would not work like you suggest. So if someone installs multiple applications my original problem would occur anyway. Your solution tries to not create conflict entries in the delete-on-reboot-table but does not resolve existing conflicts.

Anyway I think we should implement your solution because delete-on-reboot-table is not intended to being written to.


I've made the change. Had to tweak it a bit to handle both network drives and files on drives different than $TEMP's drive, but it works now. The change is available in CVS. Let me know if there are any problems with it.

One weird thing that I had to deal with is MoveFile() that doesn't report failure when moving in-use files from a network drive to a physical drive. It copies the file, but never deletes the original and reports success. I had to use IfFileExists instead of IfErrors to check for success and also delete the destination file, even though the source file still exists.


Hi kichik and others,

two question for Library.nsh:

1. I want to use Library.nsh for all my files (not only runtim e dlls and typelibs) so I can use all those fine solutions for "in use" files without coding them again. Currently I use those macros with some workaround because InstallLib can only UPGRADE. For my application dlls (in "program files\...") I want to be able to downgrade. And I have some files that are no DLLs (just data files etc). So it would be very nice if I could disable the version comparing steps. I had a look into it but currently do not have a good feeling about just adding some jumps there. I don't want to break something...

Workaround for me is: just call uninstalllib before installlib so the file will be removed (this means: version check will be always positive). I assume files without version information will always be copied...

2. I have a DLL which is not very important for my application. It is a plugin DLL which is used by a third party software. If the 3rd party software runs, I now can make upgrades without stopping that software (very nice) by using the library.nsh and my macros. This will move the DLL in use to TEMP (still running) and will install the new one in INSTDIR. When the installer finishes it wants to reboot the machine. This isn't necessary here. The user can run the software without rebooting and for the new plugin DLL restarting the 3rd party software will do the trick.

I'm thinking about doing this for myself (special macro using "SetRebootFlag false") but maybe this could be done in Library.nsh, too. (There my be others who want something like this.)

Best regards
stb


Why not add some code :-)



; ################################################################
; installs a file (existing file will be removed)
; OUTFILE: target filename, LOCALFILE: source filename (local)
!macro InstallFile OUTFILE LOCALFILE
; USE ON YOUR OWN RISK! Please report bugs here: http://stefan.bertels.org/
!insertmacro DeleteFile "${OUTFILE}"
!insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "${LOCALFILE}" "${OUTFILE}" "$INSTDIR"
!macroend

; ################################################################
; installs a DLL that has to be registered (COM) (existing file will be removed)
; OUTFILE: target filename, LOCALFILE: source filename (local)
!macro InstallDLL OUTFILE LOCALFILE
; USE ON YOUR OWN RISK! Please report bugs here: http://stefan.bertels.org/
!insertmacro DeleteDLL "${OUTFILE}"
!insertmacro InstallLib REGDLL NOTSHARED REBOOT_NOTPROTECTED "${LOCALFILE}" "${OUTFILE}" "$INSTDIR"
!macroend

; ################################################################
; installs a DLL that has to be registered (COM) (existing file will be removed)
; this special variant does not change the reboot flag (will not be set here)
; OUTFILE: target filename, LOCALFILE: source filename (local)
!macro InstallDLLneverReboot OUTFILE LOCALFILE
; USE ON YOUR OWN RISK! Please report bugs here: http://stefan.bertels.org/
Push $0
StrCpy $0 "0" ; reboot flag false
IfRebootFlag 0 +2
StrCpy $0 "1" ; reboot flag true
!insertmacro DeleteFile "${OUTFILE}"
!insertmacro InstallLib REGDLL NOTSHARED REBOOT_NOTPROTECTED "${LOCALFILE}" "${OUTFILE}" "$INSTDIR"
StrCmp $0 "0" 0 +2 ; when reboot flag was not set before, we set it to false now
SetRebootFlag false
Pop $0
!macroend

; ################################################################
!macro DeleteFile FILE
; USE ON YOUR OWN RISK! Please report bugs here: http://stefan.bertels.org/
!insertmacro UnInstallLib DLL NOTSHARED REBOOT_NOTPROTECTED "${FILE}"
!macroend

; ################################################################
!macro DeleteDLL FILE
; USE ON YOUR OWN RISK! Please report bugs here: http://stefan.bertels.org/
!define LIBRARY_COM
!insertmacro UnInstallLib REGDLL NOTSHARED REBOOT_NOTPROTECTED "${FILE}"
!undef LIBRARY_COM
!macroend

Using Library for regular files can be achieved by ignoring version information. Probably another possible value for libtype should be added. Something like FILE.

For unimportant files, another option can be added (LIBRARY_IGNORE_REBOOT or something), or SetRebootFlag false can be called by the user. Seems to me it's more of a user thing. But if you can find a good setting name for it, I don't mind.

As always, patch or feature request ;)


What does LIBRARY_VERSION_NONE?

I this is some (external) option, I don't see a chance of line 276 and following to run. I couldn't find the position where this is defined within Library.nsh.


It's an internal constant used by the Library. LibraryLocal.exe defines it when no version information is found in the DLL or TLB.


@kichik: is line 521 (SetOverwrite lastused) dead code (library.nsh)?


No. It's coupled with line 507 or 511 to return the overwrite flag to its last used value. SetOverwrite is not affected by Return as it's really a compile time command.


patch for (1.) by pm