Handling NSIS Uninstall.dat and the AdvUninstLog plugin - installation

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
Why does it take forever to use !insertmacro UNINSTALL.LOG_OPEN_INSTALL in the first place?
What is the appropriate way to handle this problem?
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:
http://forums.winamp.com/showthread.php?t=302976
https://gitlab.com/inkscape/inkscape/issues/300
https://nsis-dev.github.io/NSIS-Forums/html/t-356786.html
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?

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.

Related

Windows installer is too clever, tries to repair when tester deletes config file

Our application is deployed to the target machine with an msi file. All works nicely. Our tester has gone through his plan, and one of the tests requires deleting the application's configuration file. The application is designed to alert the user with a dialog on startup saying "missing config". However, what happens is that - somehow! - the software starts the installer again and retrieves the missing file from the msi! Which is nice, but not what we want. How do we disable that behaviour?
without going into much depth of the windows installer mechanics (if you interested in that there a plenty of articles about this), the shortcut of the software is probably advertised, which means the windows installer checks if everything is in its place before the software is started.
if you can edit the msi, make the shortcut non advertised.
if you can't, install it with DISABLEADVTSHORTCUTS
e.g. msiexec /i myMsi.msi DISABLEADVTSHORTCUTS=1
please note that this is only a quick (and dirty) workaround,
to fix this proper you need to understand the whole windows installer advertising (also called repair or self resiliency) mechanism.
but explaining all the causes and the mechanism of the repair is far beyond this answer and there are quite some articles and posts about that on the internet (and especially on MSDN and stackoverflow)
There is a more correct answer to this, and it is NOT DISABLEADVTSHORTCUTS. You set the component id to null in the MSI file to prevent repair of that individual file. See ComponentId comments here:
http://msdn.microsoft.com/en-us/library/aa368007(v=vs.85).aspx
Edit the MSI file with Orca to delete the Componenty ID, and write an uninstall custom action to delete the file at uninstall if it's there.
In addition, that's a redundant test. Windows will restore that file for you if it's missing, so the idea that you need a test to notify that it's missing is pointless. The true test should be that Windows will restore the file if it's lost, and your app needs to do potentially nothing about the missing file.
You don't mention what tool you are using to make your MSI but I'm going to go out on a limb and guess Visual Studio Deployment Projects (.VDRPOJ).
One of the (many) horrible things about this tool was that it fails to expose the foundational concept of components. Instead it makes every file a key file of it's own component and hides the existence of the component from you. I say 'was' because Microsoft killed this project type in VS. There are around 50k people complaining on UserVoice to bring this tool back and I'm guessing that 49,990 of them don't know what a key path is.
Windows Installer has a concept called the component rules and each component has a keypath. The keypath teaches MSI how to handle repair scenarios. But your tool has to allow you to be able to control this to make it work.
Windows Installer is functioning exactly the way it's supposed to function. You just aren't up to speed on what that is.
However, if you want to ignore Windows Installer best practices and continue using the tool you use today, the trick is to install the app.config file as a different file. Then have the application copy the file to the real file name on run. Windows Installer won't service what it didn't install.
Several answers have been provided that can work:
You can install the file with a blank guid. Then you need to remove it on uninstall using the RemoveFile feature. You will also run into issues if you want to replace it during an upgrade. Could be tricky at times.
You can disable the advertised shortcut(s), but this affects too much in my opinion.
Finally you can use my suggestion to install a separate non-advertised shortcut to use to launch the application. Such a shortcut bypasses the self-repair check. It may still be invoked by other means such as missing file associations, COM registration or similar, but those are exception states.
However, my preference is that an application can start without a config file present, if at all possible. I always suggest a good startup routine with "internal defaults" available. The startup routine should also degrade gracefully if faced with any file system access denied conditions.
Most importantly you should place this config file in the userprofile so you can generate the file on first launch for the user in question. It can even be copied from a read-only copy in the main installation directory.
When you generate a file from internal defaults and put it in a userprofile location, the file will have no interference with Windows Installer at all. The issues that results is how to clean up user data on uninstall. I discussed this with Stefan Kruger (MSI MVP) at one point, and I agree with his notion that user data is indeed user data and should not be automatically dealt with by your installer at all. Leave it installed, and clean it up via system administrator tools if necessary - for example logon scripts.

How to execute innosetup installer from third party silently and without it attempting to install dependencies?

