Wix toolset build optimization for repeatable builds - wix

I don't know much about wix tools.
I have a project that needs to be packaged in many msi packages. The number of files is about 700. The difference between each msi package mainly in the architecture (x86, x64) and a small set of variable resources (no more than 5-10 files).
Full build takes more than one hour. Most of the time is taken by light.exe.
Is it possible to somehow cache the result of work in order to speed up this process, since each msi differs by a minimum set of files? Maybe I should organize the files in a special way to make this possible?

Computer and Disk: The obvious first: are you working of a fast disk on a good computer? Or are you pulling files from the network or writing to slow disks? 700 files is not that much. Are they very big? How many setups are you building? A good SSD or NVME disk should help - also some good CPU behind it.
Build Speed: I have this existing answer on build optimization: Speed up Build-Process of WiX-Installer - I would recommend you build a shared setup and install via a WiX Burn setup.exe bundle (Burn builds setup.exe executables that can install embedded MSI files or other setup-type files). A separate MSI that you build seldomly can work. Thoughts on setup cohesion and coupling.
WiX Help File: How To: Optimize build speed
Cab Cache & wixlibs: Please read this:
Reusing WIX components to speed up candle/light
What are .wixlibs and why would you use them?
Links:
Is there a way to speed up WiX builds?
How can I speed up MSI package install and uninstall?

You can define a single cabinet file for your shared files and individual cabinet files for unique sets of files. Use conditional compilation using defines to include only select cabinets in the MSI file.
<Media Id="1" Cabinet="Shared.cab" CompressionLevel="high" VolumeLabel="Shared binaries." EmbedCab="no" />
<Media Id="2" Cabinet="Set1.cab" CompressionLevel="high" VolumeLabel="Unique files 1" EmbedCab="no" />
<Media Id="3" Cabinet="Set2.cab" CompressionLevel="high" VolumeLabel="Unique files 2" EmbedCab="no" />
On the <File> element set the DiskId="" attribute to associate a single file with target cabinet.
Light should be able to use existing cabinets from cache when input files does not change between you different builds.

Related

WiX Toolset bundle with total content size > 2GB

