Monday, April 21, 2008

Can You Hitch in a Cab?

Sometimes when repackaging an msi for an enterprise it's necessary to add files to an installation using a transform. For example, you may wish to drop out a common file that has been captured by the original vendor with a custom component code, and install it using its proper merge module.

It would be great to be able to embed the cab into the transform, so that the transform is self-contained. Well there's good news, and bad news.

The good news is that you can embed the cab in the transform. The bad news is that the transform can't be used during installation. Which is pretty bad news.

So effectively, you can hitch in a cab, but its illegal. At least in the msi world.

Here's the low down. You can embed a cab in the transform as long as the cab's binary field is listed in a regular table entry. For example, it must be listed in the Binary table. If the cab is listed only in the _Streams table, it won't get saved into the transform.

If you generate a transform file with an embedded cab, and apply it to an msi in InstEd, you will successfully be able to extract the cab again, proving that the transform contains the cab file. In fact, if you apply the transform to an msi, and perform a Save Transformed command, the resultant msi will install fine, including any files from the added cab.

However, if you apply the transform with the embedded cab to the msi during an installation:
msiexec /i msi_file TRANSFORMS=mst_file
then you get an error related to the installation not being able to find the cab.

My guess is that this is related to this little snippet in the msdn docs for MsiDatabaseApplyTransform:
The MsiDatabaseApplyTransform function delays transforming tables until it is necessary. Any tables to be added or dropped are processed immediately. However, changes to the existing table are delayed until the table is loaded or the database is committed.

When installing an msi, it seems that the OLE structured storage streams are extracted "before" the table that references the cab is transformed. This is surmised from fact that the transform contains the stream for the cab (you can pull it out of the transform in InstEd), but it is not accessible during the installation process. So, a likely scenario is that the table that references the cab's binary data (and hence the underlying stream) is transformed after the streams are extracted.

Can you hitch in a cab and get away with it?
Could you force the table that references the cab to be transformed before the installation code extracts the streams? Well, I haven't tested it, and it would be unsupported, but you might be able to do so by adding a custom action to the transform that reads from the table that references the cab. This would force the table to be transformed.

If this custom action could be run early enough in the InstallExecuteSequence (or even the InstallUISequence) then perhaps the table would be transformed before the streams were extracted. But if it did work, it would be unsupported and could possibly break in future releases of msi.

Having said that, it would be nice if Microsoft did officially allow cabs to be embedded in transforms.

No comments: