diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs')
| -rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | 1282 |
1 files changed, 1282 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs new file mode 100644 index 00000000..2e2c5417 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | |||
| @@ -0,0 +1,1282 @@ | |||
| 1 | // 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. | ||
| 2 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind | ||
| 4 | { | ||
| 5 | using System; | ||
| 6 | using System.Collections; | ||
| 7 | using System.Collections.Generic; | ||
| 8 | using System.Diagnostics; | ||
| 9 | using System.Globalization; | ||
| 10 | using System.IO; | ||
| 11 | using System.Linq; | ||
| 12 | using WixToolset.Bind; | ||
| 13 | using WixToolset.Core.Bind; | ||
| 14 | using WixToolset.Core.WindowsInstaller.Databases; | ||
| 15 | using WixToolset.Data; | ||
| 16 | using WixToolset.Data.Bind; | ||
| 17 | using WixToolset.Data.Rows; | ||
| 18 | using WixToolset.Extensibility; | ||
| 19 | using WixToolset.Msi; | ||
| 20 | |||
| 21 | /// <summary> | ||
| 22 | /// Binds a databse. | ||
| 23 | /// </summary> | ||
| 24 | internal class BindDatabaseCommand | ||
| 25 | { | ||
| 26 | // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. | ||
| 27 | private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); | ||
| 28 | |||
| 29 | public BindDatabaseCommand(IBindContext context, Validator validator) | ||
| 30 | { | ||
| 31 | this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); | ||
| 32 | |||
| 33 | this.BindPaths = context.BindPaths; | ||
| 34 | this.CabbingThreadCount = context.CabbingThreadCount; | ||
| 35 | this.CabCachePath = context.CabCachePath; | ||
| 36 | this.Codepage = context.Codepage; | ||
| 37 | this.DefaultCompressionLevel = context.DefaultCompressionLevel; | ||
| 38 | this.DelayedFields = context.DelayedFields; | ||
| 39 | this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; | ||
| 40 | this.Extensions = context.Extensions; | ||
| 41 | this.Output = context.IntermediateRepresentation; | ||
| 42 | this.OutputPath = context.OutputPath; | ||
| 43 | this.PdbFile = context.OutputPdbPath; | ||
| 44 | this.IntermediateFolder = context.IntermediateFolder; | ||
| 45 | this.Validator = validator; | ||
| 46 | this.WixVariableResolver = context.WixVariableResolver; | ||
| 47 | |||
| 48 | this.BackendExtensions = context.ExtensionManager.Create<IWindowsInstallerBackendExtension>(); | ||
| 49 | } | ||
| 50 | |||
| 51 | private IEnumerable<BindPath> BindPaths { get; } | ||
| 52 | |||
| 53 | private int Codepage { get; } | ||
| 54 | |||
| 55 | private int CabbingThreadCount { get; } | ||
| 56 | |||
| 57 | private string CabCachePath { get; } | ||
| 58 | |||
| 59 | private CompressionLevel DefaultCompressionLevel { get; } | ||
| 60 | |||
| 61 | public IEnumerable<IDelayedField> DelayedFields { get; } | ||
| 62 | |||
| 63 | public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; } | ||
| 64 | |||
| 65 | public bool DeltaBinaryPatch { get; set; } | ||
| 66 | |||
| 67 | private IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { get; } | ||
| 68 | |||
| 69 | private IEnumerable<IBinderExtension> Extensions { get; } | ||
| 70 | |||
| 71 | private IEnumerable<InspectorExtension> InspectorExtensions { get; } | ||
| 72 | |||
| 73 | private string PdbFile { get; } | ||
| 74 | |||
| 75 | private Output Output { get; } | ||
| 76 | |||
| 77 | private string OutputPath { get; } | ||
| 78 | |||
| 79 | private bool SuppressAddingValidationRows { get; } | ||
| 80 | |||
| 81 | private bool SuppressLayout { get; } | ||
| 82 | |||
| 83 | private TableDefinitionCollection TableDefinitions { get; } | ||
| 84 | |||
| 85 | private string IntermediateFolder { get; } | ||
| 86 | |||
| 87 | private Validator Validator { get; } | ||
| 88 | |||
| 89 | private IBindVariableResolver WixVariableResolver { get; } | ||
| 90 | |||
| 91 | public IEnumerable<FileTransfer> FileTransfers { get; private set; } | ||
| 92 | |||
| 93 | public IEnumerable<string> ContentFilePaths { get; private set; } | ||
| 94 | |||
| 95 | public void Execute() | ||
| 96 | { | ||
| 97 | List<FileTransfer> fileTransfers = new List<FileTransfer>(); | ||
| 98 | |||
| 99 | HashSet<string> suppressedTableNames = new HashSet<string>(); | ||
| 100 | |||
| 101 | // If there are any fields to resolve later, create the cache to populate during bind. | ||
| 102 | IDictionary<string, string> variableCache = null; | ||
| 103 | if (this.DelayedFields.Any()) | ||
| 104 | { | ||
| 105 | variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); | ||
| 106 | } | ||
| 107 | |||
| 108 | this.LocalizeUI(this.Output.Tables); | ||
| 109 | |||
| 110 | // Process the summary information table before the other tables. | ||
| 111 | bool compressed; | ||
| 112 | bool longNames; | ||
| 113 | int installerVersion; | ||
| 114 | string modularizationGuid; | ||
| 115 | { | ||
| 116 | BindSummaryInfoCommand command = new BindSummaryInfoCommand(); | ||
| 117 | command.Output = this.Output; | ||
| 118 | command.Execute(); | ||
| 119 | |||
| 120 | compressed = command.Compressed; | ||
| 121 | longNames = command.LongNames; | ||
| 122 | installerVersion = command.InstallerVersion; | ||
| 123 | modularizationGuid = command.ModularizationGuid; | ||
| 124 | } | ||
| 125 | |||
| 126 | // Stop processing if an error previously occurred. | ||
| 127 | if (Messaging.Instance.EncounteredError) | ||
| 128 | { | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | |||
| 132 | // Modularize identifiers and add tables with real streams to the import tables. | ||
| 133 | if (OutputType.Module == this.Output.Type) | ||
| 134 | { | ||
| 135 | // Gather all the suppress modularization identifiers | ||
| 136 | HashSet<string> suppressModularizationIdentifiers = null; | ||
| 137 | Table wixSuppressModularizationTable = this.Output.Tables["WixSuppressModularization"]; | ||
| 138 | if (null != wixSuppressModularizationTable) | ||
| 139 | { | ||
| 140 | suppressModularizationIdentifiers = new HashSet<string>(wixSuppressModularizationTable.Rows.Select(row => (string)row[0])); | ||
| 141 | } | ||
| 142 | |||
| 143 | foreach (Table table in this.Output.Tables) | ||
| 144 | { | ||
| 145 | table.Modularize(modularizationGuid, suppressModularizationIdentifiers); | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | // This must occur after all variables and source paths have been resolved and after modularization. | ||
| 150 | List<FileFacade> fileFacades; | ||
| 151 | { | ||
| 152 | GetFileFacadesCommand command = new GetFileFacadesCommand(); | ||
| 153 | command.FileTable = this.Output.Tables["File"]; | ||
| 154 | command.WixFileTable = this.Output.Tables["WixFile"]; | ||
| 155 | command.WixDeltaPatchFileTable = this.Output.Tables["WixDeltaPatchFile"]; | ||
| 156 | command.WixDeltaPatchSymbolPathsTable = this.Output.Tables["WixDeltaPatchSymbolPaths"]; | ||
| 157 | command.Execute(); | ||
| 158 | |||
| 159 | fileFacades = command.FileFacades; | ||
| 160 | } | ||
| 161 | |||
| 162 | ////if (OutputType.Patch == this.Output.Type) | ||
| 163 | ////{ | ||
| 164 | //// foreach (SubStorage substorage in this.Output.SubStorages) | ||
| 165 | //// { | ||
| 166 | //// Output transform = substorage.Data; | ||
| 167 | |||
| 168 | //// ResolveFieldsCommand command = new ResolveFieldsCommand(); | ||
| 169 | //// command.Tables = transform.Tables; | ||
| 170 | //// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; | ||
| 171 | //// command.FileManagerCore = this.FileManagerCore; | ||
| 172 | //// command.FileManagers = this.FileManagers; | ||
| 173 | //// command.SupportDelayedResolution = false; | ||
| 174 | //// command.TempFilesLocation = this.TempFilesLocation; | ||
| 175 | //// command.WixVariableResolver = this.WixVariableResolver; | ||
| 176 | //// command.Execute(); | ||
| 177 | |||
| 178 | //// this.MergeUnrealTables(transform.Tables); | ||
| 179 | //// } | ||
| 180 | ////} | ||
| 181 | |||
| 182 | { | ||
| 183 | CreateSpecialPropertiesCommand command = new CreateSpecialPropertiesCommand(); | ||
| 184 | command.PropertyTable = this.Output.Tables["Property"]; | ||
| 185 | command.WixPropertyTable = this.Output.Tables["WixProperty"]; | ||
| 186 | command.Execute(); | ||
| 187 | } | ||
| 188 | |||
| 189 | if (Messaging.Instance.EncounteredError) | ||
| 190 | { | ||
| 191 | return; | ||
| 192 | } | ||
| 193 | |||
| 194 | // Add binder variables for all properties. | ||
| 195 | Table propertyTable = this.Output.Tables["Property"]; | ||
| 196 | if (null != propertyTable) | ||
| 197 | { | ||
| 198 | foreach (PropertyRow propertyRow in propertyTable.Rows) | ||
| 199 | { | ||
| 200 | // Set the ProductCode if it is to be generated. | ||
| 201 | if (OutputType.Product == this.Output.Type && "ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal)) | ||
| 202 | { | ||
| 203 | propertyRow.Value = Common.GenerateGuid(); | ||
| 204 | |||
| 205 | // Update the target ProductCode in any instance transforms. | ||
| 206 | foreach (SubStorage subStorage in this.Output.SubStorages) | ||
| 207 | { | ||
| 208 | Output subStorageOutput = subStorage.Data; | ||
| 209 | if (OutputType.Transform != subStorageOutput.Type) | ||
| 210 | { | ||
| 211 | continue; | ||
| 212 | } | ||
| 213 | |||
| 214 | Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"]; | ||
| 215 | foreach (Row row in instanceSummaryInformationTable.Rows) | ||
| 216 | { | ||
| 217 | if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0)) | ||
| 218 | { | ||
| 219 | row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value); | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | // Add the property name and value to the variableCache. | ||
| 227 | if (null != variableCache) | ||
| 228 | { | ||
| 229 | string key = String.Concat("property.", Common.Demodularize(this.Output.Type, modularizationGuid, propertyRow.Property)); | ||
| 230 | variableCache[key] = propertyRow.Value; | ||
| 231 | } | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | // Extract files that come from cabinet files (this does not extract files from merge modules). | ||
| 236 | { | ||
| 237 | ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand(); | ||
| 238 | command.FilesWithEmbeddedFiles = this.ExpectedEmbeddedFiles; | ||
| 239 | command.Execute(); | ||
| 240 | } | ||
| 241 | |||
| 242 | if (OutputType.Product == this.Output.Type) | ||
| 243 | { | ||
| 244 | // Retrieve files and their information from merge modules. | ||
| 245 | Table wixMergeTable = this.Output.Tables["WixMerge"]; | ||
| 246 | |||
| 247 | if (null != wixMergeTable) | ||
| 248 | { | ||
| 249 | ExtractMergeModuleFilesCommand command = new ExtractMergeModuleFilesCommand(); | ||
| 250 | command.FileFacades = fileFacades; | ||
| 251 | command.FileTable = this.Output.Tables["File"]; | ||
| 252 | command.WixFileTable = this.Output.Tables["WixFile"]; | ||
| 253 | command.WixMergeTable = wixMergeTable; | ||
| 254 | command.OutputInstallerVersion = installerVersion; | ||
| 255 | command.SuppressLayout = this.SuppressLayout; | ||
| 256 | command.TempFilesLocation = this.IntermediateFolder; | ||
| 257 | command.Execute(); | ||
| 258 | |||
| 259 | fileFacades.AddRange(command.MergeModulesFileFacades); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | else if (OutputType.Patch == this.Output.Type) | ||
| 263 | { | ||
| 264 | // Merge transform data into the output object. | ||
| 265 | IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output); | ||
| 266 | |||
| 267 | fileFacades.AddRange(filesFromTransform); | ||
| 268 | } | ||
| 269 | |||
| 270 | // stop processing if an error previously occurred | ||
| 271 | if (Messaging.Instance.EncounteredError) | ||
| 272 | { | ||
| 273 | return; | ||
| 274 | } | ||
| 275 | |||
| 276 | Messaging.Instance.OnMessage(WixVerboses.UpdatingFileInformation()); | ||
| 277 | |||
| 278 | // Gather information about files that did not come from merge modules (i.e. rows with a reference to the File table). | ||
| 279 | { | ||
| 280 | UpdateFileFacadesCommand command = new UpdateFileFacadesCommand(); | ||
| 281 | command.FileFacades = fileFacades; | ||
| 282 | command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule); | ||
| 283 | command.ModularizationGuid = modularizationGuid; | ||
| 284 | command.Output = this.Output; | ||
| 285 | command.OverwriteHash = true; | ||
| 286 | command.TableDefinitions = this.TableDefinitions; | ||
| 287 | command.VariableCache = variableCache; | ||
| 288 | command.Execute(); | ||
| 289 | } | ||
| 290 | |||
| 291 | // Set generated component guids. | ||
| 292 | this.SetComponentGuids(this.Output); | ||
| 293 | |||
| 294 | // With the Component Guids set now we can create instance transforms. | ||
| 295 | this.CreateInstanceTransforms(this.Output); | ||
| 296 | |||
| 297 | this.ValidateComponentGuids(this.Output); | ||
| 298 | |||
| 299 | this.UpdateControlText(this.Output); | ||
| 300 | |||
| 301 | if (this.DelayedFields.Any()) | ||
| 302 | { | ||
| 303 | ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand(); | ||
| 304 | command.OutputType = this.Output.Type; | ||
| 305 | command.DelayedFields = this.DelayedFields; | ||
| 306 | command.ModularizationGuid = null; | ||
| 307 | command.VariableCache = variableCache; | ||
| 308 | command.Execute(); | ||
| 309 | } | ||
| 310 | |||
| 311 | // Assign files to media. | ||
| 312 | RowDictionary<MediaRow> assignedMediaRows; | ||
| 313 | Dictionary<MediaRow, IEnumerable<FileFacade>> filesByCabinetMedia; | ||
| 314 | IEnumerable<FileFacade> uncompressedFiles; | ||
| 315 | { | ||
| 316 | AssignMediaCommand command = new AssignMediaCommand(); | ||
| 317 | command.FilesCompressed = compressed; | ||
| 318 | command.FileFacades = fileFacades; | ||
| 319 | command.Output = this.Output; | ||
| 320 | command.TableDefinitions = this.TableDefinitions; | ||
| 321 | command.Execute(); | ||
| 322 | |||
| 323 | assignedMediaRows = command.MediaRows; | ||
| 324 | filesByCabinetMedia = command.FileFacadesByCabinetMedia; | ||
| 325 | uncompressedFiles = command.UncompressedFileFacades; | ||
| 326 | } | ||
| 327 | |||
| 328 | // Update file sequence. | ||
| 329 | this.UpdateMediaSequences(this.Output.Type, fileFacades, assignedMediaRows); | ||
| 330 | |||
| 331 | // stop processing if an error previously occurred | ||
| 332 | if (Messaging.Instance.EncounteredError) | ||
| 333 | { | ||
| 334 | return; | ||
| 335 | } | ||
| 336 | |||
| 337 | // Extended binder extensions can be called now that fields are resolved. | ||
| 338 | { | ||
| 339 | Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]); | ||
| 340 | |||
| 341 | foreach (IBinderExtension extension in this.Extensions) | ||
| 342 | { | ||
| 343 | extension.AfterResolvedFields(this.Output); | ||
| 344 | } | ||
| 345 | |||
| 346 | List<FileFacade> updatedFileFacades = new List<FileFacade>(); | ||
| 347 | |||
| 348 | foreach (Row updatedFile in updatedFiles.Rows) | ||
| 349 | { | ||
| 350 | string updatedId = updatedFile.FieldAsString(0); | ||
| 351 | |||
| 352 | FileFacade updatedFacade = fileFacades.First(f => f.File.File.Equals(updatedId)); | ||
| 353 | |||
| 354 | updatedFileFacades.Add(updatedFacade); | ||
| 355 | } | ||
| 356 | |||
| 357 | if (updatedFileFacades.Any()) | ||
| 358 | { | ||
| 359 | UpdateFileFacadesCommand command = new UpdateFileFacadesCommand(); | ||
| 360 | command.FileFacades = fileFacades; | ||
| 361 | command.UpdateFileFacades = updatedFileFacades; | ||
| 362 | command.ModularizationGuid = modularizationGuid; | ||
| 363 | command.Output = this.Output; | ||
| 364 | command.OverwriteHash = true; | ||
| 365 | command.TableDefinitions = this.TableDefinitions; | ||
| 366 | command.VariableCache = variableCache; | ||
| 367 | command.Execute(); | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | // stop processing if an error previously occurred | ||
| 372 | if (Messaging.Instance.EncounteredError) | ||
| 373 | { | ||
| 374 | return; | ||
| 375 | } | ||
| 376 | |||
| 377 | Directory.CreateDirectory(this.IntermediateFolder); | ||
| 378 | |||
| 379 | if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch) | ||
| 380 | { | ||
| 381 | CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand(); | ||
| 382 | command.FileFacades = fileFacades; | ||
| 383 | command.WixPatchIdTable = this.Output.Tables["WixPatchId"]; | ||
| 384 | command.TempFilesLocation = this.IntermediateFolder; | ||
| 385 | command.Execute(); | ||
| 386 | } | ||
| 387 | |||
| 388 | // create cabinet files and process uncompressed files | ||
| 389 | string layoutDirectory = Path.GetDirectoryName(this.OutputPath); | ||
| 390 | if (!this.SuppressLayout || OutputType.Module == this.Output.Type) | ||
| 391 | { | ||
| 392 | Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles()); | ||
| 393 | |||
| 394 | var command = new CreateCabinetsCommand(); | ||
| 395 | command.CabbingThreadCount = this.CabbingThreadCount; | ||
| 396 | command.CabCachePath = this.CabCachePath; | ||
| 397 | command.DefaultCompressionLevel = this.DefaultCompressionLevel; | ||
| 398 | command.Output = this.Output; | ||
| 399 | command.BackendExtensions = this.BackendExtensions; | ||
| 400 | command.LayoutDirectory = layoutDirectory; | ||
| 401 | command.Compressed = compressed; | ||
| 402 | command.FileRowsByCabinet = filesByCabinetMedia; | ||
| 403 | command.ResolveMedia = this.ResolveMedia; | ||
| 404 | command.TableDefinitions = this.TableDefinitions; | ||
| 405 | command.TempFilesLocation = this.IntermediateFolder; | ||
| 406 | command.WixMediaTable = this.Output.Tables["WixMedia"]; | ||
| 407 | command.Execute(); | ||
| 408 | |||
| 409 | fileTransfers.AddRange(command.FileTransfers); | ||
| 410 | } | ||
| 411 | |||
| 412 | if (OutputType.Patch == this.Output.Type) | ||
| 413 | { | ||
| 414 | // copy output data back into the transforms | ||
| 415 | this.CopyToTransformData(this.Output); | ||
| 416 | } | ||
| 417 | |||
| 418 | // stop processing if an error previously occurred | ||
| 419 | if (Messaging.Instance.EncounteredError) | ||
| 420 | { | ||
| 421 | return; | ||
| 422 | } | ||
| 423 | |||
| 424 | // add back suppressed tables which must be present prior to merging in modules | ||
| 425 | if (OutputType.Product == this.Output.Type) | ||
| 426 | { | ||
| 427 | Table wixMergeTable = this.Output.Tables["WixMerge"]; | ||
| 428 | |||
| 429 | if (null != wixMergeTable && 0 < wixMergeTable.Rows.Count) | ||
| 430 | { | ||
| 431 | foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) | ||
| 432 | { | ||
| 433 | string sequenceTableName = sequence.ToString(); | ||
| 434 | Table sequenceTable = this.Output.Tables[sequenceTableName]; | ||
| 435 | |||
| 436 | if (null == sequenceTable) | ||
| 437 | { | ||
| 438 | sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]); | ||
| 439 | } | ||
| 440 | |||
| 441 | if (0 == sequenceTable.Rows.Count) | ||
| 442 | { | ||
| 443 | suppressedTableNames.Add(sequenceTableName); | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | //foreach (BinderExtension extension in this.Extensions) | ||
| 450 | //{ | ||
| 451 | // extension.PostBind(this.Context); | ||
| 452 | //} | ||
| 453 | |||
| 454 | // generate database file | ||
| 455 | Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase()); | ||
| 456 | string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath)); | ||
| 457 | this.GenerateDatabase(this.Output, tempDatabaseFile, false, false); | ||
| 458 | |||
| 459 | FileTransfer transfer; | ||
| 460 | if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, this.Output.Type.ToString(), null, out transfer)) // note where this database needs to move in the future | ||
| 461 | { | ||
| 462 | transfer.Built = true; | ||
| 463 | fileTransfers.Add(transfer); | ||
| 464 | } | ||
| 465 | |||
| 466 | // stop processing if an error previously occurred | ||
| 467 | if (Messaging.Instance.EncounteredError) | ||
| 468 | { | ||
| 469 | return; | ||
| 470 | } | ||
| 471 | |||
| 472 | // Output the output to a file | ||
| 473 | Pdb pdb = new Pdb(); | ||
| 474 | pdb.Output = this.Output; | ||
| 475 | if (!String.IsNullOrEmpty(this.PdbFile)) | ||
| 476 | { | ||
| 477 | pdb.Save(this.PdbFile); | ||
| 478 | } | ||
| 479 | |||
| 480 | // Merge modules. | ||
| 481 | if (OutputType.Product == this.Output.Type) | ||
| 482 | { | ||
| 483 | Messaging.Instance.OnMessage(WixVerboses.MergingModules()); | ||
| 484 | |||
| 485 | MergeModulesCommand command = new MergeModulesCommand(); | ||
| 486 | command.FileFacades = fileFacades; | ||
| 487 | command.Output = this.Output; | ||
| 488 | command.OutputPath = tempDatabaseFile; | ||
| 489 | command.SuppressedTableNames = suppressedTableNames; | ||
| 490 | command.Execute(); | ||
| 491 | |||
| 492 | // stop processing if an error previously occurred | ||
| 493 | if (Messaging.Instance.EncounteredError) | ||
| 494 | { | ||
| 495 | return; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | // inspect the MSI prior to running ICEs | ||
| 500 | //InspectorCore inspectorCore = new InspectorCore(); | ||
| 501 | //foreach (InspectorExtension inspectorExtension in this.InspectorExtensions) | ||
| 502 | //{ | ||
| 503 | // inspectorExtension.Core = inspectorCore; | ||
| 504 | // inspectorExtension.InspectDatabase(tempDatabaseFile, pdb); | ||
| 505 | |||
| 506 | // inspectorExtension.Core = null; // reset. | ||
| 507 | //} | ||
| 508 | |||
| 509 | if (Messaging.Instance.EncounteredError) | ||
| 510 | { | ||
| 511 | return; | ||
| 512 | } | ||
| 513 | |||
| 514 | // validate the output if there is an MSI validator | ||
| 515 | if (null != this.Validator) | ||
| 516 | { | ||
| 517 | Stopwatch stopwatch = Stopwatch.StartNew(); | ||
| 518 | |||
| 519 | // set the output file for source line information | ||
| 520 | this.Validator.Output = this.Output; | ||
| 521 | |||
| 522 | Messaging.Instance.OnMessage(WixVerboses.ValidatingDatabase()); | ||
| 523 | |||
| 524 | this.Validator.Validate(tempDatabaseFile); | ||
| 525 | |||
| 526 | stopwatch.Stop(); | ||
| 527 | Messaging.Instance.OnMessage(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds)); | ||
| 528 | |||
| 529 | // Stop processing if an error occurred. | ||
| 530 | if (Messaging.Instance.EncounteredError) | ||
| 531 | { | ||
| 532 | return; | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | // Process uncompressed files. | ||
| 537 | if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) | ||
| 538 | { | ||
| 539 | var command = new ProcessUncompressedFilesCommand(); | ||
| 540 | command.Compressed = compressed; | ||
| 541 | command.FileFacades = uncompressedFiles; | ||
| 542 | command.LayoutDirectory = layoutDirectory; | ||
| 543 | command.LongNamesInImage = longNames; | ||
| 544 | command.MediaRows = assignedMediaRows; | ||
| 545 | command.ResolveMedia = this.ResolveMedia; | ||
| 546 | command.DatabasePath = tempDatabaseFile; | ||
| 547 | command.WixMediaTable = this.Output.Tables["WixMedia"]; | ||
| 548 | command.Execute(); | ||
| 549 | |||
| 550 | fileTransfers.AddRange(command.FileTransfers); | ||
| 551 | } | ||
| 552 | |||
| 553 | this.FileTransfers = fileTransfers; | ||
| 554 | this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source).ToList(); | ||
| 555 | } | ||
| 556 | |||
| 557 | /// <summary> | ||
| 558 | /// Localize dialogs and controls. | ||
| 559 | /// </summary> | ||
| 560 | /// <param name="tables">The tables to localize.</param> | ||
| 561 | private void LocalizeUI(TableIndexedCollection tables) | ||
| 562 | { | ||
| 563 | Table dialogTable = tables["Dialog"]; | ||
| 564 | if (null != dialogTable) | ||
| 565 | { | ||
| 566 | foreach (Row row in dialogTable.Rows) | ||
| 567 | { | ||
| 568 | string dialog = (string)row[0]; | ||
| 569 | |||
| 570 | if (this.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl)) | ||
| 571 | { | ||
| 572 | if (CompilerConstants.IntegerNotSet != localizedControl.X) | ||
| 573 | { | ||
| 574 | row[1] = localizedControl.X; | ||
| 575 | } | ||
| 576 | |||
| 577 | if (CompilerConstants.IntegerNotSet != localizedControl.Y) | ||
| 578 | { | ||
| 579 | row[2] = localizedControl.Y; | ||
| 580 | } | ||
| 581 | |||
| 582 | if (CompilerConstants.IntegerNotSet != localizedControl.Width) | ||
| 583 | { | ||
| 584 | row[3] = localizedControl.Width; | ||
| 585 | } | ||
| 586 | |||
| 587 | if (CompilerConstants.IntegerNotSet != localizedControl.Height) | ||
| 588 | { | ||
| 589 | row[4] = localizedControl.Height; | ||
| 590 | } | ||
| 591 | |||
| 592 | row[5] = (int)row[5] | localizedControl.Attributes; | ||
| 593 | |||
| 594 | if (!String.IsNullOrEmpty(localizedControl.Text)) | ||
| 595 | { | ||
| 596 | row[6] = localizedControl.Text; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | Table controlTable = tables["Control"]; | ||
| 603 | if (null != controlTable) | ||
| 604 | { | ||
| 605 | foreach (Row row in controlTable.Rows) | ||
| 606 | { | ||
| 607 | string dialog = (string)row[0]; | ||
| 608 | string control = (string)row[1]; | ||
| 609 | |||
| 610 | if (this.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl)) | ||
| 611 | { | ||
| 612 | if (CompilerConstants.IntegerNotSet != localizedControl.X) | ||
| 613 | { | ||
| 614 | row[3] = localizedControl.X.ToString(); | ||
| 615 | } | ||
| 616 | |||
| 617 | if (CompilerConstants.IntegerNotSet != localizedControl.Y) | ||
| 618 | { | ||
| 619 | row[4] = localizedControl.Y.ToString(); | ||
| 620 | } | ||
| 621 | |||
| 622 | if (CompilerConstants.IntegerNotSet != localizedControl.Width) | ||
| 623 | { | ||
| 624 | row[5] = localizedControl.Width.ToString(); | ||
| 625 | } | ||
| 626 | |||
| 627 | if (CompilerConstants.IntegerNotSet != localizedControl.Height) | ||
| 628 | { | ||
| 629 | row[6] = localizedControl.Height.ToString(); | ||
| 630 | } | ||
| 631 | |||
| 632 | row[7] = (int)row[7] | localizedControl.Attributes; | ||
| 633 | |||
| 634 | if (!String.IsNullOrEmpty(localizedControl.Text)) | ||
| 635 | { | ||
| 636 | row[9] = localizedControl.Text; | ||
| 637 | } | ||
| 638 | } | ||
| 639 | } | ||
| 640 | } | ||
| 641 | } | ||
| 642 | |||
| 643 | /// <summary> | ||
| 644 | /// Copy file data between transform substorages and the patch output object | ||
| 645 | /// </summary> | ||
| 646 | /// <param name="output">The output to bind.</param> | ||
| 647 | /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param> | ||
| 648 | private IEnumerable<FileFacade> CopyFromTransformData(Output output) | ||
| 649 | { | ||
| 650 | var command = new CopyTransformDataCommand(); | ||
| 651 | command.CopyOutFileRows = true; | ||
| 652 | command.Output = output; | ||
| 653 | command.TableDefinitions = this.TableDefinitions; | ||
| 654 | command.Execute(); | ||
| 655 | |||
| 656 | return command.FileFacades; | ||
| 657 | } | ||
| 658 | |||
| 659 | /// <summary> | ||
| 660 | /// Copy file data between transform substorages and the patch output object | ||
| 661 | /// </summary> | ||
| 662 | /// <param name="output">The output to bind.</param> | ||
| 663 | /// <param name="allFileRows">True if copying from transform to patch, false the other way.</param> | ||
| 664 | private void CopyToTransformData(Output output) | ||
| 665 | { | ||
| 666 | var command = new CopyTransformDataCommand(); | ||
| 667 | command.CopyOutFileRows = false; | ||
| 668 | command.Output = output; | ||
| 669 | command.TableDefinitions = this.TableDefinitions; | ||
| 670 | command.Execute(); | ||
| 671 | } | ||
| 672 | |||
| 673 | private void UpdateMediaSequences(OutputType outputType, IEnumerable<FileFacade> fileFacades, RowDictionary<MediaRow> mediaRows) | ||
| 674 | { | ||
| 675 | // Calculate sequence numbers and media disk id layout for all file media information objects. | ||
| 676 | if (OutputType.Module == outputType) | ||
| 677 | { | ||
| 678 | int lastSequence = 0; | ||
| 679 | foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI. | ||
| 680 | { | ||
| 681 | facade.File.Sequence = ++lastSequence; | ||
| 682 | } | ||
| 683 | } | ||
| 684 | else | ||
| 685 | { | ||
| 686 | int lastSequence = 0; | ||
| 687 | MediaRow mediaRow = null; | ||
| 688 | Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>(); | ||
| 689 | |||
| 690 | // sequence the non-patch-added files | ||
| 691 | foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI. | ||
| 692 | { | ||
| 693 | if (null == mediaRow) | ||
| 694 | { | ||
| 695 | mediaRow = mediaRows.Get(facade.WixFile.DiskId); | ||
| 696 | if (OutputType.Patch == outputType) | ||
| 697 | { | ||
| 698 | // patch Media cannot start at zero | ||
| 699 | lastSequence = mediaRow.LastSequence; | ||
| 700 | } | ||
| 701 | } | ||
| 702 | else if (mediaRow.DiskId != facade.WixFile.DiskId) | ||
| 703 | { | ||
| 704 | mediaRow.LastSequence = lastSequence; | ||
| 705 | mediaRow = mediaRows.Get(facade.WixFile.DiskId); | ||
| 706 | } | ||
| 707 | |||
| 708 | if (0 < facade.WixFile.PatchGroup) | ||
| 709 | { | ||
| 710 | List<FileFacade> patchGroup = patchGroups[facade.WixFile.PatchGroup]; | ||
| 711 | |||
| 712 | if (null == patchGroup) | ||
| 713 | { | ||
| 714 | patchGroup = new List<FileFacade>(); | ||
| 715 | patchGroups.Add(facade.WixFile.PatchGroup, patchGroup); | ||
| 716 | } | ||
| 717 | |||
| 718 | patchGroup.Add(facade); | ||
| 719 | } | ||
| 720 | else | ||
| 721 | { | ||
| 722 | facade.File.Sequence = ++lastSequence; | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | if (null != mediaRow) | ||
| 727 | { | ||
| 728 | mediaRow.LastSequence = lastSequence; | ||
| 729 | mediaRow = null; | ||
| 730 | } | ||
| 731 | |||
| 732 | // sequence the patch-added files | ||
| 733 | foreach (List<FileFacade> patchGroup in patchGroups.Values) | ||
| 734 | { | ||
| 735 | foreach (FileFacade facade in patchGroup) | ||
| 736 | { | ||
| 737 | if (null == mediaRow) | ||
| 738 | { | ||
| 739 | mediaRow = mediaRows.Get(facade.WixFile.DiskId); | ||
| 740 | } | ||
| 741 | else if (mediaRow.DiskId != facade.WixFile.DiskId) | ||
| 742 | { | ||
| 743 | mediaRow.LastSequence = lastSequence; | ||
| 744 | mediaRow = mediaRows.Get(facade.WixFile.DiskId); | ||
| 745 | } | ||
| 746 | |||
| 747 | facade.File.Sequence = ++lastSequence; | ||
| 748 | } | ||
| 749 | } | ||
| 750 | |||
| 751 | if (null != mediaRow) | ||
| 752 | { | ||
| 753 | mediaRow.LastSequence = lastSequence; | ||
| 754 | } | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | /// <summary> | ||
| 759 | /// Set the guids for components with generatable guids. | ||
| 760 | /// </summary> | ||
| 761 | /// <param name="output">Internal representation of the database to operate on.</param> | ||
| 762 | private void SetComponentGuids(Output output) | ||
| 763 | { | ||
| 764 | Table componentTable = output.Tables["Component"]; | ||
| 765 | if (null != componentTable) | ||
| 766 | { | ||
| 767 | Hashtable registryKeyRows = null; | ||
| 768 | Hashtable directories = null; | ||
| 769 | Hashtable componentIdGenSeeds = null; | ||
| 770 | Dictionary<string, List<FileRow>> fileRows = null; | ||
| 771 | |||
| 772 | // find components with generatable guids | ||
| 773 | foreach (ComponentRow componentRow in componentTable.Rows) | ||
| 774 | { | ||
| 775 | // component guid will be generated | ||
| 776 | if ("*" == componentRow.Guid) | ||
| 777 | { | ||
| 778 | if (null == componentRow.KeyPath || componentRow.IsOdbcDataSourceKeyPath) | ||
| 779 | { | ||
| 780 | Messaging.Instance.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(componentRow.SourceLineNumbers)); | ||
| 781 | } | ||
| 782 | else if (componentRow.IsRegistryKeyPath) | ||
| 783 | { | ||
| 784 | if (null == registryKeyRows) | ||
| 785 | { | ||
| 786 | Table registryTable = output.Tables["Registry"]; | ||
| 787 | |||
| 788 | registryKeyRows = new Hashtable(registryTable.Rows.Count); | ||
| 789 | |||
| 790 | foreach (Row registryRow in registryTable.Rows) | ||
| 791 | { | ||
| 792 | registryKeyRows.Add((string)registryRow[0], registryRow); | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | Row foundRow = registryKeyRows[componentRow.KeyPath] as Row; | ||
| 797 | |||
| 798 | string bitness = componentRow.Is64Bit ? "64" : String.Empty; | ||
| 799 | if (null != foundRow) | ||
| 800 | { | ||
| 801 | string regkey = String.Concat(bitness, foundRow[1], "\\", foundRow[2], "\\", foundRow[3]); | ||
| 802 | componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, regkey.ToLowerInvariant()).ToString("B").ToUpperInvariant(); | ||
| 803 | } | ||
| 804 | } | ||
| 805 | else // must be a File KeyPath | ||
| 806 | { | ||
| 807 | // if the directory table hasn't been loaded into an indexed hash | ||
| 808 | // of directory ids to target names do that now. | ||
| 809 | if (null == directories) | ||
| 810 | { | ||
| 811 | Table directoryTable = output.Tables["Directory"]; | ||
| 812 | |||
| 813 | int numDirectoryTableRows = (null != directoryTable) ? directoryTable.Rows.Count : 0; | ||
| 814 | |||
| 815 | directories = new Hashtable(numDirectoryTableRows); | ||
| 816 | |||
| 817 | // get the target paths for all directories | ||
| 818 | if (null != directoryTable) | ||
| 819 | { | ||
| 820 | foreach (Row row in directoryTable.Rows) | ||
| 821 | { | ||
| 822 | // if the directory Id already exists, we will skip it here since | ||
| 823 | // checking for duplicate primary keys is done later when importing tables | ||
| 824 | // into database | ||
| 825 | if (directories.ContainsKey(row[0])) | ||
| 826 | { | ||
| 827 | continue; | ||
| 828 | } | ||
| 829 | |||
| 830 | string targetName = Common.GetName((string)row[2], false, true); | ||
| 831 | directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName)); | ||
| 832 | } | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 836 | // if the component id generation seeds have not been indexed | ||
| 837 | // from the WixDirectory table do that now. | ||
| 838 | if (null == componentIdGenSeeds) | ||
| 839 | { | ||
| 840 | Table wixDirectoryTable = output.Tables["WixDirectory"]; | ||
| 841 | |||
| 842 | int numWixDirectoryRows = (null != wixDirectoryTable) ? wixDirectoryTable.Rows.Count : 0; | ||
| 843 | |||
| 844 | componentIdGenSeeds = new Hashtable(numWixDirectoryRows); | ||
| 845 | |||
| 846 | // if there are any WixDirectory rows, build up the Component Guid | ||
| 847 | // generation seeds indexed by Directory/@Id. | ||
| 848 | if (null != wixDirectoryTable) | ||
| 849 | { | ||
| 850 | foreach (Row row in wixDirectoryTable.Rows) | ||
| 851 | { | ||
| 852 | componentIdGenSeeds.Add(row[0], (string)row[1]); | ||
| 853 | } | ||
| 854 | } | ||
| 855 | } | ||
| 856 | |||
| 857 | // if the file rows have not been indexed by File.Component yet | ||
| 858 | // then do that now | ||
| 859 | if (null == fileRows) | ||
| 860 | { | ||
| 861 | Table fileTable = output.Tables["File"]; | ||
| 862 | |||
| 863 | int numFileRows = (null != fileTable) ? fileTable.Rows.Count : 0; | ||
| 864 | |||
| 865 | fileRows = new Dictionary<string, List<FileRow>>(numFileRows); | ||
| 866 | |||
| 867 | if (null != fileTable) | ||
| 868 | { | ||
| 869 | foreach (FileRow file in fileTable.Rows) | ||
| 870 | { | ||
| 871 | List<FileRow> files; | ||
| 872 | if (!fileRows.TryGetValue(file.Component, out files)) | ||
| 873 | { | ||
| 874 | files = new List<FileRow>(); | ||
| 875 | fileRows.Add(file.Component, files); | ||
| 876 | } | ||
| 877 | |||
| 878 | files.Add(file); | ||
| 879 | } | ||
| 880 | } | ||
| 881 | } | ||
| 882 | |||
| 883 | // validate component meets all the conditions to have a generated guid | ||
| 884 | List<FileRow> currentComponentFiles = fileRows[componentRow.Component]; | ||
| 885 | int numFilesInComponent = currentComponentFiles.Count; | ||
| 886 | string path = null; | ||
| 887 | |||
| 888 | foreach (FileRow fileRow in currentComponentFiles) | ||
| 889 | { | ||
| 890 | if (fileRow.File == componentRow.KeyPath) | ||
| 891 | { | ||
| 892 | // calculate the key file's canonical target path | ||
| 893 | string directoryPath = Binder.GetDirectoryPath(directories, componentIdGenSeeds, componentRow.Directory, true); | ||
| 894 | string fileName = Common.GetName(fileRow.FileName, false, true).ToLower(CultureInfo.InvariantCulture); | ||
| 895 | path = Path.Combine(directoryPath, fileName); | ||
| 896 | |||
| 897 | // find paths that are not canonicalized | ||
| 898 | if (path.StartsWith(@"PersonalFolder\my pictures", StringComparison.Ordinal) || | ||
| 899 | path.StartsWith(@"ProgramFilesFolder\common files", StringComparison.Ordinal) || | ||
| 900 | path.StartsWith(@"ProgramMenuFolder\startup", StringComparison.Ordinal) || | ||
| 901 | path.StartsWith("TARGETDIR", StringComparison.Ordinal) || | ||
| 902 | path.StartsWith(@"StartMenuFolder\programs", StringComparison.Ordinal) || | ||
| 903 | path.StartsWith(@"WindowsFolder\fonts", StringComparison.Ordinal)) | ||
| 904 | { | ||
| 905 | Messaging.Instance.OnMessage(WixErrors.IllegalPathForGeneratedComponentGuid(componentRow.SourceLineNumbers, fileRow.Component, path)); | ||
| 906 | } | ||
| 907 | |||
| 908 | // if component has more than one file, the key path must be versioned | ||
| 909 | if (1 < numFilesInComponent && String.IsNullOrEmpty(fileRow.Version)) | ||
| 910 | { | ||
| 911 | Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentUnversionedKeypath(componentRow.SourceLineNumbers)); | ||
| 912 | } | ||
| 913 | } | ||
| 914 | else | ||
| 915 | { | ||
| 916 | // not a key path, so it must be an unversioned file if component has more than one file | ||
| 917 | if (1 < numFilesInComponent && !String.IsNullOrEmpty(fileRow.Version)) | ||
| 918 | { | ||
| 919 | Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentVersionedNonkeypath(componentRow.SourceLineNumbers)); | ||
| 920 | } | ||
| 921 | } | ||
| 922 | } | ||
| 923 | |||
| 924 | // if the rules were followed, reward with a generated guid | ||
| 925 | if (!Messaging.Instance.EncounteredError) | ||
| 926 | { | ||
| 927 | componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, path).ToString("B").ToUpperInvariant(); | ||
| 928 | } | ||
| 929 | } | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | /// <summary> | ||
| 936 | /// Creates instance transform substorages in the output. | ||
| 937 | /// </summary> | ||
| 938 | /// <param name="output">Output containing instance transform definitions.</param> | ||
| 939 | private void CreateInstanceTransforms(Output output) | ||
| 940 | { | ||
| 941 | // Create and add substorages for instance transforms. | ||
| 942 | Table wixInstanceTransformsTable = output.Tables["WixInstanceTransforms"]; | ||
| 943 | if (null != wixInstanceTransformsTable && 0 <= wixInstanceTransformsTable.Rows.Count) | ||
| 944 | { | ||
| 945 | string targetProductCode = null; | ||
| 946 | string targetUpgradeCode = null; | ||
| 947 | string targetProductVersion = null; | ||
| 948 | |||
| 949 | Table targetSummaryInformationTable = output.Tables["_SummaryInformation"]; | ||
| 950 | Table targetPropertyTable = output.Tables["Property"]; | ||
| 951 | |||
| 952 | // Get the data from target database | ||
| 953 | foreach (Row propertyRow in targetPropertyTable.Rows) | ||
| 954 | { | ||
| 955 | if ("ProductCode" == (string)propertyRow[0]) | ||
| 956 | { | ||
| 957 | targetProductCode = (string)propertyRow[1]; | ||
| 958 | } | ||
| 959 | else if ("ProductVersion" == (string)propertyRow[0]) | ||
| 960 | { | ||
| 961 | targetProductVersion = (string)propertyRow[1]; | ||
| 962 | } | ||
| 963 | else if ("UpgradeCode" == (string)propertyRow[0]) | ||
| 964 | { | ||
| 965 | targetUpgradeCode = (string)propertyRow[1]; | ||
| 966 | } | ||
| 967 | } | ||
| 968 | |||
| 969 | // Index the Instance Component Rows. | ||
| 970 | Dictionary<string, ComponentRow> instanceComponentGuids = new Dictionary<string, ComponentRow>(); | ||
| 971 | Table targetInstanceComponentTable = output.Tables["WixInstanceComponent"]; | ||
| 972 | if (null != targetInstanceComponentTable && 0 < targetInstanceComponentTable.Rows.Count) | ||
| 973 | { | ||
| 974 | foreach (Row row in targetInstanceComponentTable.Rows) | ||
| 975 | { | ||
| 976 | // Build up all the instances, we'll get the Components rows from the real Component table. | ||
| 977 | instanceComponentGuids.Add((string)row[0], null); | ||
| 978 | } | ||
| 979 | |||
| 980 | Table targetComponentTable = output.Tables["Component"]; | ||
| 981 | foreach (ComponentRow componentRow in targetComponentTable.Rows) | ||
| 982 | { | ||
| 983 | string component = (string)componentRow[0]; | ||
| 984 | if (instanceComponentGuids.ContainsKey(component)) | ||
| 985 | { | ||
| 986 | instanceComponentGuids[component] = componentRow; | ||
| 987 | } | ||
| 988 | } | ||
| 989 | } | ||
| 990 | |||
| 991 | // Generate the instance transforms | ||
| 992 | foreach (Row instanceRow in wixInstanceTransformsTable.Rows) | ||
| 993 | { | ||
| 994 | string instanceId = (string)instanceRow[0]; | ||
| 995 | |||
| 996 | Output instanceTransform = new Output(instanceRow.SourceLineNumbers); | ||
| 997 | instanceTransform.Type = OutputType.Transform; | ||
| 998 | instanceTransform.Codepage = output.Codepage; | ||
| 999 | |||
| 1000 | Table instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | ||
| 1001 | string targetPlatformAndLanguage = null; | ||
| 1002 | |||
| 1003 | foreach (Row summaryInformationRow in targetSummaryInformationTable.Rows) | ||
| 1004 | { | ||
| 1005 | if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE | ||
| 1006 | { | ||
| 1007 | targetPlatformAndLanguage = (string)summaryInformationRow[1]; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | // Copy the row's data to the transform. | ||
| 1011 | Row copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(null); | ||
| 1012 | copyOfSummaryRow[0] = summaryInformationRow[0]; | ||
| 1013 | copyOfSummaryRow[1] = summaryInformationRow[1]; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | // Modify the appropriate properties. | ||
| 1017 | Table propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]); | ||
| 1018 | |||
| 1019 | // Change the ProductCode property | ||
| 1020 | string productCode = (string)instanceRow[2]; | ||
| 1021 | if ("*" == productCode) | ||
| 1022 | { | ||
| 1023 | productCode = Common.GenerateGuid(); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | Row productCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); | ||
| 1027 | productCodeRow.Operation = RowOperation.Modify; | ||
| 1028 | productCodeRow.Fields[1].Modified = true; | ||
| 1029 | productCodeRow[0] = "ProductCode"; | ||
| 1030 | productCodeRow[1] = productCode; | ||
| 1031 | |||
| 1032 | // Change the instance property | ||
| 1033 | Row instanceIdRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); | ||
| 1034 | instanceIdRow.Operation = RowOperation.Modify; | ||
| 1035 | instanceIdRow.Fields[1].Modified = true; | ||
| 1036 | instanceIdRow[0] = (string)instanceRow[1]; | ||
| 1037 | instanceIdRow[1] = instanceId; | ||
| 1038 | |||
| 1039 | if (null != instanceRow[3]) | ||
| 1040 | { | ||
| 1041 | // Change the ProductName property | ||
| 1042 | Row productNameRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); | ||
| 1043 | productNameRow.Operation = RowOperation.Modify; | ||
| 1044 | productNameRow.Fields[1].Modified = true; | ||
| 1045 | productNameRow[0] = "ProductName"; | ||
| 1046 | productNameRow[1] = (string)instanceRow[3]; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | if (null != instanceRow[4]) | ||
| 1050 | { | ||
| 1051 | // Change the UpgradeCode property | ||
| 1052 | Row upgradeCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); | ||
| 1053 | upgradeCodeRow.Operation = RowOperation.Modify; | ||
| 1054 | upgradeCodeRow.Fields[1].Modified = true; | ||
| 1055 | upgradeCodeRow[0] = "UpgradeCode"; | ||
| 1056 | upgradeCodeRow[1] = instanceRow[4]; | ||
| 1057 | |||
| 1058 | // Change the Upgrade table | ||
| 1059 | Table targetUpgradeTable = output.Tables["Upgrade"]; | ||
| 1060 | if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count) | ||
| 1061 | { | ||
| 1062 | string upgradeId = (string)instanceRow[4]; | ||
| 1063 | Table upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]); | ||
| 1064 | foreach (Row row in targetUpgradeTable.Rows) | ||
| 1065 | { | ||
| 1066 | // In case they are upgrading other codes to this new product, leave the ones that don't match the | ||
| 1067 | // Product.UpgradeCode intact. | ||
| 1068 | if (targetUpgradeCode == (string)row[0]) | ||
| 1069 | { | ||
| 1070 | Row upgradeRow = upgradeTable.CreateRow(null); | ||
| 1071 | upgradeRow.Operation = RowOperation.Add; | ||
| 1072 | upgradeRow.Fields[0].Modified = true; | ||
| 1073 | // I was hoping to be able to RowOperation.Modify, but that didn't appear to function. | ||
| 1074 | // upgradeRow.Fields[0].PreviousData = (string)row[0]; | ||
| 1075 | |||
| 1076 | // Inserting a new Upgrade record with the updated UpgradeCode | ||
| 1077 | upgradeRow[0] = upgradeId; | ||
| 1078 | upgradeRow[1] = row[1]; | ||
| 1079 | upgradeRow[2] = row[2]; | ||
| 1080 | upgradeRow[3] = row[3]; | ||
| 1081 | upgradeRow[4] = row[4]; | ||
| 1082 | upgradeRow[5] = row[5]; | ||
| 1083 | upgradeRow[6] = row[6]; | ||
| 1084 | |||
| 1085 | // Delete the old row | ||
| 1086 | Row upgradeRemoveRow = upgradeTable.CreateRow(null); | ||
| 1087 | upgradeRemoveRow.Operation = RowOperation.Delete; | ||
| 1088 | upgradeRemoveRow[0] = row[0]; | ||
| 1089 | upgradeRemoveRow[1] = row[1]; | ||
| 1090 | upgradeRemoveRow[2] = row[2]; | ||
| 1091 | upgradeRemoveRow[3] = row[3]; | ||
| 1092 | upgradeRemoveRow[4] = row[4]; | ||
| 1093 | upgradeRemoveRow[5] = row[5]; | ||
| 1094 | upgradeRemoveRow[6] = row[6]; | ||
| 1095 | } | ||
| 1096 | } | ||
| 1097 | } | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | // If there are instance Components generate new GUIDs for them. | ||
| 1101 | if (0 < instanceComponentGuids.Count) | ||
| 1102 | { | ||
| 1103 | Table componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); | ||
| 1104 | foreach (ComponentRow targetComponentRow in instanceComponentGuids.Values) | ||
| 1105 | { | ||
| 1106 | string guid = targetComponentRow.Guid; | ||
| 1107 | if (!String.IsNullOrEmpty(guid)) | ||
| 1108 | { | ||
| 1109 | Row instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers); | ||
| 1110 | instanceComponentRow.Operation = RowOperation.Modify; | ||
| 1111 | instanceComponentRow.Fields[1].Modified = true; | ||
| 1112 | instanceComponentRow[0] = targetComponentRow[0]; | ||
| 1113 | instanceComponentRow[1] = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)).ToString("B").ToUpper(CultureInfo.InvariantCulture); | ||
| 1114 | instanceComponentRow[2] = targetComponentRow[2]; | ||
| 1115 | instanceComponentRow[3] = targetComponentRow[3]; | ||
| 1116 | instanceComponentRow[4] = targetComponentRow[4]; | ||
| 1117 | instanceComponentRow[5] = targetComponentRow[5]; | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | // Update the summary information | ||
| 1123 | Hashtable summaryRows = new Hashtable(instanceSummaryInformationTable.Rows.Count); | ||
| 1124 | foreach (Row row in instanceSummaryInformationTable.Rows) | ||
| 1125 | { | ||
| 1126 | summaryRows[row[0]] = row; | ||
| 1127 | |||
| 1128 | if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) | ||
| 1129 | { | ||
| 1130 | row[1] = targetPlatformAndLanguage; | ||
| 1131 | } | ||
| 1132 | else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) | ||
| 1133 | { | ||
| 1134 | row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode); | ||
| 1135 | } | ||
| 1136 | else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) | ||
| 1137 | { | ||
| 1138 | row[1] = 0; | ||
| 1139 | } | ||
| 1140 | else if ((int)SummaryInformation.Transform.Security == (int)row[0]) | ||
| 1141 | { | ||
| 1142 | row[1] = "4"; | ||
| 1143 | } | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) | ||
| 1147 | { | ||
| 1148 | Row summaryRow = instanceSummaryInformationTable.CreateRow(null); | ||
| 1149 | summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; | ||
| 1150 | summaryRow[1] = targetPlatformAndLanguage; | ||
| 1151 | } | ||
| 1152 | else if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags)) | ||
| 1153 | { | ||
| 1154 | Row summaryRow = instanceSummaryInformationTable.CreateRow(null); | ||
| 1155 | summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; | ||
| 1156 | summaryRow[1] = "0"; | ||
| 1157 | } | ||
| 1158 | else if (!summaryRows.Contains((int)SummaryInformation.Transform.Security)) | ||
| 1159 | { | ||
| 1160 | Row summaryRow = instanceSummaryInformationTable.CreateRow(null); | ||
| 1161 | summaryRow[0] = (int)SummaryInformation.Transform.Security; | ||
| 1162 | summaryRow[1] = "4"; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | output.SubStorages.Add(new SubStorage(instanceId, instanceTransform)); | ||
| 1166 | } | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | /// <summary> | ||
| 1171 | /// Validate that there are no duplicate GUIDs in the output. | ||
| 1172 | /// </summary> | ||
| 1173 | /// <remarks> | ||
| 1174 | /// Duplicate GUIDs without conditions are an error condition; with conditions, it's a | ||
| 1175 | /// warning, as the conditions might be mutually exclusive. | ||
| 1176 | /// </remarks> | ||
| 1177 | private void ValidateComponentGuids(Output output) | ||
| 1178 | { | ||
| 1179 | Table componentTable = output.Tables["Component"]; | ||
| 1180 | if (null != componentTable) | ||
| 1181 | { | ||
| 1182 | Dictionary<string, bool> componentGuidConditions = new Dictionary<string, bool>(componentTable.Rows.Count); | ||
| 1183 | |||
| 1184 | foreach (ComponentRow row in componentTable.Rows) | ||
| 1185 | { | ||
| 1186 | // we don't care about unmanaged components and if there's a * GUID remaining, | ||
| 1187 | // there's already an error that prevented it from being replaced with a real GUID. | ||
| 1188 | if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid) | ||
| 1189 | { | ||
| 1190 | bool thisComponentHasCondition = !String.IsNullOrEmpty(row.Condition); | ||
| 1191 | bool allComponentsHaveConditions = thisComponentHasCondition; | ||
| 1192 | |||
| 1193 | if (componentGuidConditions.ContainsKey(row.Guid)) | ||
| 1194 | { | ||
| 1195 | allComponentsHaveConditions = componentGuidConditions[row.Guid] && thisComponentHasCondition; | ||
| 1196 | |||
| 1197 | if (allComponentsHaveConditions) | ||
| 1198 | { | ||
| 1199 | Messaging.Instance.OnMessage(WixWarnings.DuplicateComponentGuidsMustHaveMutuallyExclusiveConditions(row.SourceLineNumbers, row.Component, row.Guid)); | ||
| 1200 | } | ||
| 1201 | else | ||
| 1202 | { | ||
| 1203 | Messaging.Instance.OnMessage(WixErrors.DuplicateComponentGuids(row.SourceLineNumbers, row.Component, row.Guid)); | ||
| 1204 | } | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | componentGuidConditions[row.Guid] = allComponentsHaveConditions; | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | } | ||
| 1211 | } | ||
| 1212 | |||
| 1213 | /// <summary> | ||
| 1214 | /// Update Control and BBControl text by reading from files when necessary. | ||
| 1215 | /// </summary> | ||
| 1216 | /// <param name="output">Internal representation of the msi database to operate upon.</param> | ||
| 1217 | private void UpdateControlText(Output output) | ||
| 1218 | { | ||
| 1219 | UpdateControlTextCommand command = new UpdateControlTextCommand(); | ||
| 1220 | command.BBControlTable = output.Tables["BBControl"]; | ||
| 1221 | command.WixBBControlTable = output.Tables["WixBBControl"]; | ||
| 1222 | command.ControlTable = output.Tables["Control"]; | ||
| 1223 | command.WixControlTable = output.Tables["WixControl"]; | ||
| 1224 | command.Execute(); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | private string ResolveMedia(MediaRow mediaRow, string mediaLayoutDirectory, string layoutDirectory) | ||
| 1228 | { | ||
| 1229 | string layout = null; | ||
| 1230 | |||
| 1231 | foreach (var extension in this.BackendExtensions) | ||
| 1232 | { | ||
| 1233 | layout = extension.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory); | ||
| 1234 | if (!String.IsNullOrEmpty(layout)) | ||
| 1235 | { | ||
| 1236 | break; | ||
| 1237 | } | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | // If no binder file manager resolved the layout, do the default behavior. | ||
| 1241 | if (String.IsNullOrEmpty(layout)) | ||
| 1242 | { | ||
| 1243 | if (String.IsNullOrEmpty(mediaLayoutDirectory)) | ||
| 1244 | { | ||
| 1245 | layout = layoutDirectory; | ||
| 1246 | } | ||
| 1247 | else if (Path.IsPathRooted(mediaLayoutDirectory)) | ||
| 1248 | { | ||
| 1249 | layout = mediaLayoutDirectory; | ||
| 1250 | } | ||
| 1251 | else | ||
| 1252 | { | ||
| 1253 | layout = Path.Combine(layoutDirectory, mediaLayoutDirectory); | ||
| 1254 | } | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | return layout; | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /// <summary> | ||
| 1261 | /// Creates the MSI/MSM/PCP database. | ||
| 1262 | /// </summary> | ||
| 1263 | /// <param name="output">Output to create database for.</param> | ||
| 1264 | /// <param name="databaseFile">The database file to create.</param> | ||
| 1265 | /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param> | ||
| 1266 | /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> | ||
| 1267 | private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory) | ||
| 1268 | { | ||
| 1269 | var command = new GenerateDatabaseCommand(); | ||
| 1270 | command.Extensions = this.Extensions; | ||
| 1271 | command.Output = output; | ||
| 1272 | command.OutputPath = databaseFile; | ||
| 1273 | command.KeepAddedColumns = keepAddedColumns; | ||
| 1274 | command.UseSubDirectory = useSubdirectory; | ||
| 1275 | command.SuppressAddingValidationRows = this.SuppressAddingValidationRows; | ||
| 1276 | command.TableDefinitions = this.TableDefinitions; | ||
| 1277 | command.TempFilesLocation = this.IntermediateFolder; | ||
| 1278 | command.Codepage = this.Codepage; | ||
| 1279 | command.Execute(); | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | } | ||
