Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
585 views
in Technique[技术] by (71.8m points)

windows installer - How to express a file dependency using WiX

I have a couple of MSI files installing different applications. All these packages share the same underlying runtime which basically is a set of DLLs. This runtime is pulled in each of the installers as a merge module. Installing a couple of these packages works just fine, always the latest version of the runtime stays on the system and when the last package is removed everything is removed from the system.

Now I had to split one of the DLLs into 2 and added a new component to the runtime installing the new DLL. This new DLL is linked with other libs of the runtime. Now assume the following scenario:

  • install an old package with the merge module for the runtime without the new DLL
  • install a new different package with a newer version of the merge module for the runtime. Now there are 2 packages on the system
  • remove the new package again

Now the old package is broken because:

  • the new component for the new DLL had a reference count of one as the old package didn't have it and therefore gets removed
  • the other runtime DLLs stay on the system because they are still referenced by the older package. However as they are new they are already linked with the new DLL that is now no longer present

So my question is:

  • Is there either a way to explicitly state in the WiX code that file A depends on file B so that it stays on the system until all references have been uninstalled?
  • or is there a way to explicitly downgrade the dependees in a way the dependency does not longer exists?
  • Am I doing something fundamentally wrong?

What I did try on a clean machine was to follow Stein ?smul suggestion like this:

<Component Id='OldLibsNowDependingOnNewLib' Guid='C8DCD2AB-CBE5-4853-9B25-9D6FE1F678DD'>
  <File Id='LibOne' Name='LibOne.dll' Source='$(var.SourceDir)/LibOne.dll' />
  <File Id='LibTwo' Name='LibTwo.dll' Source='$(var.SourceDir)/LibTwo.dll' />
</Component>
<Component Id='NewLibComponent' Guid='CD2DB93D-1952-4788-A537-CE5FFDE5F0C8' Shared='yes'>
  <File Id='LibNew' Name='LibNew.dll' Source='$(var.SourceDir)/LibNew.dll' />
</Component>

However unfortunately this doesn't change the behaviour.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

UPDATE: Looking in the SDK again, I see the flag msidbComponentAttributesShared for components. This looks promising for the problem you describe. Please try to enable this flag and recompile the version 2 of your setup (unless it is live).

Enable the Shared flag for the component in question (last part):

<Component Feature="Product" Shared="yes">

This seems to be for patch support, but maybe it will work for your case too. From the MSI SDK:

"If a component is marked with this attribute value in at least one package installed on the system, the installer treats the component as marked in all packages. If a package that shares the marked component is uninstalled, Windows Installer 4.5 can continue to share the highest version of the component on the system, even if that highest version was installed by the package that is being uninstalled."


I think the above should work, but don't have time to test right now. Leaving the below for review.


Short Answer: Use WiX's Burn (setup chainer) to install in sequence the application setup and a new, separate runtime setup that can be handled independently of your application setup versions.


Prerequisite Setup: Interesting case. This is why I like to split prerequisites into its own MSI package and deploy it via a Burn Bundle Bootstrapper. Burn is WiX's bootstrapper / downloader / chainer - essentially a way to run several setups in sequence - in a few different formats such as MSI, EXE, MSU, MSP. When doing this - putting the runtime in its own MSI - there are no entanglements and you get good decoupling of your runtime and application-specific files. In other words: you can update the runtime files on its own - with their own MSI. The files will even have a reference count of 1 meaning you can easily uninstall them all (not if you install via a merge module that also can be included in other packages - more below).

Merge Modules - Semi-Static Linking?: In a weird way merge modules are sort of semi-static linking. The whole merge module is a version - a binary bundle (think COM) - but its installation behavior is one of "higher version wins" only. Hence a single newer MSI with the newest merge module in it will update the shared files for all applications that use them. Uninstalling will then do what you see: preserve the files that were originally installed by older setups.

Options: One "solution" in your case could be to re-compile the older setup with the newer merge module and then reistall, which I understand you don't like. I don't like it either. I guess it is no solution at all. Some other suggestions:

  • Permanent component: You can set the hosting component for the new file to be permanent on the system. This is very easy, but also quite silly and not always very desirable. Uninstall will then not remove the file at all.
  • Prerequisite: This is my favorite option mentioned above. You compile a prerequisite MSI setup that installs the runtime components. This MSI can deliver updates to itself without affecting the main application. This is the primary benefit I am after: Cohesion & Coupling benefits.
    • Merge Modules: I would avoid merge modules altogether, but it is common to merge the same merge module into the prerequisite setup - if you have a merge module already.
      • In most cases merging a merge module is fine since you then install the prerequisite and then you can install and uninstall application versions at will without affecting the runtime since a different product (prerequisite MSI) installed the runtime - and that setup should stay behind and not be uninstalled.
      • If the merge module does not work and brings along the conflict that you already had, maybe try to combine with the msidbComponentAttributesShared "solution" mentioned above. Not tested by me so far. Always risky to suggest things like this, but it is "best effort".
    • WiX Include Files: I prefer to use WiX include files which allows me to pull in new files without re-authoring a whole merge module in binary format (think C++ include files as opposed to a merge module's COM-style binary reuse).
  • Side-By-Side: Many people prefer to install prerequisites side-by-side so that several versions of the runtime can co-exist. This may or may not involve the GAC. Switching runtime versions would then be a manifest-manipulation task. Generally somewhat confusing, but doable. You can use both merge modules and separate MSI files to deploy such runtimes - as described above. I would definitely use a prerequisite MSI.

I can't think of more right now, but I know I have forgotten something important this time. Let me persist what I have for now in case it sparks ideas for you.

Cumbersome Prerequisite Setups: Note that prerequisite MSI files are not so bad for corporate deployment since deployment systems will allow one to define relationships between MSI files and to set up deployment chains. For home users you can easily wrap everything in a large setup.exe.

Nonsense Options: Options that don't make sense would be to roll the new file into both setup versions. No gain, lots of overhead. Some people like to copy new files locally to the main installation folder. Does not work since the files it is linked to are likely elsewhere (runtime location). Static linking wouldn't be relevant in this case I think. Only as a last resort to solve a live problem I guess. Setting the SharedDllRefCounter flag will not affect MSI reference counting, it is for legacy reference counting (non-MSI setups), though tweaking this manually is an emergency "solution". The last resort people end up with is typically to abandon the runtime installation and install everything to the same installation folder. Then you have to always recompile everything for every release - which is what you want to avoid?


Some Links:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...