I have an installer from a third party. Through trial and error I discovered it was an innosetup installer. When I call it with the /silent flag it installs just fine, until it executes installers for 3 dependencies (direct X is one, for example) which then require user input to cancel. I want to be able to run the installer and have it either install the dependencies silently or not at all. When going through the installer GUI normally it does give me 3 checkboxes at the end on the last page of the wizard (before I would hit the Finish button) that allows me to choose whether or not to install the dependencies. Is there a way of doing this that I don't know about? From my research it seems impossible without knowing the types and components available (and executing the installer with the /help or /? options had no effect) and I will probably need to get a new installer from the third party. The only other option I can think of would be to have some sort of timeout that after a certain period of time of inactivity from the installer I kill the install process (since the files I was interested in had already been installed at that point I think).
Checkboxes on the finish page sound like postinstall [Run] entries. There is no way to influence the selection of these from the command line, except that the original setup author can choose to have a different set of options selected for interactive install vs. silent install. (Or they might have extended the /LOADINF option to work with these, although this is unlikely.)
Given that this is a third-party installer, your best bet is to contact the original vendor and see if you can get them to change the default silent selection, or to add an additional command-line parameter to change the defaults.
Failing that, you could consider using a program such as AutoIt to auto-click the wizard GUI when run non-silently.
(If the things that it's trying to install really are dependencies, though, then you probably should let it install them. And it should be installing those silently too anyway.)
Killing the install process after a specified time seems like an excellent recipe for disaster.
Those are probably [Tasks] within InnoSetup's install, which you may be able to deselect by passing /tasks="" in the command line (along with the /silent). Here is a list of command line options: http://www.jrsoftware.org/ishelp/topic_setupcmdline.htm . Adding /suppressmsgboxes may help also.
It seems it is impossible to do what I wish without knowing more about the structure of their setup. I was however successful in solving my original problem by killing the third party installer after waiting a specified amount of time (which I got from reading this question).

How can I safely remove old InnoSetup installations?

I use InnoSetup 5.5.1 (a) for my Delphi 6 software installations. A user just informed me that the old uninstall entries pile up in the Control Panel Remove Programs list if not uninstalled manually. I'm thinking of changing my install to remove old entries automatically but I don't want to remove old entries that are valid. Some users like to keep an old version or two around in case they don't like a later version or for fear a later version will break something.
For clarity's sake, I am referring specifically to the typical situation where a user usually accepts the default installation directory, overwriting the existing version. However, with the exception that some users may install one or more versions to a different directory just to preserve them, while choosing to overwrite most of the time. I found this SO post on removing old versions:
InnoSetup: How to automatically uninstall previous installed version?
But did not see any mention on knowing how to detect which of the old versions are superfluous or not.
Therefore, during an install, how can I automatically remove old uninstall entries automatically from the installed program list without removing any that the user actually wants to keep?
As long as you keep your AppId the same between different versions of your application, there will only ever be one Add/Remove entry for it, no matter how many times the user runs the installer. This is the normal and recommended design for a typical application where the user only has one copy of it and wants to keep it up to date.
If, however, the user chooses alternate installation locations in one or more of the later installs, then it's possible for the older location to get "orphaned" -- running the uninstall will remove the newer copy but leave the older one behind, with no Add/Remove entry. (It will still be possible to uninstall it by running the uninstaller manually.) It's normally recommended to avoid this situation by including these options in your [Setup] section:
DisableDirPage=auto
DisableProgramGroupPage=auto
Using these options will make Inno skip asking these questions for an upgrade install, which helps to prevent the user accidentally making orphan copies. (If the user does intentionally want to move the installation, they can still do it by uninstalling first.)
Note that I've sidestepped your question a bit, since as written it doesn't make sense -- if there are multiple copies, there's no possible way to automatically determine which are "superfluous", since that's purely a judgement call on the part of the user. What I've tried to explain here is that your design should aim to discourage this happening accidentally.

TFS & Visual Studio integration - Get Latest does not always work?

I'm developing in Visual Studio 2005, using TFS as the source control. Whenever I haven't been working on the solution for a while, I always do a recursive Get Latest in Solution Explorer.
However, this doesn't always seem to work. If I know I don't have the latest version of a file, even right-clicking this (in Solution Explorer), choosing Get Specific Version and ticking the "Force get" box doesn't work.
I seem to need to open up the TFS Source Control window, and there force a Get of the file in question.
Also, the Solution Explorer often has the little "checked out to someone else" icon next to files, but when I check in Source Control, they're not checked out at all!
I'd just like to know if these problems are widespread, whether they persist in VS2008 (I haven't used TFS for a big project in 2008 yet), and if there are any fixes or workarounds.
1) I would not make a habit out of Get Latest from Solution Explorer. Even if it always worked 100% bug free, it is far slower and less reliable than doing it from the command line or Source Control Explorer. SlnExp has to crawl your whole project structure and issue non-recursive calls...pseudo algorithm:
parse sln file
foreach project in sln
TFS_GET makefile
parse makefile
enumerate sourcefiles[]
TFS_GET sourcefiles[]
loop
SCE requires no parsing and issues one single recursive webservice call. In addition to the performance gain, this is much safer:
(a) Build-time dependencies aren't always part of a project's file list. Executable tools, 3rd party assemblies, and deployment scripts are all common examples. SCE will download them, SlnExp won't.
(b) Scoping Get calls down to specific files won't yield the expected result when a file is renamed or moved. At best, the "old" name is deleted from disk; at worst, nothing appears to happen at all. (this may be the cause of the bug you reported) In order for a file to truly be renamed/moved in sync with the server, the old & new paths must both be inside the scope of the Get.
2) There have been many bug fixes to the SlnExp "glyphs" over the years. I won't claim that VS2008 SP1 is perfect in this regard but it is definitely improved.
Sometimes Get specific version even checking both checkboxes won't get you the latest file. Most commonly what happens is that you've made a change to a file, and you want to undo those changes by re-getting the latest version. Well... that's what Undo pending changes is for and not the purpose of Get specific version.
If in doubt:
undo pending check in on the file(s) before you do 'get latest'
do a compare afterwards to make sure your file matches the expected version
run a recursive 'compare' on your whole project afterwards to see what's different
keep an eye on pending changes window and sometimes you may need to check 'take server version' to resolve an incompatible pending change
And this one's my favorite that I just discovered :
check the Output window for messages such as this :
Warning - Unable to refresh R:\TFS-PROJECTS\www.example.com\ExampleMVC\Example MVC\Example MVC.csproj because you have a pending edit.
Yes this critical message appears in the output window. No other notifications!
Nothing in pending changes and no other dialog message telling you that the file you just requested explicitly was not retrieved! And yes - you resolve this by just running Undo pending changes and getting the file.

