// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
namespace WixToolset.Core.WindowsInstaller.Bind
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using WixToolset.Core.Bind;
using WixToolset.Data;
using WixToolset.Data.Tuples;
using WixToolset.Data.WindowsInstaller;
using WixToolset.Extensibility;
using WixToolset.Extensibility.Data;
using WixToolset.Extensibility.Services;
///
/// Binds a databse.
///
internal class BindDatabaseCommand
{
// As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
public BindDatabaseCommand(IBindContext context, IEnumerable backendExtension, Validator validator)
{
this.Messaging = context.ServiceProvider.GetService();
this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
this.CabbingThreadCount = context.CabbingThreadCount;
this.CabCachePath = context.CabCachePath;
this.Codepage = context.Codepage;
this.DefaultCompressionLevel = context.DefaultCompressionLevel;
this.DelayedFields = context.DelayedFields;
this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
this.FileSystemExtensions = context.FileSystemExtensions;
this.Intermediate = context.IntermediateRepresentation;
this.OutputPath = context.OutputPath;
this.IntermediateFolder = context.IntermediateFolder;
this.Validator = validator;
this.BackendExtensions = backendExtension;
}
private int Codepage { get; }
private int CabbingThreadCount { get; }
private string CabCachePath { get; }
private CompressionLevel? DefaultCompressionLevel { get; }
public IEnumerable DelayedFields { get; }
public IEnumerable ExpectedEmbeddedFiles { get; }
public IEnumerable FileSystemExtensions { get; }
public bool DeltaBinaryPatch { get; set; }
private IEnumerable BackendExtensions { get; }
private Intermediate Intermediate { get; }
private IMessaging Messaging { get; }
private string OutputPath { get; }
private bool SuppressAddingValidationRows { get; }
private bool SuppressLayout { get; }
private TableDefinitionCollection TableDefinitions { get; }
private string IntermediateFolder { get; }
private Validator Validator { get; }
public IEnumerable FileTransfers { get; private set; }
public IEnumerable ContentFilePaths { get; private set; }
public Pdb Pdb { get; private set; }
public void Execute()
{
var section = this.Intermediate.Sections.Single();
var fileTransfers = new List();
var containsMergeModules = false;
var suppressedTableNames = new HashSet();
// If there are any fields to resolve later, create the cache to populate during bind.
var variableCache = this.DelayedFields.Any() ? new Dictionary(StringComparer.InvariantCultureIgnoreCase) : null;
// Process the summary information table before the other tables.
bool compressed;
bool longNames;
int installerVersion;
string modularizationGuid;
{
var command = new BindSummaryInfoCommand(section);
command.Execute();
compressed = command.Compressed;
longNames = command.LongNames;
installerVersion = command.InstallerVersion;
modularizationGuid = command.ModularizationGuid;
}
// Add binder variables for all properties.
if (SectionType.Product == section.Type || variableCache != null)
{
foreach (var propertyRow in section.Tuples.OfType())
{
// Set the ProductCode if it is to be generated.
if ("ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
{
propertyRow.Value = Common.GenerateGuid();
#if TODO_FIX_INSTANCE_TRANSFORM // Is this still necessary?
// Update the target ProductCode in any instance transforms.
foreach (SubStorage subStorage in this.Output.SubStorages)
{
Output subStorageOutput = subStorage.Data;
if (OutputType.Transform != subStorageOutput.Type)
{
continue;
}
Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
foreach (Row row in instanceSummaryInformationTable.Rows)
{
if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
{
row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
break;
}
}
}
#endif
}
// Add the property name and value to the variableCache.
if (variableCache != null)
{
var key = String.Concat("property.", propertyRow.Property);
variableCache[key] = propertyRow.Value;
}
}
}
// Sequence all the actions.
{
var command = new SequenceActionsCommand(section);
command.Messaging = this.Messaging;
command.Execute();
}
{
var command = new CreateSpecialPropertiesCommand(section);
command.Execute();
}
#if TODO_FINISH_PATCH
////if (OutputType.Patch == this.Output.Type)
////{
//// foreach (SubStorage substorage in this.Output.SubStorages)
//// {
//// Output transform = substorage.Data;
//// ResolveFieldsCommand command = new ResolveFieldsCommand();
//// command.Tables = transform.Tables;
//// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
//// command.FileManagerCore = this.FileManagerCore;
//// command.FileManagers = this.FileManagers;
//// command.SupportDelayedResolution = false;
//// command.TempFilesLocation = this.TempFilesLocation;
//// command.WixVariableResolver = this.WixVariableResolver;
//// command.Execute();
//// this.MergeUnrealTables(transform.Tables);
//// }
////}
#endif
if (this.Messaging.EncounteredError)
{
return;
}
this.Messaging.Write(VerboseMessages.UpdatingFileInformation());
// This must occur after all variables and source paths have been resolved.
List fileFacades;
{
var command = new GetFileFacadesCommand(section);
command.Execute();
fileFacades = command.FileFacades;
}
// Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
{
var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles);
command.Execute();
}
// Gather information about files that do not come from merge modules.
{
var command = new UpdateFileFacadesCommand(this.Messaging, section);
command.FileFacades = fileFacades;
command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
command.OverwriteHash = true;
command.TableDefinitions = this.TableDefinitions;
command.VariableCache = variableCache;
command.Execute();
}
// Now that the variable cache is populated, resolve any delayed fields.
if (this.DelayedFields.Any())
{
var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
command.Execute();
}
// Set generated component guids.
{
var command = new CalculateComponentGuids(this.Messaging, section);
command.Execute();
}
// Retrieve file information from merge modules.
if (SectionType.Product == section.Type)
{
var wixMergeTuples = section.Tuples.OfType().ToList();
if (wixMergeTuples.Any())
{
containsMergeModules = true;
var command = new ExtractMergeModuleFilesCommand(this.Messaging, section, wixMergeTuples);
command.FileFacades = fileFacades;
command.OutputInstallerVersion = installerVersion;
command.SuppressLayout = this.SuppressLayout;
command.IntermediateFolder = this.IntermediateFolder;
command.Execute();
fileFacades.AddRange(command.MergeModulesFileFacades);
}
}
#if TODO_FINISH_PATCH
else if (OutputType.Patch == this.Output.Type)
{
// Merge transform data into the output object.
IEnumerable filesFromTransform = this.CopyFromTransformData(this.Output);
fileFacades.AddRange(filesFromTransform);
}
#endif
// stop processing if an error previously occurred
if (this.Messaging.EncounteredError)
{
return;
}
// Assign files to media.
Dictionary assignedMediaRows;
Dictionary> filesByCabinetMedia;
IEnumerable uncompressedFiles;
{
var command = new AssignMediaCommand(section, this.Messaging);
command.FileFacades = fileFacades;
command.FilesCompressed = compressed;
command.Execute();
assignedMediaRows = command.MediaRows;
filesByCabinetMedia = command.FileFacadesByCabinetMedia;
uncompressedFiles = command.UncompressedFileFacades;
}
// stop processing if an error previously occurred
if (this.Messaging.EncounteredError)
{
return;
}
// Time to create the output object. Try to put as much above here as possible, updating the IR is better.
Output output;
{
var command = new CreateOutputFromIRCommand(section, this.TableDefinitions, this.BackendExtensions);
command.Execute();
output = command.Output;
}
// Update file sequence.
{
var command = new UpdateMediaSequencesCommand(output, fileFacades, assignedMediaRows);
command.Execute();
}
// Modularize identifiers.
if (OutputType.Module == output.Type)
{
var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType());
command.Execute();
}
else // we can create instance transforms since Component Guids are set.
{
#if TODO_FIX_INSTANCE_TRANSFORM
this.CreateInstanceTransforms(this.Output);
#endif
}
#if TODO_FINISH_UPDATE
// Extended binder extensions can be called now that fields are resolved.
{
Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);
foreach (IBinderExtension extension in this.Extensions)
{
extension.AfterResolvedFields(this.Output);
}
List updatedFileFacades = new List();
foreach (Row updatedFile in updatedFiles.Rows)
{
string updatedId = updatedFile.FieldAsString(0);
FileFacade updatedFacade = fileFacades.First(f => f.File.File.Equals(updatedId));
updatedFileFacades.Add(updatedFacade);
}
if (updatedFileFacades.Any())
{
UpdateFileFacadesCommand command = new UpdateFileFacadesCommand();
command.FileFacades = fileFacades;
command.UpdateFileFacades = updatedFileFacades;
command.ModularizationGuid = modularizationGuid;
command.Output = this.Output;
command.OverwriteHash = true;
command.TableDefinitions = this.TableDefinitions;
command.VariableCache = variableCache;
command.Execute();
}
}
#endif
// Stop processing if an error previously occurred.
if (this.Messaging.EncounteredError)
{
return;
}
// Ensure the intermediate folder is created since delta patches will be
// created there.
Directory.CreateDirectory(this.IntermediateFolder);
if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
{
var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType().FirstOrDefault());
command.Execute();
}
// create cabinet files and process uncompressed files
var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
if (!this.SuppressLayout || OutputType.Module == output.Type)
{
this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());
var command = new CreateCabinetsCommand();
command.CabbingThreadCount = this.CabbingThreadCount;
command.CabCachePath = this.CabCachePath;
command.DefaultCompressionLevel = this.DefaultCompressionLevel;
command.Output = output;
command.Messaging = this.Messaging;
command.BackendExtensions = this.BackendExtensions;
command.LayoutDirectory = layoutDirectory;
command.Compressed = compressed;
command.FileRowsByCabinet = filesByCabinetMedia;
command.ResolveMedia = this.ResolveMedia;
command.TableDefinitions = this.TableDefinitions;
command.TempFilesLocation = this.IntermediateFolder;
command.WixMediaTuples = section.Tuples.OfType();
command.Execute();
fileTransfers.AddRange(command.FileTransfers);
}
#if TODO_FINISH_PATCH
if (OutputType.Patch == this.Output.Type)
{
// copy output data back into the transforms
this.CopyToTransformData(this.Output);
}
#endif
this.ValidateComponentGuids(output);
// stop processing if an error previously occurred
if (this.Messaging.EncounteredError)
{
return;
}
// Generate database file.
this.Messaging.Write(VerboseMessages.GeneratingDatabase());
string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath));
this.GenerateDatabase(output, tempDatabaseFile, false, false);
if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, output.Type.ToString(), null, out var transfer)) // note where this database needs to move in the future
{
transfer.Built = true;
fileTransfers.Add(transfer);
}
// Stop processing if an error previously occurred.
if (this.Messaging.EncounteredError)
{
return;
}
// Merge modules.
if (containsMergeModules)
{
this.Messaging.Write(VerboseMessages.MergingModules());
// Add back possibly suppressed sequence tables since all sequence tables must be present
// for the merge process to work. We'll drop the suppressed sequence tables again as
// necessary.
foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
{
var sequenceTableName = sequence.ToString();
var sequenceTable = output.Tables[sequenceTableName];
if (null == sequenceTable)
{
sequenceTable = output.EnsureTable(this.TableDefinitions[sequenceTableName]);
}
if (0 == sequenceTable.Rows.Count)
{
suppressedTableNames.Add(sequenceTableName);
}
}
var command = new MergeModulesCommand();
command.FileFacades = fileFacades;
command.Output = output;
command.OutputPath = tempDatabaseFile;
command.SuppressedTableNames = suppressedTableNames;
command.Execute();
}
if (this.Messaging.EncounteredError)
{
return;
}
#if TODO_FINISH_VALIDATION
// Validate the output if there is an MSI validator.
if (null != this.Validator)
{
Stopwatch stopwatch = Stopwatch.StartNew();
// set the output file for source line information
this.Validator.Output = this.Output;
Messaging.Instance.Write(WixVerboses.ValidatingDatabase());
this.Validator.Validate(tempDatabaseFile);
stopwatch.Stop();
Messaging.Instance.Write(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds));
// Stop processing if an error occurred.
if (Messaging.Instance.EncounteredError)
{
return;
}
}
#endif
// Process uncompressed files.
if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
{
var command = new ProcessUncompressedFilesCommand(section);
command.Compressed = compressed;
command.FileFacades = uncompressedFiles;
command.LayoutDirectory = layoutDirectory;
command.LongNamesInImage = longNames;
command.ResolveMedia = this.ResolveMedia;
command.DatabasePath = tempDatabaseFile;
command.Execute();
fileTransfers.AddRange(command.FileTransfers);
}
this.FileTransfers = fileTransfers;
this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source.Path).ToList();
this.Pdb = new Pdb { Output = output };
// TODO: Eventually this gets removed
var intermediate = new Intermediate(this.Intermediate.Id, new[] { section }, this.Intermediate.Localizations.ToDictionary(l => l.Culture, StringComparer.OrdinalIgnoreCase), this.Intermediate.EmbedFilePaths);
intermediate.Save(Path.ChangeExtension(this.OutputPath, "wir"));
}
#if TODO_FINISH_PATCH
///
/// Copy file data between transform substorages and the patch output object
///
/// The output to bind.
/// True if copying from transform to patch, false the other way.
private IEnumerable CopyFromTransformData(Output output)
{
var command = new CopyTransformDataCommand();
command.CopyOutFileRows = true;
command.Output = output;
command.TableDefinitions = this.TableDefinitions;
command.Execute();
return command.FileFacades;
}
///
/// Copy file data between transform substorages and the patch output object
///
/// The output to bind.
/// True if copying from transform to patch, false the other way.
private void CopyToTransformData(Output output)
{
var command = new CopyTransformDataCommand();
command.CopyOutFileRows = false;
command.Output = output;
command.TableDefinitions = this.TableDefinitions;
command.Execute();
}
#endif
#if TODO_FIX_INSTANCE_TRANSFORM
///
/// Creates instance transform substorages in the output.
///
/// Output containing instance transform definitions.
private void CreateInstanceTransforms(Output output)
{
// Create and add substorages for instance transforms.
Table wixInstanceTransformsTable = output.Tables["WixInstanceTransforms"];
if (null != wixInstanceTransformsTable && 0 <= wixInstanceTransformsTable.Rows.Count)
{
string targetProductCode = null;
string targetUpgradeCode = null;
string targetProductVersion = null;
Table targetSummaryInformationTable = output.Tables["_SummaryInformation"];
Table targetPropertyTable = output.Tables["Property"];
// Get the data from target database
foreach (Row propertyRow in targetPropertyTable.Rows)
{
if ("ProductCode" == (string)propertyRow[0])
{
targetProductCode = (string)propertyRow[1];
}
else if ("ProductVersion" == (string)propertyRow[0])
{
targetProductVersion = (string)propertyRow[1];
}
else if ("UpgradeCode" == (string)propertyRow[0])
{
targetUpgradeCode = (string)propertyRow[1];
}
}
// Index the Instance Component Rows.
Dictionary instanceComponentGuids = new Dictionary();
Table targetInstanceComponentTable = output.Tables["WixInstanceComponent"];
if (null != targetInstanceComponentTable && 0 < targetInstanceComponentTable.Rows.Count)
{
foreach (Row row in targetInstanceComponentTable.Rows)
{
// Build up all the instances, we'll get the Components rows from the real Component table.
instanceComponentGuids.Add((string)row[0], null);
}
Table targetComponentTable = output.Tables["Component"];
foreach (ComponentRow componentRow in targetComponentTable.Rows)
{
string component = (string)componentRow[0];
if (instanceComponentGuids.ContainsKey(component))
{
instanceComponentGuids[component] = componentRow;
}
}
}
// Generate the instance transforms
foreach (Row instanceRow in wixInstanceTransformsTable.Rows)
{
string instanceId = (string)instanceRow[0];
Output instanceTransform = new Output(instanceRow.SourceLineNumbers);
instanceTransform.Type = OutputType.Transform;
instanceTransform.Codepage = output.Codepage;
Table instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
string targetPlatformAndLanguage = null;
foreach (Row summaryInformationRow in targetSummaryInformationTable.Rows)
{
if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE
{
targetPlatformAndLanguage = (string)summaryInformationRow[1];
}
// Copy the row's data to the transform.
Row copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(null);
copyOfSummaryRow[0] = summaryInformationRow[0];
copyOfSummaryRow[1] = summaryInformationRow[1];
}
// Modify the appropriate properties.
Table propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]);
// Change the ProductCode property
string productCode = (string)instanceRow[2];
if ("*" == productCode)
{
productCode = Common.GenerateGuid();
}
Row productCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
productCodeRow.Operation = RowOperation.Modify;
productCodeRow.Fields[1].Modified = true;
productCodeRow[0] = "ProductCode";
productCodeRow[1] = productCode;
// Change the instance property
Row instanceIdRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
instanceIdRow.Operation = RowOperation.Modify;
instanceIdRow.Fields[1].Modified = true;
instanceIdRow[0] = (string)instanceRow[1];
instanceIdRow[1] = instanceId;
if (null != instanceRow[3])
{
// Change the ProductName property
Row productNameRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
productNameRow.Operation = RowOperation.Modify;
productNameRow.Fields[1].Modified = true;
productNameRow[0] = "ProductName";
productNameRow[1] = (string)instanceRow[3];
}
if (null != instanceRow[4])
{
// Change the UpgradeCode property
Row upgradeCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers);
upgradeCodeRow.Operation = RowOperation.Modify;
upgradeCodeRow.Fields[1].Modified = true;
upgradeCodeRow[0] = "UpgradeCode";
upgradeCodeRow[1] = instanceRow[4];
// Change the Upgrade table
Table targetUpgradeTable = output.Tables["Upgrade"];
if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count)
{
string upgradeId = (string)instanceRow[4];
Table upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]);
foreach (Row row in targetUpgradeTable.Rows)
{
// In case they are upgrading other codes to this new product, leave the ones that don't match the
// Product.UpgradeCode intact.
if (targetUpgradeCode == (string)row[0])
{
Row upgradeRow = upgradeTable.CreateRow(null);
upgradeRow.Operation = RowOperation.Add;
upgradeRow.Fields[0].Modified = true;
// I was hoping to be able to RowOperation.Modify, but that didn't appear to function.
// upgradeRow.Fields[0].PreviousData = (string)row[0];
// Inserting a new Upgrade record with the updated UpgradeCode
upgradeRow[0] = upgradeId;
upgradeRow[1] = row[1];
upgradeRow[2] = row[2];
upgradeRow[3] = row[3];
upgradeRow[4] = row[4];
upgradeRow[5] = row[5];
upgradeRow[6] = row[6];
// Delete the old row
Row upgradeRemoveRow = upgradeTable.CreateRow(null);
upgradeRemoveRow.Operation = RowOperation.Delete;
upgradeRemoveRow[0] = row[0];
upgradeRemoveRow[1] = row[1];
upgradeRemoveRow[2] = row[2];
upgradeRemoveRow[3] = row[3];
upgradeRemoveRow[4] = row[4];
upgradeRemoveRow[5] = row[5];
upgradeRemoveRow[6] = row[6];
}
}
}
}
// If there are instance Components generate new GUIDs for them.
if (0 < instanceComponentGuids.Count)
{
Table componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]);
foreach (ComponentRow targetComponentRow in instanceComponentGuids.Values)
{
string guid = targetComponentRow.Guid;
if (!String.IsNullOrEmpty(guid))
{
Row instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers);
instanceComponentRow.Operation = RowOperation.Modify;
instanceComponentRow.Fields[1].Modified = true;
instanceComponentRow[0] = targetComponentRow[0];
instanceComponentRow[1] = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)).ToString("B").ToUpper(CultureInfo.InvariantCulture);
instanceComponentRow[2] = targetComponentRow[2];
instanceComponentRow[3] = targetComponentRow[3];
instanceComponentRow[4] = targetComponentRow[4];
instanceComponentRow[5] = targetComponentRow[5];
}
}
}
// Update the summary information
Hashtable summaryRows = new Hashtable(instanceSummaryInformationTable.Rows.Count);
foreach (Row row in instanceSummaryInformationTable.Rows)
{
summaryRows[row[0]] = row;
if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0])
{
row[1] = targetPlatformAndLanguage;
}
else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0])
{
row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode);
}
else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0])
{
row[1] = 0;
}
else if ((int)SummaryInformation.Transform.Security == (int)row[0])
{
row[1] = "4";
}
}
if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage))
{
Row summaryRow = instanceSummaryInformationTable.CreateRow(null);
summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage;
summaryRow[1] = targetPlatformAndLanguage;
}
else if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags))
{
Row summaryRow = instanceSummaryInformationTable.CreateRow(null);
summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags;
summaryRow[1] = "0";
}
else if (!summaryRows.Contains((int)SummaryInformation.Transform.Security))
{
Row summaryRow = instanceSummaryInformationTable.CreateRow(null);
summaryRow[0] = (int)SummaryInformation.Transform.Security;
summaryRow[1] = "4";
}
output.SubStorages.Add(new SubStorage(instanceId, instanceTransform));
}
}
}
#endif
///
/// Validate that there are no duplicate GUIDs in the output.
///
///
/// Duplicate GUIDs without conditions are an error condition; with conditions, it's a
/// warning, as the conditions might be mutually exclusive.
///
private void ValidateComponentGuids(Output output)
{
Table componentTable = output.Tables["Component"];
if (null != componentTable)
{
Dictionary componentGuidConditions = new Dictionary(componentTable.Rows.Count);
foreach (Data.WindowsInstaller.Rows.ComponentRow row in componentTable.Rows)
{
// we don't care about unmanaged components and if there's a * GUID remaining,
// there's already an error that prevented it from being replaced with a real GUID.
if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid)
{
bool thisComponentHasCondition = !String.IsNullOrEmpty(row.Condition);
bool allComponentsHaveConditions = thisComponentHasCondition;
if (componentGuidConditions.ContainsKey(row.Guid))
{
allComponentsHaveConditions = componentGuidConditions[row.Guid] && thisComponentHasCondition;
if (allComponentsHaveConditions)
{
this.Messaging.Write(WarningMessages.DuplicateComponentGuidsMustHaveMutuallyExclusiveConditions(row.SourceLineNumbers, row.Component, row.Guid));
}
else
{
this.Messaging.Write(ErrorMessages.DuplicateComponentGuids(row.SourceLineNumbers, row.Component, row.Guid));
}
}
componentGuidConditions[row.Guid] = allComponentsHaveConditions;
}
}
}
}
///
/// Update Control and BBControl text by reading from files when necessary.
///
/// Internal representation of the msi database to operate upon.
private void UpdateControlText(Output output)
{
var command = new UpdateControlTextCommand();
command.Messaging = this.Messaging;
command.BBControlTable = output.Tables["BBControl"];
command.WixBBControlTable = output.Tables["WixBBControl"];
command.ControlTable = output.Tables["Control"];
command.WixControlTable = output.Tables["WixControl"];
command.Execute();
}
private string ResolveMedia(MediaTuple mediaRow, string mediaLayoutDirectory, string layoutDirectory)
{
string layout = null;
foreach (var extension in this.BackendExtensions)
{
layout = extension.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory);
if (!String.IsNullOrEmpty(layout))
{
break;
}
}
// If no binder file manager resolved the layout, do the default behavior.
if (String.IsNullOrEmpty(layout))
{
if (String.IsNullOrEmpty(mediaLayoutDirectory))
{
layout = layoutDirectory;
}
else if (Path.IsPathRooted(mediaLayoutDirectory))
{
layout = mediaLayoutDirectory;
}
else
{
layout = Path.Combine(layoutDirectory, mediaLayoutDirectory);
}
}
return layout;
}
///
/// Creates the MSI/MSM/PCP database.
///
/// Output to create database for.
/// The database file to create.
/// Whether to keep columns added in a transform.
/// Whether to use a subdirectory based on the file name for intermediate files.
private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
{
var command = new GenerateDatabaseCommand();
command.Extensions = this.FileSystemExtensions;
command.Output = output;
command.OutputPath = databaseFile;
command.KeepAddedColumns = keepAddedColumns;
command.UseSubDirectory = useSubdirectory;
command.SuppressAddingValidationRows = this.SuppressAddingValidationRows;
command.TableDefinitions = this.TableDefinitions;
command.TempFilesLocation = this.IntermediateFolder;
command.Codepage = this.Codepage;
command.Execute();
}
}
}