Wix Toolset: How to restore replaced file on unins

2019-09-15 01:25发布

问题:

I have two products - product A and product B.

Product A contains file A.dll with version 1.0.0. Or any other type of file (.config with text 1.0.0). Product B updates file A.dll with version 2.0.0 (or config with text 2.0.0).

After uninstallation of Product B, it removes A.dll. If I mark A.dll in product B as "rermanent" it will not be removed.

But I need to restore A.dll version 1.0.0 after product B has been uninstalled.

As a possible workaround, I can run Product A in "restore" installation mode.

Is there any other solution?

P.S: Case example: Product A has been released, and due to some bugs, required hot-fixes come next as a Product B (to have additional entry in Programs and Features list). The Product B (hot-fix) replaces A.dll with newer one. I want to support uninstallation of Product B (hot-fix) and return to state before it is installed (A.dll with version 1.0.0).

回答1:

If these are separate products, why do you install one on top of the other? Is this a UAT / QA construct for temporary use? Could you install these products side-by-side (in separate folders for each product) and then just pull back whichever version you don't need anymore? Your scenario of pulling back bits and pieces of your "new" install that is mixed in with your original install does not work well with Windows Installer.

Is this a released product or are you still in development? Why not just pull back what is on the box and install a new version of your setup which cleans up everything. You could author the upgrade table to uninstall both existing products you have already deployed. For safety reasons i would change the path you install to for the new version of your setup, and I would uninstall the existing packages early in the InstallExecuteSequence.

The overall message: you are not supposed to mix different products in the same installation location. Each product is supposed to have its own installation folder, and if you have several setups installing to the same location the files should be installed with the same component guids.

See if this makes some sense to you: Change my component GUID in wix?



回答2:

If you're really talking about data files (such as config files) that you want to restore when Product B is uninstalled, I think we're looking at a custom action when A is installed which backs up the data used by A. When B is uninstalled it restores the data from the backup location.

Either way, the issue seems to be the application design where A and B both apparently share some common set of data files, except that they only share when both are installed.



回答3:

NOTE: this answer was written before the OP modified his question.


In my opinion this indicates an error in application design. What you are dealing with is a shared file that is required in two different versions - or so it seems? Can product A use version 2.0.0 of the dll or does it absolutely require version 1.0.0?

Short answer:

  1. Either install the dll that you have shared now as a private dll in the main application's installation folder (for each product, in the version they require), or...

  2. Make sure both products can use the most updated dll in the shared location (shared deployment requiring full backwards compatibility).

  3. You can also install the two dll versions side-by-side (in the GAC for .NET assemblies, or in WinSxS for Win32 files) or somewhere else on disk where both applications can access them (they load the one they need - and leave the other version alone on uninstall). Which version loads depends on the application manifest (some find messing up the manifest to be the second coming of dll-hell - the wrong stuff is loaded).

  4. Finally you can statically link the dll's into your main executable (my least preferred option for large corporation deployment - details below). Static linking in .NET is a little bit different from native, Win32 binaries: Static Linking of libraries created on C# .NET. I am not sure of the performance implications of static .NET linking (I only ever do Win32 static linking).

As a rule of thumb shared files must be rather stable with few updates to really be useful as shared. They are typically "libraries". I might chose to deploy private copies for a while during development until I reach a more stable state and switch to shared files (side-by-side or global, whichever fits).

If you are dealing with COM, then the technology's overall "global registration nature" makes it necessary to maintain backwards compatibility (files are always shared) or you can try "registration-less COM" - essentially COM with the registry information embedded in manifest files and local copies of all files in the main application installation directory - everything happens in one folder in an "isolated" fashion. COM is old, but since it is used for .NET via COM Interop it will continue to be a headache for everyone for years to come.


Below is a more elaborate discussion of these options. It got a little messy - like it gets when you just write off the top of your head. I will try to clean it up as soon as I get some distance to it and can see clearer what is wrong with it.


If product A can use version 2.0.0, then you have a basic shared file between applications, and the normal way to install it would be a merge module included by both setups. You should have backwards compatibility so that product A can use any version of the file, including version 2.0.0. Product B should also be able to use any version of the shared dll, and you should be able to deploy an upgrade to the shared dll that both products can handle. A genuinely shared file. During product installation higher versions of the file should always be installed, and all products using the file should be able to handle the new version (backwards compatibility). Great if you have security hot-fixes that you want to deploy to many products quickly and reliably without rebuilding all products individually. This is obviously what shared files are for.