Is it possible to make a WiX installation bundle with size of total .msi's and exe's inside bundle more than 2GB? Currently in my .wxs template I have a <chain> inside of a <bundle> that contains multiple <MsiPackage>'es. I haven't created that WiX template on my own, I'm just improving/fixing bugs in a legacy project, so I am not very proficient with the WiX toolset. Anyway, then I create a .wixobj out of that .wxs template with candle.exe and this .wixobj I feed to light.exe. It seems that if total size of content to link is more than 2GB light.exe fails with error:
error LGHT0306 : An error (E_FAIL) was returned while finalizing a CAB
file. This most commonly happens when creating a CAB file with more
than 65535 files in it. Either reduce the number of files in your
installation package or split your installation package's files into
more than one CAB file using the Media element.
I looked for solution and one suggestion was to use <MediaElement> but that one is only available in a <product> tag which I guess I cannot use in my situation, because the bundle that I am cooking has a custom bootstrapper embedded, and as far as I understood wix product template is not about bootstrapper applications. Another opinion that I found was that you can't create WiX bundle with size of single package more than 2GBs, but that is not exactly my case, because each package I am bundling is no more than 700MBs.
I have already tried separating <MsiPackage>s into <PackageGroup>'s in separate <Fragment>'s which was referenced in <Bundle>'s <chain> in <PackageGroupRef>, but it didn't help, light.exe throws the same error. I have also found a suggestion to try to put packages into separate <Container>s in <bundle>, but I didn't manage to try it because I didn't understand how to do it, WiX bundle documentation just mentions that the are some containers but never actually gave an advice on how to use them.
I am using WiX 3.11
UPD: I just wanted to add some clarifications.
My goal is to make a bundle what would be a single .exe file with all related .cab's and other stuff embedded into the final bundle, so delivering bundle that consist of .exe bootstrapper and multiple external .cab file isn't really a preferred option even though it might be x times faster unless this is the only solution in my case.
UPD 2:
It seems that yet you can assemble a bundle with 2 GBs + size, it won't start. Maybe because the output executable is x86, and windows can't launch x86 executables over 2gbs size. I didn't manage to make an x64 bundle, maybe that would work, but I never found how to do it. Guess the only option is to let users download missing content and make bundle only contain essential parts. Thanks everyone for help.
Compressed bundles cannot be bigger than 2GB. Burn supports containers up to 2GB each and only one container can be attached to the bundle .exe itself.
Bob's answer is still correct for v3.
Support for multiple attached containers was recently added in v4 (#6144). That allows a single file bundle to be larger than 2GB. v4 also added support for x64 (and ARM64) bundles, which allows the bundle to be larger than the x86 maximum.
A related open feature request is #6521 where WiX would automatically split payloads across containers so that the user doesn't have to manually create new containers to avoid the cabinet limitations for size or number of files.

How can I compare the content of two (or more) MSI files?

How can I do a "content compare" of two (or more) MSI files and see what is actually different inside the files - instead of doing a useless binary compare? (which obviously only tells me if I am dealing with copies of the same file or not).
Some relevant and typical problem scenarios:
Our build system spits out MSI files like crazy, and sometimes we need to figure out what differences exist between different MSI files (read: something changed, and now we are failing deployment).
We have MSI files compiled from the same sources in different locations, and some of them fail to run reporting System.BadImageFormatException - how can we debug what the differences in the MSI files are? (an answer dealing with this error specifically here: Are applications dependent on the environment where it was compiled?).
MSI files can be compiled with all kinds of tools, but for stackoverflow users such files are probably most commonly created using WiX or Visual Studio Installer Projects (free toolkits).
This is a Q/A-style question on the topic of comparing your compiled MSI files to determine what real "content differences" exist.
Microsoft Orca: If you have Visual Studio installed, try searching for Orca-x86_en-us.msi - under Program Files (x86) - and install it. Then find Orca in the start menu.
Current path: C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86
Change version numbers as appropriate
About MSI Files
Roughly speaking MSI files are COM-structured storage files - essentially a file system within a file - with streams of different content, one of which is a stripped down SQL Server database (in the most generic of terms I believe). Provided the MSI files are readable, the content (of various formats and types) can be compared.
Tech Note: It is conceivable that an MSI which triggers a System.BadImageFormatException is just not runnable (msiexec.exe can't run it), but it may still be readable - and hence comparable (please add a comment to verify this if you experience it).
Streams: Some streams in the MSI are tables with string values. There may also be streams for embedded cab-archives used to store files to deploy, and tables with binary content only - such as the Binary table where compiled and uncompiled custom actions alike are stored along with other binary content the setup needs. And there is a special "summary stream" and a table with icons stored in their native, binary format, and the list goes on... For most of the tables we can compare the strings in each table pretty much like we compare text in a Word document (which also used to be OLE / COM files - though newer versions now use Open Office XML) and get a detailed report of differences. In order to do this, you obviously need a special-purpose tool for the job - one capable of finding its way though all the relevant streams. Some commercial and free tools for this are listed below.
Binary content: Before elaborating this, I should note that comparing content in the Binary Table, Cabs Table, Icon Table - or any other binary table, will generally allow you a binary compare only (particularly for compiled custom action dll and exe files). Script custom actions - in the binary table - can be compared as text, but compiled custom actions are binary compare only. So if your problem emanates from a compiled custom action, you can't really see it in a direct compare (you see the binary difference only). You need to hit your source control system to see what code was used for compiled custom actions of any kind - hopefully you have a good label practice so you can find the actual source code used in each setup. I don't use this practice, but for internal, corporate releases perhaps you can even include your debug-build dll for your compiled custom action, and attempt to attach the debugger to the running code to really figure out what is going on? I wouldn't use a debug mode dll for a public release though - unless I'd clarified any risks. Debug code may be riddled with (unexpected) debug message boxes (used as entry points to attach the debugger) and other problems that should never hit a production package.
Come to think of it, your cab files and icon files can definitely be compared to their corresponding versions in older (or newer) MSI files by using the technique to decompile MSI files using dark.exe - which is described below. Then, using a good compare tool (Beyond Compare is mentioned below), you can do a full diff on the cab file content between different MSI versions (and some of the files could be text files, that could be text compared). I guess cabs and icons are sort of "transparent binaries" in an open format as opposed to compiled binaries (with custom actions and more) which are not inherently decompilable or inspectable (unless you know how to decompile managed binaries).
In conclusion: MSI files are fully transparent with the exception of compiled custom actions. This transparency is one of the core benefits of MSI. Most Windows Installer benefits, over previous deployment technologies, generally center around corporate deployment benefits. Unfortunately developers may only see the bad aspects of MSI: the (potential) MSI anti-patterns (towards bottom - very messy and ad-hoc for now). Admittedly some of these problems are very serious and violate "the principle of least astonishment". Developers - why have other and equally important things to do - may frankly be left scratching their heads in disbelief.
Leave no mistake about it though: MSI has massive corporate deployment benefits (see same link as above, towards bottom). Condensed: reliable silent running, remote management, rollback, logging, implicit uninstall feature, elevated rights, standardized command line, transparency, transforms for standardized setup customization and admin install to reliably extract files. Just to name the big ones quickly. Benefits in list form here.
A lot of digressions so far - let's get to the point. What tools can be used to compare MSI files?
Commercial Tools
Several commercial deployment tools such as Installshield, Advanced Installer and many other MSI tools have support for viewing and comparing MSI files. Maybe I add too many links, but let me use my usual policy of "if you link to one, you link to everyone" - it should save some time and some Google searches.
As a special note - a nostalgic one - the best MSI-diff feature I ever saw was in Wise Package Studio. It was head and shoulders above the rest to be honest - always working, neatly color coded and just easy to comprehend. This tool is no longer for sale as described here: What installation product to use? InstallShield, WiX, Wise, Advanced Installer, etc (if you have a packaging team in your corporation, maybe they have a spare license laying around?).
Free Tools
The commercial tools are good, but there are also several free alternatives that can be used to compare MSI files - and below is a list of some of them along with some hints for how to use each tool (in a rather minimalistic way).
There are some more details added for dark.exe - which is not a comparison tool for COM-structured storage files at all, but a way to decompile MSI files to WiX XML source files and extract all support files (icons, binaries, cabs, setup files) - allowing them to be compared with regular text / binary compare tools afterwards.
1. Orca (MSI SDK)
Microsoft's own MSI SDK tool / viewer called Orca can view MSI files and edit them, but there is no direct support for comparing two MSI files (that I know about). I suppose you could export the tables and then compare them, but other tools have more built-in features. This option is mentioned since you may already have Orca installed and then this is probably a quick way to get a simple diff done. The "poor man's option".
You may already have the installer. If you have Visual Studio installed, try searching for Orca-x86_en-us.msi - under Program Files (x86) - and install it. Then find Orca in the start menu. Technically Orca is installed as part of the Windows SDK (large, but free download). If you don't have Visual Studio installed, perhaps you know someone who does? Just have them search for this MSI and send you (it is a tiny half mb file) - should take them seconds. If not, you can always download the Windows SDK
2. Super Orca (free third party tool)
Super Orca will allow a rudimentary compare of two MSI files. My smoke test seems to reveal that advanced fields such as the Summary Stream may be ignored. In other words a straight table compare only. There could be other limitations. Maybe it is good enough? It is easy to use.
Note: I have not been able to verify for sure, but I believe this tool saved my MSI without warning once. That was very undesirable at the time.
3. widiffdb.vbs (MSI SDK)
The MSI SDK has a VBScript you can use to view differences between two MSI files. It is called widiffdb.vbs (msdn). With this tool I can see the Summary Stream differences ignored by Super Orca. Anything MSI SDK is authoritative.
UPDATE: All MSI SDK API scripts on github.com (the actual VBScript code).
Throwing in a link to the full list of such MSI SDK VBScripts - for various purposes. Don't be confused, only widiffdb.vbs is needed for comparing MSI files, but there are many useful scripts for other purposes to be found.
If you have Visual Studio installed, just search for widiffdb.vbs. Launch with cscript.exe and pass in full path to two MSI files to compare them. Output in console.
Usage:
cscript.exe widiffdb.vbs "Setup 1.msi" "Setup 2.msi"
Sample Output:
Property Value [ALLUSERS] {1}->{2}
Property Value [MSIINSTALLPERUSER] {}->{1}
Property INSERT [MSIINSTALLPERUSER]
\005SummaryInformation [9] {{00000000-0000-0000-0000-000000000000}}->{{00000000-0000-0000-0000-000000000001}}
\005SummaryInformation [12] {28.03.2019 15:20:02}->{28.03.2019 14:40:52}
\005SummaryInformation [13] {28.03.2019 15:20:02}->{28.03.2019 14:40:52}
\005SummaryInformation [15] {2}->{10}
To find the script, you can search for it under Program Files (x86) if you have Visual Studio installed (it is part of the Windows SDK which will also be installed along with Visual Studio) - (currently the path is: C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86 - just replace the version numbers as appropriately and you should find the MSI quicker).
4. dark.exe (WiX toolkit - open source)
The dark.exe binary from the WiX toolset (a toolkit which was likely used to compile some of your MSI files). This dark.exe is a "disassembler" or "decompiler" for MSI files. It will convert MSI files to wxs XML format (WiX's own format used to compile MSI files in the first place), along with a number of extracted binary files (if you use the correct decompile options and flags).
The wxs source files can be compared as regular text source files (my favorite tool for this is Beyond Compare, but it is a commercial tool - there are plenty of text compare tools - including those in Visual Studio). The binary files can obviously be binary compared. Any extracted CAB file can be compared to another, similar cab file from another MSI setup version for example.
Here is a sample command line:
dark.exe -x outputfolder MySetup.msi
In many cases this will yield a very good compare of the MSI files and you should be able to determine what is really different.
The extracted binaries could be script files (VBScripts, etc...) or any number of other binaries (for example compiled DLL custom actions). In the latter case you can't really decompile it further - unless it is a .NET binary and you have expertise in decompiling such binaries.
However, it should be noted that WiX-generated MSI files compiled using the exact same WiX source files can be different for a couple of reasons:
The same WiX source file can also be compiled with different compiler and linker settings, and this can affect the generated MSI file in several different ways. To see all switches, download and install WiX and just write candle.exe or light.exe into a command prompt and hit enter.
Certain fields such as package GUIDs and product GUIDs can be set to auto-generate in the wxs file. The resultant, corresponding field in the generated MSI file will obviously be different for every build in this case.
I don't have a full list of what fields can be set to auto-generate at this point (if you know, maybe hit edit and modify this in situ).
The mentioned auto-generated fields can also be hard-coded (which is not good for the package GUID, but that is another, long story - just know that if you find two MSI files that are binary different with the same package GUID, then you are in serious trouble - if they are in the wild - Windows Installer will treat them as the same file by definition). Package codes should always be auto-generated. Digression.
The MSI files themselves obviously have different file date information having been compiled separately - just to state the obvious.
And a special note somewhat unrelated to the topic at hand, but important nonetheless: you can use dark.exe to decompile executables compiled with WiX's Burn feature. This is WiX's bootstrapper feature used to install one or more MSI and / or EXE files in sequence - one after the other. These bootstrappers are EXE files and you can decompress them into their constituent MSI and/or EXE files:
dark.exe -x outputfolder setup.exe
Just open a command prompt, CD to the folder where the setup.exe resides. Then specify the above command. Concrete sample: dark.exe -x outputfolder MySetup.exe. The output folder will contain a couple of sub-folders containing both extracted MSI and EXE files and manifests and resource file for the Burn GUI. Any MSI files can then be disassembled as described above to produce a WiX source file (wxs).
5. InstEd (free third party tool - with plus version available)
For some reason I have never used this tool actively, but tested it several times. Testing it again it does seem to do the job of comparing two MSI files, albeit from a strange menu option (which made me think the feature did not work before).
Open an MSI, then go to Transform => Compare Against... and browse to the MSI you want to compare the first one to.
Comparison seems OK, and I see that there are changes in the Summary Stream - for example - but the diff doesn't seem to show what is different (unless I just don't see it).
To see the summary stream changes, open both files in separate InstEd instances and go Tables => Summary Info... in both instances. Now compare the information in the property sheets. Alternatively use the widiffdb.vbs script listed above.
6. Other Tools... (COM-structured storage file viewers)
There are no doubt many other tools capable of viewing COM-structured storage files, but I think the above options should suffice for most users. I'll add a link to installsite.org's list of MSI tools again.
7. Advanced Installer (commercial tool with some free features)
This commercial tool will be able to function as a viewer and allow some basic operations on MSI files even without running with a full license. The nice bit is that you don't even need to use the raw tables, but can use a much nicer user interface to "hotfix" various things in the MSI. For example various upgrade parameters (continue or fail when major upgrade uninstalls fail, etc...).
Changes made in the Table Editor view (straight-up table view) will not be visible in the other "wizard views". The reason for this is explained here.
Links
The same (similar) information written for system administrators.

Attempting to remove compression from an MSI causes its files to disappear

I'm trying to build an MSP patch for a program that's currently deployed using an MSI package. I've been able to successfully generate a PCP file using the following (slightly-redacted) WiX code, based on an example from the WiX documentation.
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<PatchCreation
Id="D25F1136-2BEE-4A82-9236-7261067D4BDC"
CleanWorkingFolder="yes"
OutputPath="XYZ.pcp"
WholeFilesOnly="yes"
>
<PatchInformation />
<PatchMetadata
AllowRemoval="no"
Classification="Major Upgrade"
Description="XYZ Update"
DisplayName="XYZ Update"
ManufacturerName="XYZ"
MoreInfoURL="http://www.contoso.com/qr/XYZ.html"
TargetProductName="$(var.ProductName)"
/>
<Family Name="PrsnPtch">
<UpgradeImage Id="LatestImage" SourceFile="new_msi_dir\XYZ.msi">
<TargetImage Id="OldImage" Order="1" SourceFile="old_msi_dir\XYZ.msi" />
</UpgradeImage>
</Family>
<PatchSequence PatchFamily="XyzPatch" Supersede="yes" />
</PatchCreation>
</Wix>
and a build script set up to call msimsp.exe.
msimsp -s "%PCPDIR%\XYZ.pcp" -p "%PCPDIR%\XYZ.msp" -l "%PCPDIR%\patch.log"
Unfortunately, the above command fails with the following error message:
ERROR: UpgradedImages.MsiPath '[...]\XYZ.msi' is marked as having
compressed files (PID_WORDCOUNT property of Summary Information
stream). PatchWiz is unable to patch files compressed in a cabinet.
So I tried building an uncompressed version of the MSI by adding a CompressionLevel attribute to the MediaTemplate element in its WXS file.
<MediaTemplate EmbedCab="yes" CompressionLevel="none" />
For building the MSI itself, this is a success. The resulting file is 3.7 times the size of the uncompressed original, and 7-Zip shows the files therein as having a compression method of “None”. And I am able to successfully install the program on a VM.
The problem is that then I still get the “PatchWiz is unable to patch files compressed in a cabinet” error when running msimsp.
The culprit seems to be the Compressed attribute on the WXS Package element:
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine"
Description="XYZ Installer"/>
So naturally, I tried changing Compressed="yes" to Compressed="no" (with no other changes to this WXS file). And this “works” as far as allowing msimsp to not give any errors.
But it introduces the serious problem that the MSI file is “empty”, its size having been reduced from about 3.6 MB to a mere 280 KB. Viewing the archive in 7-Zip shows that it does not contain any of the files that it's supposed to install. Viewing it in Orca shows a Media table with one row containing the values DiskId=1 and LastSequence=19, but nothing in the Cabinet column (the original MSI had an auto-generated “#cab1.cab” here).
Why would changing the Compression attribute make the CAB file go missing? How do I fix it?
This patch creation process does not work by using MSI files with embedded cabs or external cabs. It uses administrative images that you create using msiexec /a ..... This creates an MSI file together with all the external loose files in their appropriate directories. The patch creation process uses two of these admin images to generate the patch, the msp file, and it compares each corresponding file in each image to create the delta between the two products.
An administrative install is not an "install" - it's mainly just file extraction from the MSI into loose files in directories. This also marks the resultant MSI file as having "no files" in it - it's just the tables - so the patch creation process will complain if it finds this or anything indicating the files are in a cab. It needs separate loose files.
Once you have two administrative images the patch creation process (using a PCP file) runs against the two images comparing each file for changes and generating an actual patch, an msp file.
(Note that there is an IgnoreMissingSrcFiles setting in the PCP TargetImages table that allows missing files to be ignored while the patch is being created - it would make no sense to have this setting if the patch creation process used CAB files because all the files would always be there. )

TFS2008 - packaging in MSI and cab file for installer project

I am using automated build and installer project to create msi file in TFS 2008. There I need to create a big folder structure having thousands of file and having size of hundreds of MB on user machine when user installs using MSI file. Using TFS installer project, if I add individual file then my installer.vdproj becomes really big and unmaintainable.
So is there a good way to handle these bunch of files as single unit? I can think of compression mechanism for now, which has two parts: -
How to compress the files into a cab file(in MSBulid process may be)? It seems the makecab command only takes one file at a time?
How to modify installer project so that cab file is part of MSI and on user's machine the cab file is uncompressed to create the folder structure? can I have cab file as well as MSI (so that cab file is not part of MSI) ? rest of the dlls go into MSI.
Thanks
Tools from the Windows Install SDK such as MSIFILER.EXE can be easily scripted to load a whole directory of files (for example) into the MSI's file store or the CAB.
In your case, you would want to configure the script to be run as a post-build event, so that first you build the setup project's MSI then the script modifies it.
You are correct that makecab.exe only takes one file at a time. You can use a CMD shell FOR-loop to add them one-by-one (e.g. FOR /F %%file IN ('dir /b /s') DO makecab %%file output.cab). Personally, I prefer archive tools like 7-zip which are also scriptable from the command line.
There are several methods to add the files to the Windows Installer package (e.g. WiImport.vbs, MAKEMSI, or manually)...
..but maybe you don't want to do that. I think it might be better to just include the archive (zip, cab, whatever) and create a custom action in the setup project to extract it during the install.

Can a .msi file install itself (presumably via a Custom Action)?

I wand to construct an MSI which, in its installation process, will deploy itself along with its contained Files/Components, to the TargetDir.
So MyApp.msi contains MyApp.exe and MyAppBootstrapperEmpty.exe (with no resources) in its File Table.
The user launches a MyAppBootstrapperPackaged.exe (containing MyApp.msi as a resource, obtained from the internet somewhere, or email or otherwise). MyAppBootStrapperPackaged.exe extracts MyApp.msi to a temp folder and executes it via msiexec.exe.
After the msiexec.exe process completes, I want MyApp.msi, MyBootstrapperEmpty.exe (AND MyApp.exe in %ProgramFiles%\MyApp folder so MyApp.exe can be assured access to MyApp.msi when it runs (for creating the below-mentioned packaged content).
MyAppBootstrapper*.exe could try and copy MyApp.msi to %ProgramFiles%\MyApp folder, but would need elevation to do so, and would not allow for its removal via Windows Installer uninstall process (from Add/Remove Programs or otherwise), which should be preserved.
Obviously (I think it's obvious - am I wrong?) I can't include the MSI as a file in my Media/CAB (chicken and egg scenario), so I believe it would have to be done via a Custom Action before the install process, adding the original MSI to the MSI DB's Media/CAB and the appropriate entry in the File table on the fly. Can this be done and if so how?
Think of a content distribution model where content files are only ever to be distributed together with the App. Content is produced by the end user via the App at run time, and packaged into a distributable EXE which includes both the App and the content.
MyApp's installer must remain an MSI, but may be executed by a Bootstrapper EXE. The installed MyApp.exe must have access to both MyApp.msi and EXE is to be "assembled" at runtime by the App from a base (empty) MyAppBootstrapper.exe, which is also installed by the MSI, and the content created by the end-user. The EXE's resource MSI must be the same as that used to install the App which is doing the runtime packaging.
WIX is not to be installed with MyApp.
There can be no network dependencies at run-/packaging- time (i.e. can't do the packaging via a Webservice - must be done locally).
I am familiar with (and using) Custom Actions (managed and unmanaged, via DTF and otherwise).
Add an uncompressed medium to your wxs like this:
<Media Id='2'/>
And then create a component with a File element like this:
<File Source='/path/to/myinstaller.msi' Compressed='no' DiskId='2' />
This will make the installer look for a file called "myinstaller.msi" on the installation medium, in the same folder as the msi that is being installed. The source path above should point to a dummy file, it is only there to appease wix.
Edit: The following sample test.wxs demonstrates that it works. It produces a test.msi file which installs itself to c:\program files\test. Note that you need to put a dummy test.msi file in the same folder as text.wxs to appease wix.
<?xml version='1.0' encoding='utf-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product
Name='ProductName'
Id='*'
Language='1033'
Version='0.0.1'
Manufacturer='ManufacturerName' >
<Package
Keywords='Installer'
Description='Installer which installs itself'
Manufacturer='ManufactererName'
InstallerVersion='100'
Languages='1033'
Compressed='yes'
SummaryCodepage='1252'/>
<Media Id='1' Cabinet='test.cab' EmbedCab='yes'/>
<Media Id='2' />
<Directory Id='TARGETDIR' Name="SourceDir">
<Directory Id='ProgramFilesFolder'>
<Directory Id='TestFolder' Name='Test' >
<Component Id="InstallMyself">
<File Source="./test.msi" Compressed="no" DiskId="2" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature
Id='Complete'
Display='expand'
Level='1'
Title='Copy msi file to program files folder'
Description='Test'>
<ComponentRef Id="InstallMyself" />
</Feature>
</Product>
</Wix>
Having one .MSI package launch another .MSI package from "within" itself is called a nested install, and it's bad juju (see Rule 20). Windows Installer has some global data that it uses to manage the current install, and it doesn't handle well multiple installs at the same time. For the same reason, if you start one install and then try to start another while the first is still in progress, you'll usually see a pop-up to the effect of "another install in progress, please wait until it's done".
You can have a program, usually called a bootstrapper (I think that's what you're referring to) which is itself not an install package, but which contains an install package (such as an .MSI or an .EXE) as a resource, possibly compressed. The action of the bootstrapper program is to extract/expand the resource to a file, commonly in a %TEMP% directory, then either launch the extracted .EXE or run MSIEXEC on the extracted .MSI. The bootstrapper can contain multiple resources and extract+install them one by one, if you need to install prerequisites before the main package. Or you can ship multiple packages as separate files, and have the bootstrapper execute/install them directly from the distribution media one by one, or copy them down to the target machine and run the series of install from there, or...
WiX itself does not get installed, no. It's a tool with which .MSI packages can be built. The WiX project has on its wishlist a generic bootstrapper program, but it hasn't been implemented yet. There are other bootstrappers available, e.g. this one.
You won't need a custom action -- in fact, since the bootstrapper isn't itself a Windows Installer installation package, "custom action" has no meaning to it. And, if you're familiar enough with CAs to know about managed/unmanaged/DTF, then you know enough to avoid custom actions whenever you can. (grin)
I think it's much easier for your bootstrapper to extract MSI file to some predefined location rather than to the temp folder. For example, to C:\Documents and Settings\All Users\Application Data\My Company\My Product Install Cache. After installation finishes bootstrapper would leave MSI file sitting there. If at some stage user decides to reinstall your product Windows Installer will be able to locate source MSI file.
Also, add path to this file to RemoveFile table so that it gets deleted on uninstall. You can use RemoveFile element in WiX for that.
So if I understand, then I think I would have the app create a transform (MST) that has the content files and apply that to the base MSI. I'm still not convinced that I understand though. :)
I'd configure the MSI cache path to a known location.
Then at runtime if you need to "edit" the MSI use VBScript or similar.
But still, I ask WHY!?!
I am also working on a way to deploy multiple MSI files. I have a bootstrapper.exe program that bundles the MSI files and runs them one at a time. This solves my problem for most cases.
The case it does not solve is GPO (Global Policy Object) distribution of the install. GPO requires a dot-msi file to run an install.
To do this here's what I did which almost solved the problem (but not quite). I put the dot-msi files in the file table of an installer and put my bootstrapper in the binary table and run it from a custom action inserted after InstallFinalize in the InstallExecuteSequence. Of course the bootstrapper won't be able to run other MSI's because the top level MSI holds the _MSIExecute mutex.
It was pretty easy to get a little further. I made the bootstrapper return control to the top level installer and continute. And then I added a WaitForSingleObject call to wait for the top level install to finish, and the bootstrapper can then continue to finish the install.
My problem is that the GPO installation happens at boot time and the top level install completes before the sub installers are done and GPO reboots the machine.
The toplevel install also returns a success status when the install may actually fail later on.
I'm still looking for a way to block the top level install from completing until after the bootstrapper completes.