If an installation package was built from many merge modules, this
/// method can somewhat decrease package size, complexity, and installation time.
/// This method will also convert a package with all or mostly uncompressed
/// files into a package where all files are compressed.
/// If the package contains any not-yet-applied binary file patches (for
/// example, a package generated by a call to ) then
/// this method will apply the patches before compressing the updated files.
/// This method edits the database summary information and the File, Media
/// and Patch tables as necessary to maintain a valid installation package.
/// The cabinet compression level used during re-cabbing can be configured with the
/// property.
///
public void Consolidate(string mediaCabinet)
{
this.LogMessage("Consolidating package");
Directory.CreateDirectory(this.TempDirectory);
this.LogMessage("Extracting/preparing files");
this.ProcessFilesByMediaDisk(null,
new ProcessFilesOnOneMediaDiskHandler(this.PrepareOneMediaDiskForConsolidation));
this.LogMessage("Applying any file patches");
ApplyFilePatchesForConsolidation();
this.LogMessage("Clearing PatchPackage, Patch, MsiPatchHeaders tables");
if (this.Tables.Contains("PatchPackage"))
{
this.Execute("DELETE FROM `PatchPackage` WHERE `PatchId` <> ''");
}
if (this.Tables.Contains("Patch"))
{
this.Execute("DELETE FROM `Patch` WHERE `File_` <> ''");
}
if (this.Tables.Contains("MsiPatchHeaders"))
{
this.Execute("DELETE FROM `MsiPatchHeaders` WHERE `StreamRef` <> ''");
}
this.LogMessage("Resequencing files");
ArrayList files = new ArrayList();
using(View fileView = this.OpenView("SELECT `File`, `Attributes`, `Sequence` " +
"FROM `File` ORDER BY `Sequence`"))
{
fileView.Execute();
foreach (Record fileRec in fileView) using(fileRec)
{
files.Add(fileRec[1]);
int fileAttributes = fileRec.GetInteger(2);
fileAttributes &= ~(int) (WixToolset.Dtf.WindowsInstaller.FileAttributes.Compressed
| WixToolset.Dtf.WindowsInstaller.FileAttributes.NonCompressed | WixToolset.Dtf.WindowsInstaller.FileAttributes.PatchAdded);
fileRec[2] = fileAttributes;
fileRec[3] = files.Count;
fileView.Update(fileRec);
}
}
bool internalCab = false;
if(mediaCabinet.StartsWith("#", StringComparison.Ordinal))
{
internalCab = true;
mediaCabinet = mediaCabinet.Substring(1);
}
this.LogMessage("Cabbing files");
string[] fileKeys = (string[]) files.ToArray(typeof(string));
string cabPath = Path.Combine(internalCab ? this.TempDirectory
: this.WorkingDirectory, mediaCabinet);
this.cabName = mediaCabinet;
this.cabMsg = "compress {0}\\{1}";
new CabInfo(cabPath).PackFiles(this.TempDirectory, fileKeys,
fileKeys, this.CompressionLevel, this.CabinetProgress);
this.DeleteEmbeddedCabs();
if(internalCab)
{
this.LogMessage("Inserting cab stream into MSI");
Record cabRec = new Record(1);
cabRec.SetStream(1, cabPath);
this.Execute("INSERT INTO `_Streams` (`Name`, `Data`) VALUES ('" + mediaCabinet + "', ?)", cabRec);
}
this.LogMessage("Inserting cab media record into MSI");
this.Execute("DELETE FROM `Media` WHERE `DiskId` <> 0");
this.Execute("INSERT INTO `Media` (`DiskId`, `LastSequence`, `Cabinet`) " +
"VALUES (1, " + files.Count + ", '" + (internalCab ? "#" : "") + mediaCabinet + "')");
this.LogMessage("Setting compressed flag on package summary info");
this.SummaryInfo.WordCount = this.SummaryInfo.WordCount | 2;
this.SummaryInfo.Persist();
}
private void DeleteEmbeddedCabs()
{
using (View view = this.OpenView("SELECT `Cabinet` FROM `Media` WHERE `Cabinet` <> ''"))
{
view.Execute();
foreach (Record rec in view) using(rec)
{
string cab = rec.GetString(1);
if(cab.StartsWith("#", StringComparison.Ordinal))
{
cab = cab.Substring(1);
this.LogMessage("Deleting embedded cab stream: {0}", cab);
this.Execute("DELETE FROM `_Streams` WHERE `Name` = '{0}'", cab);
}
}
}
}
private void PrepareOneMediaDiskForConsolidation(string mediaCab,
InstallPathMap compressedFileMap, InstallPathMap uncompressedFileMap)
{
if(compressedFileMap.Count > 0)
{
string cabFile = null;
if(mediaCab.StartsWith("#", StringComparison.Ordinal))
{
mediaCab = mediaCab.Substring(1);
using (View streamView = this.OpenView("SELECT `Name`, `Data` FROM `_Streams` " +
"WHERE `Name` = '{0}'", mediaCab))
{
streamView.Execute();
Record streamRec = streamView.Fetch();
if(streamRec == null)
{
this.LogMessage("Stream not found: {0}", mediaCab);
throw new InstallerException("Stream not found: " + mediaCab);
}
using(streamRec)
{
this.LogMessage("extract cab {0}", mediaCab);
cabFile = Path.Combine(this.TempDirectory,
Path.GetFileNameWithoutExtension(mediaCab) + ".cab");
streamRec.GetStream("Data", cabFile);
}
}
}
else
{
cabFile = Path.Combine(this.SourceDirectory, mediaCab);
}
string[] fileKeys = new string[compressedFileMap.Keys.Count];
compressedFileMap.Keys.CopyTo(fileKeys, 0);
this.cabName = mediaCab;
this.cabMsg = "extract {0}\\{1}";
new CabInfo(cabFile).UnpackFiles(fileKeys, this.TempDirectory, fileKeys,
this.CabinetProgress);
ClearReadOnlyAttribute(this.TempDirectory, fileKeys);
}
foreach (KeyValuePair