If product A can't use version 2.0.0 of the dll, then you likely have a side-by-side installation scenario (like the global assembly cache - different versions of the same assembly installed at the same time and applications load the one they need). You should install both versions of the dll in different places (with different file names), and your product A and product B should load the one they need. Or you should install to the GAC - this is what the GAC is for (.NET assemblies only). There is also a side-by-side concept for normal Win32 files (Windows side-by-side assemblies). You must never mess around in the WinSxS folder directly (the Windows assembly folder for Win32 dlls), see this excellent explanation from Advanced Istaller - just like for the GAC files are registered for installation to WinSxS). When you uninstall either product A or product B the dll needed by the other application is left untouched (there could also be other products using them). Side by side files can also be shared between products, but they won't be uninstalled if there are other products registered that need them on your product's uninstall and there are several different versions of the file to choose from so you crucially do not need to maintain backwards compatibility between the different "branches" (backwards compatibility must still be maintained for each branch - i.e version 1.0.1 must be compatible with 1.0.0 but not 2.0.0).

The most obvious option might be to include your now shared DLL in your product's main application folder - making it a "private file" that is never attempted shared between other applications / products. This works best for Win32 dll's or .NET assemblies (no registry impact or global registration - unless the .NET assembly is registered for COM Interop). COM files are registered system-wide in the registry and should be installed to a shared location with genuine backwards compatibility or delivered in a brand new versions side-by-side with the old one (with new interfaces - rarely done). .NET assemblies can be installed side-by-side in the GAC if you need side-by-side deployment, or you can deployed them into the main application's directory as "private copies" of the assembly.

So for files used by many products you have shared files, shared side by side files, installing private copies of the dll in the application's main installation folder and a final option is static linking - compiling the dll's into your product's main executable. Just mentioning this since some people prefer this option. It does not allow deployment of updates to the shared files (or your private copy of the file for that matter), but any updates to the libraries require a full recompile and redeployment of both products (or however many products we are talking about) - a lot of work. People may find this option appealing at first, but tire of it quickly. It all depends how many updates will be made to your application and the shared components (now statically linked). We repeat that too often: it all depends, but it does.

It all depends :-) on how you have designed your application and how much you want to deploy files that are shared between products and that can be updated on their own. The great benefit of deploying a private copy of your dll next to your main application exe (instead of static linking) is that you can deliver an updated dll without recompiling your whole application exe (which could be huge). You minimize the risk of new bugs being introduce in other parts of the application, and you deliver "a little hotfix" of your product, and not a full blown recompile with all the QA work that entails. You have a targeted dll-fix and a new setup to UAT / QA (if you do it well, that setup can be delivered as a patch as well).

What I like about (genuinely) shared files is that I can deliver them with a separate setup that can be updated on its own, without affecting your main product's setup at all (when I say genuinely I mean files that are used by dozens of applications, not just a couple). This decouples things nicely and all that is needed is some QA / UAT of the main application after the shared files have been "hot-fixed". There is no full recompile of your main product AND a new setup for it to QA / UAT. If you deliver many large products this is very important. You could face some excruciatingly important security hot-fixes for your shared files that are used by dozens of other applications. I see this a lot in large corporations, for example banks - you could spend weeks or months updating the same problem in dozens of products otherwise. A nightmare for everyone.

I should add, and this is a personal opinion, that I don't really like merge modules that much. It is an OK concept, but for all intents and purposes it amounts to static linking for your setup. Not quite, but almost - all files are included at compile time is what I mean. The good news, however, is that an upgraded version of the merge module can be included in another setup that will upgrade the same files as long as you have higher version files in your upgraded merge module (in your new setup). So it is not quite "static linking", but it sort of feels like it.

My personal preference: do use shared files if they really help keeping lots of products updated and hot-fixed. In most cases these shared files are very stable with few(er) updates, and because of this work well as shared (you will thank yourself at some point - when something really hits security-wise). Deploy the shared files via their own setup and set that setup as a prerequisite for your own product's setup. This is what has proved most flexible and feature complete for me - especially in large corporations (for in-house software). You can chain it all together with a bootstrapper like WiX's Burn (in corporations you would use a deployment system like SCCM instead to set up dependencies), and you can deliver updates separately for your shared files and your application files. If your application is simple and your shared files are not too stable, just deploy a private copy of the dll(s) in your main installation directory (unless it is a COM file). I would avoid too much static linking (I only prefer static linking for minimal dependency scenarios - like when you make a custom action dll for your WiX setup and you want it to run on any machines without dependencies).

Good deployment depends on good development decisions. It is all cohesion and coupling. There are only rules of thumbs and no way that is right for everyone. It will be a lot of work no matter what you do, but try to avoid small changes affecting too many applications requiring a full development / deployment release cycle for all of them - it is the overall goal of shared files.