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 | } | ||