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

6

9

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).

Coricoriaceous answered 17/9, 2008 at 21:52 Comment(4)
What is the specific business need you are addressing with this design?Exfoliation
Seems like you desire to make a simple scenario quite complicated. When I understand your question correctly you only care about distributing a single installer, so why not use some self-extracting installation file like it can be created with WinZip or IExpress (which comes with Windows for free)?Speculation
Because we want to participate as a good Windows citizen application and allow uninstallation via the normal "Add/Remove Programs" ("Programs and Features" in Vista) mechanism. And distributing a single installer is not the only we care about. We are installing the application, which is multiple files and settings, but we also need to have the installer which installed that application available to the installed application itself.Coricoriaceous
Please don't hack this, it will cause you more problems than you realize. MSI files are designed to be run one at a time. The WIX team has been working on a way to sequence the installation of several MSI files for some time, I am actually not sure of the current status, but it is worth checking it out. A large corporation might ban your install completely if it starts nesting installs and do other things that could make your install unservicable with patches and updates. I am not kidding, I have worked in large companies with deployment, and it kills products all the time.Crossfade
B
9

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>
Bacchae answered 18/2, 2009 at 0:52 Comment(6)
I like this idea and hope to try it out. Thanks!Coricoriaceous
I copied this exactly, created a text file, renamed it to test.msi put it next to the wxs file, built and it did indeed put out a test.msi file, but it was the 0kb text file that I renamed to test.msi. I don't see how this is helpful, it is just copying the test.msi file from the stage location. Is there a way to make a true copy of itself and have the msi save that to the output path as the same msi that you executed?Bucher
@Scott: I just retested and the installed file is actually the MSI itself for me, not the original dummy file. I can only reproduce the behavior that you describe by removing the "diskid=2" and "compressed=no" attributes on the file element, which then causes the dummy file to become embedded. Are you building with the command line tools like I am?Bacchae
I wouldn't do this if you need to patch your MSI at some point.Crossfade
@WimCoenen Does the dummy file need to be an MSI (have an msi extension) or can it be any file, for example source.wxs?Trepang
@Tom: it needs to have exactly the same name as the MSI you are going to create.Bacchae
H
2

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)

Hyo answered 18/9, 2008 at 4:42 Comment(3)
I don't understand why it should be bad juju. Microsoft itself does this. The SQL Server 2005/2008 installers, for example, are actually made up of multiple MSI files, which are executed depending on which features you choose under custom installation.Erbium
Jon, the SQL Server install is driven by an external program that installs each MSI in a sequence (aka: chain). They do not have one MSI calling another MSI (aka: nested). Nested installs are deprecated.Demolition
This is not what I'm talking about - I am not "launching" or running this other MSI at all. This "other" MSI is simply the original/outer/whatever MSI that is being run.Coricoriaceous
J
2

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.

Jipijapa answered 19/9, 2008 at 23:1 Comment(1)
This is my current methodology, but I am looking to at some point try wcoenen's suggestion above.Coricoriaceous
U
1

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. :)

Unkempt answered 17/9, 2008 at 22:31 Comment(4)
Transforms sounded promising, but it appears they are generated via WIX, Orca, or perhaps the Windows SDK - none of which I want to require on the user's machine with the App.Coricoriaceous
If the user can run the MSI in the first place then the API to generate a Transform is already installed. It is part of the Windows Installer.Unkempt
Still, to apply the transform the original MSI must be available, which is not guaranteed unless it is in a system-protected location such as %ProgramFiles%. How to get it there from the original location from where it was executed (probably a temp folder) & maintain uninstall support for removal?Coricoriaceous
You can create MSI's and MST via VBScript which should be an acceptable dependency.Elsi
E
0

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!?!

Elsi answered 18/11, 2008 at 23:18 Comment(0)
S
0

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.

Skit answered 8/5, 2009 at 15:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.