I've run into what appears to be a bug/design flaw in windows installer.
This occurs when an MSI file is configured so that it will automatically
uninstall a previously installed older version before running the install of
the new version, and the MSI file contains a custom action that is to be run
in managed code in one of the assemblies that is to be updated to a newer
version.
I added diagnostics to the custom action so that it printed out to a trace
listener the version number of the executing assembly. I then built two
versions of the MSI file, each time changing the version number of the
assembly and the MSI file.
I installed the older version and then ran the install of the newer version.
When running the install of the newer version it correctly detected the
older version and 1st uninstalled it - when it ran the custom action during
the uninstall the version number of the old assembly was printed out. It
then ran the install of the newer version. When it ran the custom action in
what was supposed to be the new assembly it printed out the version number
of the old assembly (that was supposedly uninstalled).
The bits on disk were correct but the wrong assembly was executing code!
It appears that windows installer creates a single appdomain and executes
all custom actions from within the default appdomain. It appears that it
never recycles the appdomain between the time the uninstall completes and
before the install begins - if so, then this is a major problem.
The problem is that once an assembly is loaded into an appdomain it stays
loaded and cannot be unloaded from that appdomain. Once the original
assembly is loaded, even if a newer version of it is on disk the fusion
layer will ignore it and continue to run the version that has been loaded.
The assembly was not signed with a strong name, but I would expect this
behavior even if it was signed. The result is that code in the original
assembly is executing when that assembly should have been deleted.
It may be that windows installer expects shadow copying to permit this (I'm
guessing that's what it uses) but if so, this is a faulty assumption.
If the installer wants to execute only once the code that hosts the CLR,
then it should run all custom actions in managed code from a second
appdomain, and then unload that appdomain after the uninstall and reload it
for the install. That is the only way to unload all the assemblies that have
been loaded without unloading the entire CLR.
So...I'm wondering if my analysis is correct, and if anyone else has run
into this. And I'm also wondering if there's a workaround other then forcing
users to manually uninstall the old version before installing the new
version.
Thanks
David Levine