Uninstall error if original install DVD is not in drive

When trying to remove our application in Add/Remove Programs, the following error pops up, and the application fails to uninstall:
Error
'mFileBagIDE.dll' is not a valid short file name.
The curious thing is that you only get this error if the original installation DVD is not in the drive. If the DVD is in the drive, the uninstall works perfectly.
Here's the real kicker: we did not catch this bug until after our application was already widely deployed, and our clients' situations are such that it is likely many of them no longer have their original DVD. This means that the next version's installer (doing a windows installer major upgrade) will fail because it is unable to first remove the previous version.
So, my question is twofold:
What did we do to create this problem so we can avoid it in future releases?
Is there a way to tell our next windows installer to ignore this error and go ahead and remove the previous version?
Our current installer (the one that is causing problems) was generated using InstallAware. We're likely moving to WiX. But solutions in any platform (InstallAware, WiX, raw MSI tables) are appreciated!
UPDATE: I have the following row in both the InstallExecuteSequence and InstallUISequence tables in my MSI, which may very well be relevant, but I have no idea what the SRCDIREX property is, or where it is being set.
| Action | Condition |
|---------------|--------------|
| ResolveSource | NOT SRCDIREX |
Probably one of the actions (either standard or custom) that references the original MSI was not conditioned to run on installation only (for example, ResolveSource should be conditioned as "Not INSTALLED"). You might be able to workaround this with a patch (an MSP file) that changes the condition on the relevant action.
I would start by determining which action is causing the error. Here's how I would do that:
Install your app from the dvd
copy the msi file to some local folder, let's say "c:\temp"
Remove the dvd
kick off the uninstall like this: "msiexec /x yourapp.msi /L*v c:\temp\uninst.log"
When the error comes up, the uninstall is effectively paused. You can then check the end of the log to see exactly where you are in the sequence. That should help you to debug.
If the answer really is ResolveSource, regular patching may not be an option.
Heath Stewart mentions this in his blog -
http://blogs.msdn.com/heaths/archive/2007/10/25/resolvesource-requires-source.aspx
"In general, do not schedule ResolveSource. If this runs when installing a patch, for example, the user will have to insert the original media whether they would otherwise need to or not."
If that's the position you find yourself in, you could create a transform that updates the condition on your ResolveSource action, and apply that to the cached copy of the msi file manually. It's a bit of a pain, but I'm pretty sure that would work.
have you tried to copy those files to %WinDir%/system32 folder?
EDIT: Make a setup to copy all the setup MSI package to the disk, and install it from the diskdrive.
Remove every files uneeded to uninstaller. Adobe, HP and many other companys are doing that.

Resources