I want to replace a single file inside a msi. How to do it?
You need to extract the CAB file stream from your msi using MsiDB.exe (supplied with the Windows Installer SDK). Run it from the command line with the -x option and specify the name of the cab file - this is listed in the Media table in the msi database.
Alternatively you can skip this part if you specify the "Package Files as:" option in the VSI options to "Compresses in Cabinet Files" to have the cab file left out of the msi when it's built (it will be created in the same directory as the msi).
Once extracted you can change the specified file in the cab folder - its name has been mangled so you need to find out what msi name for the file is in the file table and then rename your new file to that.
Once done you can pop it back in with the MsiDB utility using the -a option.
Before you add with -a you need to use msidb -k to remove the cab from the MSI.
The easiest way to do it is to repackage MSI:
- Open MSI file in Wise for Windows Installer. Choose an option to to extract files.
- Locate the file on disk and replace it.
- Build MSI.
These steps should also work for InstallShield.
Very simple example code to replace a file inside an MSI. This does not stream the new file/CAB back into the MSI but requires the CAB to be in the same directory as the MSI for installation to succeed. I'm sure with a little more effort you could alter the code to stream the CAB back in.
Const MSI_SOURCE = "application.msi"
Const FILE_REPLACE = "config.xml"
Dim filesys, installer, database, view
Dim objFile, size, result, objCab
Set filesys=CreateObject("Scripting.FileSystemObject")
Set installer = CreateObject("WindowsInstaller.Installer")
Set database = installer.OpenDatabase (MSI_SOURCE, 1)
Set objFile = filesys.GetFile(FILE_REPLACE)
size = objFile.Size
objCab.CreateCab "config.cab", False, False, False
objCab.AddFile FILE_REPLACE, filesys.GetFileName(FILE_REPLACE)
objCab.CloseCab
Set view = database.OpenView ("SELECT LastSequence FROM Media WHERE DiskId = 1")
view.Execute
Set result = view.Fetch
seq = result.StringData(1) + 1 ' Sequence for new configuration file
Set view = database.OpenView ("INSERT INTO Media (DiskId, LastSequence, Cabinet) VALUES ('2', '" & seq & "', 'config.cab')")
view.Execute
Set view = database.OpenView ("UPDATE File SET FileSize = " & size & ", Sequence = " & seq & " WHERE File = '" & LCase(FILE_REPLACE) & "'")
view.Execute