Archive: Uninstall progress bar doesn't show progress


Uninstall progress bar doesn't show progress
  Basically, the uninstall sits at 0 until it's done, then jumps to 100%

# Uninstaller sections
Section /o -un.Main UNSEC0000
RmDir /r /REBOOTOK $INSTDIR
DeleteRegValue SHCTX "${REGKEY}\Components" Main
SectionEnd


Animaether suggested in this post that it's because of too few code statements in my uninstall section, which would line up with the code I've posted above. So, assuming that the one line rmdir that I'm currently doing is what I want to do, how do I expand that so that the progress bar will work properly?

You should never use RmDir /r $INSTDIR. First of all, in the uninstaller $INSTDIR may not contain the value you expect (see the manual entry on $INSTDIR). Second, if a user installs to for example Program Files, you'll end up deleting the entire Program Files directory. Third, if a user puts some of his own files in $INSTDIR, he would not expect you to delete them without asking for confirmation first. He will, instead, expect the uninstaller to only delete what it installed (it's called an uninstaller, after all).

What you should do instead, is delete only the files you KNOW you want to delete:

Section /o -un.Main UNSEC0000
Delete /REBOOTOK "$INSTDIR\YourFile1.ext"
Delete /REBOOTOK "$INSTDIR\YourFile2.ext"
Delete /REBOOTOK "$INSTDIR\YourFile3.ext"
...etc
RmDir /REBOOTOK $INSTDIR
DeleteRegValue SHCTX "${REGKEY}\Components" Main
SectionEnd


Incidentally, the above approach will also solve your 0%->100% problem, because the progress bar is updated after every command.


Thanks for the quick reply. That all makes sense. Couple of thoughts
1. The un.Main code was auto-generated by EclipseNSIS (I know not officially part of NSIS), so perhaps I'll need to report an issue to them.
2. The code that I use to add files is "File /r /path/to/files/*", so I don't explicitly package individual files.

It seems like I need to do this http://nsis.sourceforge.net/Uninstal...nstalled_files
And it seems like this should be the default behavior, but I'm an NSIS newbie, so I don't claim to know/understand the reasoning behind the design.

The other issue that I've noticed is that if you install X directories down, then the uninstaller leaves X empty directories laying around.


Originally posted by oryan_dunn
It seems like I need to do this http://nsis.sourceforge.net/Uninstal...nstalled_files
Yes, that would be a good way to solve this issue. (Though I've never used those macros myself - I always make every File and Delete command explicit.)


Originally posted by oryan_dunn
The other issue that I've noticed is that if you install X directories down, then the uninstaller leaves X empty directories laying around.
For this, you can simply remove each directory in sequence:

RmDir "$INSTDIR\subdir1\subdir2\subdir3"
RmDir "$INSTDIR\subdir1\subdir2"
RmDir "$INSTDIR\subdir1"
RmDir "$INSTDIR"


By the way, I don't use /REBOOTOK when removing directories, because if the directory wont remove, it's probable that there are some user files left over that aren't going to be removed anyway. Although I suppose you could put two groups of RmDir commands, one with and one without /rebootok, and put them under an if rebootflag==1.

Originally posted by MSG
For this, you can simply remove each directory in sequence:

RmDir "$INSTDIR\subdir1\subdir2\subdir3"
RmDir "$INSTDIR\subdir1\subdir2"
RmDir "$INSTDIR\subdir1"
RmDir "$INSTDIR"


By the way, I don't use /REBOOTOK when removing directories, because if the directory wont remove, it's probable that there are some user files left over that aren't going to be removed anyway. Although I suppose you could put two groups of RmDir commands, one with and one without /rebootok, and put them under an if rebootflag==1.
The /REBOOTOK bits were generated by the EclipseNSIS wizard, so perhaps another issue to report.

As far as the empty directories, I may not know how far down, ie. when the user picks an install directory, they pick $ProgramFiles\A\B\C, so $INSTDIR equals that, but now I uninstall, and I end up with a directory A that contains an empty directory B. I guess the installer could walk the tree calling RMDIR on each parent until it fails? What's the best way to handle this?

if the user chooses to install into some new subfolder structure (A\B\C\) and you install files into the leaf node C, you should only uninstall your files and subfolders of C - you wouldn't touch A or B.

If you install files into another subfolder, e.g. A\B\C\YourApp\*.*, you would uninstall the files and folders within YourApp, and the folder YourApp itself. You wouldn't be touching A, B or C.

If the user thus ends up with empty folders.. that's their problem.. if they chose to do this, they probably had a reason. E.g. if your program is a networking program, they might be installing to new folders \networkstuff\YourApp\, to subsequently plan to install \networkstuff\OtherApp\. If you remove \networkstuff\, even if empty, the user will probably be annoyed :)


Originally posted by Animaether
if the user chooses to install into some new subfolder structure (A\B\C\) and you install files into the leaf node C, you should only uninstall your files and subfolders of C - you wouldn't touch A or B.
Technically you're right, but if it's, say, D:\Games\YourGameApp, and you're the only game in that dir, then I for one would be glad to see the Games dir disappear upon uninstall. Matter of preference, I guess. And it doesn't go against the "only remove files you know you want to delete" principle, because you're the one who created the Games dir in the first place.

Anyway, there's some function somewhere that gets the parent of a directory, but I forgot what it was called. I've always used RmDir $INSTDIR\.. , that works in almost all cases but it doesn't win a beauty contest.

${GetParent} in FileFunc.nsh :)

As for the rest.. matter of healthy debate.. both have their merits, I suppose.

Push comes to shove, you can always ask the user what to do :D


So the reason for the delete empty dir was not that the user may pick some nested dir, it's that the default install dir is something like $ProgramFiles\companyname\programname, and we have several programs, that all would get installed in companyname, so if you uninstall the last program, no need for the companyname dir anymore. Same goes for start menu folder structure.

I guess I could look at the parent and if it is companyname, remove it, but that seems kinda fragile.


might seem fragile, but sounds like that's exactly what you should be doing in the scenario you describe :)

It's not that difficult anyway.

StrCpy$0 "c:\program files\myCompany\myFolder" /* installation path */
${GetParent} $0 $R0 /* $R0 now contains "c:\program files\myCompany" (sans quotes) */
${GetFileName} $R0 $R1 /* $R1 now contains "myCompany" (sans quotes) */
${If} "$R1" == "myCompany"
${DirState} "$R0" $R2 /* get empty/non-empty/non-existent */
${If} $R2 == 0
RMDir "$R0"
${EndIf}
${EndIf}
( GetFileName is really more akin to a 'get leaf', as it's not really specific to getting the filename part of a path )

All of those are in FileFunc.nsh

So I've run into an issue with using Uninstall only installed files macros. In my code, I use "File /r ${FILE_LOCATION}" where file location is a folder with all the files I need to install. The File macro doesn't support recursively adding files. Would there be an easy way to modify the File macro to handle recursion?

I'd love to explicitly list the files, but the output of our build produces different files and different file names, so I have to use recursion.

Edit:
I think I'd need a function like GetContents for a directory, and would need to check if it's a file, call the file macro, if it's a directory, move into the directory, and repeat. I didn't see anything in FileFunc.nsh that would be helpful.

Edit 2:
I got around the issue of nuking the install directory by always installing into a subdir, but that doesn't solve the progressbar and really is only a stop gap solution so that people don't install into $ProgramFiles and wonder what happened when then uninstalled my app.

I used to have


InstallDir "${COMPANY}\$(^Name)"
SetOutPath $INSTDIR

and

RmDir /r /REBOOTOK $INSTDIR


and now it's

InstallDir "${COMPANY}"
SetOutPath $INSTDIR\$(^Name)

and

StrCpy $0 "$INSTDIR\$(^Name)" /* installation path */
${GetParent} $0 $R0 /* $R0 now contains "c:\program files\myCompany" (sans quotes) */
${GetFileName} $R0 $R1 /* $R1 now contains "myCompany" (sans quotes) */
RmDir /r /REBOOTOK $INSTDIR\$(^Name)
${If} "$R1" == "${COMPANY}"
${DirState} "$R0" $R2 /* get empty/non-empty/non-existent */
${If} $R2 == 0
RMDir "$R0"
${EndIf}
${EndIf}

Perhaps..
http://nsis.sourceforge.net/Advanced...og_NSIS_Header
..is more appropriate for your situation?


Wow, I'll give that a shot. Looks like exactly what I'd want.