aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Linker.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Linker.cs')
-rw-r--r--src/WixToolset.Core/Linker.cs2434
1 files changed, 2434 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs
new file mode 100644
index 00000000..166894b9
--- /dev/null
+++ b/src/WixToolset.Core/Linker.cs
@@ -0,0 +1,2434 @@
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
3namespace WixToolset
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Collections.Specialized;
9 using System.Diagnostics;
10 using System.Diagnostics.CodeAnalysis;
11 using System.Globalization;
12 using System.Linq;
13 using System.Text;
14 using WixToolset.Data;
15 using WixToolset.Data.Rows;
16 using WixToolset.Extensibility;
17 using WixToolset.Link;
18 using WixToolset.Core.Native;
19
20 /// <summary>
21 /// Linker core of the WiX toolset.
22 /// </summary>
23 public sealed class Linker : IMessageHandler
24 {
25 private static readonly char[] colonCharacter = ":".ToCharArray();
26 private static readonly string emptyGuid = Guid.Empty.ToString("B");
27
28 private List<IExtensionData> extensionData;
29
30 private List<InspectorExtension> inspectorExtensions;
31 private bool sectionIdOnRows;
32 private WixActionRowCollection standardActions;
33 private Localizer localizer;
34 private Output activeOutput;
35 private TableDefinitionCollection tableDefinitions;
36
37 /// <summary>
38 /// Creates a linker.
39 /// </summary>
40 public Linker()
41 {
42 this.sectionIdOnRows = true; // TODO: what is the correct value for this?
43
44 this.standardActions = WindowsInstallerStandard.GetStandardActions();
45 this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
46
47 this.extensionData = new List<IExtensionData>();
48 this.inspectorExtensions = new List<InspectorExtension>();
49 }
50
51 /// <summary>
52 /// Gets or sets the localizer.
53 /// </summary>
54 /// <value>The localizer.</value>
55 public Localizer Localizer
56 {
57 get { return this.localizer; }
58 set { this.localizer = value; }
59 }
60
61 /// <summary>
62 /// Gets or sets the path to output unreferenced symbols to. If null or empty, there is no output.
63 /// </summary>
64 /// <value>The path to output the xml file.</value>
65 public string UnreferencedSymbolsFile { get; set; }
66
67 /// <summary>
68 /// Gets or sets the option to show pedantic messages.
69 /// </summary>
70 /// <value>The option to show pedantic messages.</value>
71 public bool ShowPedanticMessages { get; set; }
72
73 /// <summary>
74 /// Gets the table definitions used by the linker.
75 /// </summary>
76 /// <value>Table definitions used by the linker.</value>
77 public TableDefinitionCollection TableDefinitions
78 {
79 get { return this.tableDefinitions; }
80 }
81
82 /// <summary>
83 /// Gets or sets the Wix variable resolver.
84 /// </summary>
85 /// <value>The Wix variable resolver.</value>
86 public WixVariableResolver WixVariableResolver { get; set; }
87
88 /// <summary>
89 /// Adds an extension.
90 /// </summary>
91 /// <param name="extension">The extension to add.</param>
92 public void AddExtensionData(IExtensionData extension)
93 {
94 if (null != extension.TableDefinitions)
95 {
96 foreach (TableDefinition tableDefinition in extension.TableDefinitions)
97 {
98 if (!this.tableDefinitions.Contains(tableDefinition.Name))
99 {
100 this.tableDefinitions.Add(tableDefinition);
101 }
102 else
103 {
104 throw new WixException(WixErrors.DuplicateExtensionTable(extension.GetType().ToString(), tableDefinition.Name));
105 }
106 }
107 }
108
109 // keep track of extension data so the libraries can be loaded from these later once all the table definitions
110 // are loaded; this will allow extensions to have cross table definition dependencies
111 this.extensionData.Add(extension);
112 }
113
114 /// <summary>
115 /// Links a collection of sections into an output.
116 /// </summary>
117 /// <param name="inputs">The collection of sections to link together.</param>
118 /// <param name="expectedOutputType">Expected output type, based on output file extension provided to the linker.</param>
119 /// <returns>Output object from the linking.</returns>
120 public Output Link(IEnumerable<Section> inputs, OutputType expectedOutputType)
121 {
122 Output output = null;
123 List<Section> sections = new List<Section>(inputs);
124
125 try
126 {
127 bool containsModuleSubstitution = false;
128 bool containsModuleConfiguration = false;
129
130 this.activeOutput = null;
131
132 List<Row> actionRows = new List<Row>();
133 List<Row> suppressActionRows = new List<Row>();
134
135 TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection();
136 List<Row> customRows = new List<Row>();
137
138 StringCollection generatedShortFileNameIdentifiers = new StringCollection();
139 Hashtable generatedShortFileNames = new Hashtable();
140
141 Hashtable multipleFeatureComponents = new Hashtable();
142
143 Hashtable wixVariables = new Hashtable();
144
145 // verify that modularization types match for foreign key relationships
146 foreach (TableDefinition tableDefinition in this.tableDefinitions)
147 {
148 foreach (ColumnDefinition columnDefinition in tableDefinition.Columns)
149 {
150 if (null != columnDefinition.KeyTable && 0 > columnDefinition.KeyTable.IndexOf(';') && columnDefinition.IsKeyColumnSet)
151 {
152 try
153 {
154 TableDefinition keyTableDefinition = this.tableDefinitions[columnDefinition.KeyTable];
155
156 if (0 >= columnDefinition.KeyColumn || keyTableDefinition.Columns.Count < columnDefinition.KeyColumn)
157 {
158 this.OnMessage(WixErrors.InvalidKeyColumn(tableDefinition.Name, columnDefinition.Name, columnDefinition.KeyTable, columnDefinition.KeyColumn));
159 }
160 else if (keyTableDefinition.Columns[columnDefinition.KeyColumn - 1].ModularizeType != columnDefinition.ModularizeType && ColumnModularizeType.CompanionFile != columnDefinition.ModularizeType)
161 {
162 this.OnMessage(WixErrors.CollidingModularizationTypes(tableDefinition.Name, columnDefinition.Name, columnDefinition.KeyTable, columnDefinition.KeyColumn, columnDefinition.ModularizeType.ToString(), keyTableDefinition.Columns[columnDefinition.KeyColumn - 1].ModularizeType.ToString()));
163 }
164 }
165 catch (WixMissingTableDefinitionException)
166 {
167 // ignore missing table definitions - this error is caught in other places
168 }
169 }
170 }
171 }
172
173 // Add sections from the extensions with data.
174 foreach (IExtensionData data in this.extensionData)
175 {
176 Library library = data.GetLibrary(this.tableDefinitions);
177
178 if (null != library)
179 {
180 sections.AddRange(library.Sections);
181 }
182 }
183
184 // First find the entry section and while processing all sections load all the symbols from all of the sections.
185 // sections.FindEntrySectionAndLoadSymbols(false, this, expectedOutputType, out entrySection, out allSymbols);
186 FindEntrySectionAndLoadSymbolsCommand find = new FindEntrySectionAndLoadSymbolsCommand(sections);
187 find.ExpectedOutputType = expectedOutputType;
188
189 find.Execute();
190
191 // Must have found the entry section by now.
192 if (null == find.EntrySection)
193 {
194 throw new WixException(WixErrors.MissingEntrySection(expectedOutputType.ToString()));
195 }
196
197 IDictionary<string, Symbol> allSymbols = find.Symbols;
198
199 // Add the missing standard action symbols.
200 this.LoadStandardActionSymbols(allSymbols);
201
202 // now that we know where we're starting from, create the output object
203 output = new Output(null);
204 output.EntrySection = find.EntrySection; // Note: this entry section will get added to the Output.Sections collection later
205 if (null != this.localizer && -1 != this.localizer.Codepage)
206 {
207 output.Codepage = this.localizer.Codepage;
208 }
209 this.activeOutput = output;
210
211 // Resolve the symbol references to find the set of sections we care about for linking.
212 // Of course, we start with the entry section (that's how it got its name after all).
213 ResolveReferencesCommand resolve = new ResolveReferencesCommand(output.EntrySection, allSymbols);
214 resolve.BuildingMergeModule = (OutputType.Module == output.Type);
215
216 resolve.Execute();
217
218 if (Messaging.Instance.EncounteredError)
219 {
220 return null;
221 }
222
223 // Add the resolved sections to the output then flatten the complex
224 // references that particpate in groups.
225 foreach (Section section in resolve.ResolvedSections)
226 {
227 output.Sections.Add(section);
228 }
229
230 this.FlattenSectionsComplexReferences(output.Sections);
231
232 if (Messaging.Instance.EncounteredError)
233 {
234 return null;
235 }
236
237 // The hard part in linking is processing the complex references.
238 HashSet<string> referencedComponents = new HashSet<string>();
239 ConnectToFeatureCollection componentsToFeatures = new ConnectToFeatureCollection();
240 ConnectToFeatureCollection featuresToFeatures = new ConnectToFeatureCollection();
241 ConnectToFeatureCollection modulesToFeatures = new ConnectToFeatureCollection();
242 this.ProcessComplexReferences(output, output.Sections, referencedComponents, componentsToFeatures, featuresToFeatures, modulesToFeatures);
243
244 if (Messaging.Instance.EncounteredError)
245 {
246 return null;
247 }
248
249 // Display an error message for Components that were not referenced by a Feature.
250 foreach (Symbol symbol in resolve.ReferencedSymbols.Where(s => "Component".Equals(s.Row.TableDefinition.Name, StringComparison.Ordinal)))
251 {
252 if (!referencedComponents.Contains(symbol.Name))
253 {
254 this.OnMessage(WixErrors.OrphanedComponent(symbol.Row.SourceLineNumbers, (string)symbol.Row[0]));
255 }
256 }
257
258 // Report duplicates that would ultimately end up being primary key collisions.
259 ReportConflictingSymbolsCommand reportDupes = new ReportConflictingSymbolsCommand(find.PossiblyConflictingSymbols, resolve.ResolvedSections);
260 reportDupes.Execute();
261
262 if (Messaging.Instance.EncounteredError)
263 {
264 return null;
265 }
266
267 // resolve the feature to feature connects
268 this.ResolveFeatureToFeatureConnects(featuresToFeatures, allSymbols);
269
270 // start generating OutputTables and OutputRows for all the sections in the output
271 List<Row> ensureTableRows = new List<Row>();
272 int sectionCount = 0;
273 foreach (Section section in output.Sections)
274 {
275 sectionCount++;
276 string sectionId = section.Id;
277 if (null == sectionId && this.sectionIdOnRows)
278 {
279 sectionId = "wix.section." + sectionCount.ToString(CultureInfo.InvariantCulture);
280 }
281
282 foreach (Table table in section.Tables)
283 {
284 bool copyRows = true; // by default, copy rows.
285
286 // handle special tables
287 switch (table.Name)
288 {
289 case "AppSearch":
290 this.activeOutput.EnsureTable(this.tableDefinitions["Signature"]);
291 break;
292
293 case "Class":
294 if (OutputType.Product == output.Type)
295 {
296 this.ResolveFeatures(table.Rows, 2, 11, componentsToFeatures, multipleFeatureComponents);
297 }
298 break;
299
300 case "CustomAction":
301 if (OutputType.Module == this.activeOutput.Type)
302 {
303 this.activeOutput.EnsureTable(this.tableDefinitions["AdminExecuteSequence"]);
304 this.activeOutput.EnsureTable(this.tableDefinitions["AdminUISequence"]);
305 this.activeOutput.EnsureTable(this.tableDefinitions["AdvtExecuteSequence"]);
306 this.activeOutput.EnsureTable(this.tableDefinitions["InstallExecuteSequence"]);
307 this.activeOutput.EnsureTable(this.tableDefinitions["InstallUISequence"]);
308 }
309 break;
310
311 case "Dialog":
312 this.activeOutput.EnsureTable(this.tableDefinitions["ListBox"]);
313 break;
314
315 case "Directory":
316 foreach (Row row in table.Rows)
317 {
318 if (OutputType.Module == this.activeOutput.Type)
319 {
320 string directory = row[0].ToString();
321 if (WindowsInstallerStandard.IsStandardDirectory(directory))
322 {
323 // if the directory table contains references to standard windows folders
324 // mergemod.dll will add customactions to set the MSM directory to
325 // the same directory as the standard windows folder and will add references to
326 // custom action to all the standard sequence tables. A problem will occur
327 // if the MSI does not have these tables as mergemod.dll does not add these
328 // tables to the MSI if absent. This code adds the tables in case mergemod.dll
329 // needs them.
330 this.activeOutput.EnsureTable(this.tableDefinitions["CustomAction"]);
331 this.activeOutput.EnsureTable(this.tableDefinitions["AdminExecuteSequence"]);
332 this.activeOutput.EnsureTable(this.tableDefinitions["AdminUISequence"]);
333 this.activeOutput.EnsureTable(this.tableDefinitions["AdvtExecuteSequence"]);
334 this.activeOutput.EnsureTable(this.tableDefinitions["InstallExecuteSequence"]);
335 this.activeOutput.EnsureTable(this.tableDefinitions["InstallUISequence"]);
336 }
337 else
338 {
339 foreach (string standardDirectory in WindowsInstallerStandard.GetStandardDirectories())
340 {
341 if (directory.StartsWith(standardDirectory, StringComparison.Ordinal))
342 {
343 this.OnMessage(WixWarnings.StandardDirectoryConflictInMergeModule(row.SourceLineNumbers, directory, standardDirectory));
344 }
345 }
346 }
347 }
348 }
349 break;
350
351 case "Extension":
352 if (OutputType.Product == output.Type)
353 {
354 this.ResolveFeatures(table.Rows, 1, 4, componentsToFeatures, multipleFeatureComponents);
355 }
356 break;
357
358 case "ModuleSubstitution":
359 containsModuleSubstitution = true;
360 break;
361
362 case "ModuleConfiguration":
363 containsModuleConfiguration = true;
364 break;
365
366 case "MsiAssembly":
367 if (OutputType.Product == output.Type)
368 {
369 this.ResolveFeatures(table.Rows, 0, 1, componentsToFeatures, multipleFeatureComponents);
370 }
371 break;
372
373 case "ProgId":
374 // the Extension table is required with a ProgId table
375 this.activeOutput.EnsureTable(this.tableDefinitions["Extension"]);
376 break;
377
378 case "Property":
379 // Remove property rows with no value. These are properties associated with
380 // AppSearch but without a default value.
381 for (int i = 0; i < table.Rows.Count; i++)
382 {
383 if (null == table.Rows[i][1])
384 {
385 table.Rows.RemoveAt(i);
386 i--;
387 }
388 }
389 break;
390
391 case "PublishComponent":
392 if (OutputType.Product == output.Type)
393 {
394 this.ResolveFeatures(table.Rows, 2, 4, componentsToFeatures, multipleFeatureComponents);
395 }
396 break;
397
398 case "Shortcut":
399 if (OutputType.Product == output.Type)
400 {
401 this.ResolveFeatures(table.Rows, 3, 4, componentsToFeatures, multipleFeatureComponents);
402 }
403 break;
404
405 case "TypeLib":
406 if (OutputType.Product == output.Type)
407 {
408 this.ResolveFeatures(table.Rows, 2, 6, componentsToFeatures, multipleFeatureComponents);
409 }
410 break;
411
412 case "WixAction":
413 if (this.sectionIdOnRows)
414 {
415 foreach (Row row in table.Rows)
416 {
417 row.SectionId = sectionId;
418 }
419 }
420 actionRows.AddRange(table.Rows);
421 break;
422
423 case "WixCustomTable":
424 this.LinkCustomTable(table, customTableDefinitions);
425 copyRows = false; // we've created table definitions from these rows, no need to process them any longer
426 break;
427
428 case "WixCustomRow":
429 foreach (Row row in table.Rows)
430 {
431 row.SectionId = (this.sectionIdOnRows ? sectionId : null);
432 customRows.Add(row);
433 }
434 copyRows = false;
435 break;
436
437 case "WixEnsureTable":
438 ensureTableRows.AddRange(table.Rows);
439 break;
440
441 case "WixFile":
442 foreach (Row row in table.Rows)
443 {
444 // DiskId is not valid when creating a module, so set it to
445 // 0 for all files to ensure proper sorting in the binder
446 if (OutputType.Module == this.activeOutput.Type)
447 {
448 row[5] = 0;
449 }
450
451 // if the short file name was generated, check for collisions
452 if (0x1 == (int)row[9])
453 {
454 generatedShortFileNameIdentifiers.Add((string)row[0]);
455 }
456 }
457 break;
458
459 case "WixMerge":
460 if (OutputType.Product == output.Type)
461 {
462 this.ResolveFeatures(table.Rows, 0, 7, modulesToFeatures, null);
463 }
464 break;
465
466 case "WixSuppressAction":
467 suppressActionRows.AddRange(table.Rows);
468 break;
469
470 case "WixVariable":
471 // check for colliding values and collect the wix variable rows
472 foreach (WixVariableRow row in table.Rows)
473 {
474 WixVariableRow collidingRow = (WixVariableRow)wixVariables[row.Id];
475
476 if (null == collidingRow || (collidingRow.Overridable && !row.Overridable))
477 {
478 wixVariables[row.Id] = row;
479 }
480 else if (!row.Overridable || (collidingRow.Overridable && row.Overridable))
481 {
482 this.OnMessage(WixErrors.WixVariableCollision(row.SourceLineNumbers, row.Id));
483 }
484 }
485 copyRows = false;
486 break;
487 }
488
489 if (copyRows)
490 {
491 Table outputTable = this.activeOutput.EnsureTable(this.tableDefinitions[table.Name]);
492 this.CopyTableRowsToOutputTable(table, outputTable, sectionId);
493 }
494 }
495 }
496
497 // copy the module to feature connections into the output
498 if (0 < modulesToFeatures.Count)
499 {
500 Table wixFeatureModulesTable = this.activeOutput.EnsureTable(this.tableDefinitions["WixFeatureModules"]);
501
502 foreach (ConnectToFeature connectToFeature in modulesToFeatures)
503 {
504 foreach (string feature in connectToFeature.ConnectFeatures)
505 {
506 Row row = wixFeatureModulesTable.CreateRow(null);
507 row[0] = feature;
508 row[1] = connectToFeature.ChildId;
509 }
510 }
511 }
512
513 // ensure the creation of tables that need to exist
514 if (0 < ensureTableRows.Count)
515 {
516 foreach (Row row in ensureTableRows)
517 {
518 string tableId = (string)row[0];
519 TableDefinition tableDef = null;
520
521 try
522 {
523 tableDef = this.tableDefinitions[tableId];
524 }
525 catch (WixMissingTableDefinitionException)
526 {
527 tableDef = customTableDefinitions[tableId];
528 }
529
530 this.activeOutput.EnsureTable(tableDef);
531 }
532 }
533
534 // copy all the suppress action rows to the output to suppress actions from merge modules
535 if (0 < suppressActionRows.Count)
536 {
537 Table suppressActionTable = this.activeOutput.EnsureTable(this.tableDefinitions["WixSuppressAction"]);
538 suppressActionRows.ForEach(r => suppressActionTable.Rows.Add(r));
539 }
540
541 // sequence all the actions
542 this.SequenceActions(actionRows, suppressActionRows);
543
544 // check for missing table and add them or display an error as appropriate
545 switch (this.activeOutput.Type)
546 {
547 case OutputType.Module:
548 this.activeOutput.EnsureTable(this.tableDefinitions["Component"]);
549 this.activeOutput.EnsureTable(this.tableDefinitions["Directory"]);
550 this.activeOutput.EnsureTable(this.tableDefinitions["FeatureComponents"]);
551 this.activeOutput.EnsureTable(this.tableDefinitions["File"]);
552 this.activeOutput.EnsureTable(this.tableDefinitions["ModuleComponents"]);
553 this.activeOutput.EnsureTable(this.tableDefinitions["ModuleSignature"]);
554 break;
555 case OutputType.PatchCreation:
556 Table imageFamiliesTable = this.activeOutput.Tables["ImageFamilies"];
557 Table targetImagesTable = this.activeOutput.Tables["TargetImages"];
558 Table upgradedImagesTable = this.activeOutput.Tables["UpgradedImages"];
559
560 if (null == imageFamiliesTable || 1 > imageFamiliesTable.Rows.Count)
561 {
562 this.OnMessage(WixErrors.ExpectedRowInPatchCreationPackage("ImageFamilies"));
563 }
564
565 if (null == targetImagesTable || 1 > targetImagesTable.Rows.Count)
566 {
567 this.OnMessage(WixErrors.ExpectedRowInPatchCreationPackage("TargetImages"));
568 }
569
570 if (null == upgradedImagesTable || 1 > upgradedImagesTable.Rows.Count)
571 {
572 this.OnMessage(WixErrors.ExpectedRowInPatchCreationPackage("UpgradedImages"));
573 }
574
575 this.activeOutput.EnsureTable(this.tableDefinitions["Properties"]);
576 break;
577 case OutputType.Product:
578 this.activeOutput.EnsureTable(this.tableDefinitions["File"]);
579 this.activeOutput.EnsureTable(this.tableDefinitions["Media"]);
580 break;
581 }
582
583 this.CheckForIllegalTables(this.activeOutput);
584
585 // add the custom row data
586 foreach (Row row in customRows)
587 {
588 TableDefinition customTableDefinition = (TableDefinition)customTableDefinitions[row[0].ToString()];
589 Table customTable = this.activeOutput.EnsureTable(customTableDefinition);
590 Row customRow = customTable.CreateRow(row.SourceLineNumbers);
591
592 customRow.SectionId = row.SectionId;
593
594 string[] data = row[1].ToString().Split(Common.CustomRowFieldSeparator);
595
596 for (int i = 0; i < data.Length; ++i)
597 {
598 bool foundColumn = false;
599 string[] item = data[i].Split(colonCharacter, 2);
600
601 for (int j = 0; j < customRow.Fields.Length; ++j)
602 {
603 if (customRow.Fields[j].Column.Name == item[0])
604 {
605 if (0 < item[1].Length)
606 {
607 if (ColumnType.Number == customRow.Fields[j].Column.Type)
608 {
609 try
610 {
611 customRow.Fields[j].Data = Convert.ToInt32(item[1], CultureInfo.InvariantCulture);
612 }
613 catch (FormatException)
614 {
615 this.OnMessage(WixErrors.IllegalIntegerValue(row.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name, item[1]));
616 }
617 catch (OverflowException)
618 {
619 this.OnMessage(WixErrors.IllegalIntegerValue(row.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name, item[1]));
620 }
621 }
622 else if (ColumnCategory.Identifier == customRow.Fields[j].Column.Category)
623 {
624 if (Common.IsIdentifier(item[1]) || Common.IsValidBinderVariable(item[1]) || ColumnCategory.Formatted == customRow.Fields[j].Column.Category)
625 {
626 customRow.Fields[j].Data = item[1];
627 }
628 else
629 {
630 this.OnMessage(WixErrors.IllegalIdentifier(row.SourceLineNumbers, "Data", item[1]));
631 }
632 }
633 else
634 {
635 customRow.Fields[j].Data = item[1];
636 }
637 }
638 foundColumn = true;
639 break;
640 }
641 }
642
643 if (!foundColumn)
644 {
645 this.OnMessage(WixErrors.UnexpectedCustomTableColumn(row.SourceLineNumbers, item[0]));
646 }
647 }
648
649 for (int i = 0; i < customTableDefinition.Columns.Count; ++i)
650 {
651 if (!customTableDefinition.Columns[i].Nullable && (null == customRow.Fields[i].Data || 0 == customRow.Fields[i].Data.ToString().Length))
652 {
653 this.OnMessage(WixErrors.NoDataForColumn(row.SourceLineNumbers, customTableDefinition.Columns[i].Name, customTableDefinition.Name));
654 }
655 }
656 }
657
658 //correct the section Id in FeatureComponents table
659 if (this.sectionIdOnRows)
660 {
661 Hashtable componentSectionIds = new Hashtable();
662 Table componentTable = output.Tables["Component"];
663
664 if (null != componentTable)
665 {
666 foreach (Row componentRow in componentTable.Rows)
667 {
668 componentSectionIds.Add(componentRow.Fields[0].Data.ToString(), componentRow.SectionId);
669 }
670 }
671
672 Table featureComponentsTable = output.Tables["FeatureComponents"];
673
674 if (null != featureComponentsTable)
675 {
676 foreach (Row featureComponentsRow in featureComponentsTable.Rows)
677 {
678 if (componentSectionIds.Contains(featureComponentsRow.Fields[1].Data.ToString()))
679 {
680 featureComponentsRow.SectionId = (string)componentSectionIds[featureComponentsRow.Fields[1].Data.ToString()];
681 }
682 }
683 }
684 }
685
686 // add the ModuleSubstitution table to the ModuleIgnoreTable
687 if (containsModuleSubstitution)
688 {
689 Table moduleIgnoreTableTable = this.activeOutput.EnsureTable(this.tableDefinitions["ModuleIgnoreTable"]);
690
691 Row moduleIgnoreTableRow = moduleIgnoreTableTable.CreateRow(null);
692 moduleIgnoreTableRow[0] = "ModuleSubstitution";
693 }
694
695 // add the ModuleConfiguration table to the ModuleIgnoreTable
696 if (containsModuleConfiguration)
697 {
698 Table moduleIgnoreTableTable = this.activeOutput.EnsureTable(this.tableDefinitions["ModuleIgnoreTable"]);
699
700 Row moduleIgnoreTableRow = moduleIgnoreTableTable.CreateRow(null);
701 moduleIgnoreTableRow[0] = "ModuleConfiguration";
702 }
703
704 // index all the file rows
705 Table fileTable = this.activeOutput.Tables["File"];
706 RowDictionary<FileRow> indexedFileRows = (null == fileTable) ? new RowDictionary<FileRow>() : new RowDictionary<FileRow>(fileTable);
707
708 // flag all the generated short file name collisions
709 foreach (string fileId in generatedShortFileNameIdentifiers)
710 {
711 FileRow fileRow = indexedFileRows[fileId];
712
713 string[] names = fileRow.FileName.Split('|');
714 string shortFileName = names[0];
715
716 // create lists of conflicting generated short file names
717 if (!generatedShortFileNames.Contains(shortFileName))
718 {
719 generatedShortFileNames.Add(shortFileName, new ArrayList());
720 }
721 ((ArrayList)generatedShortFileNames[shortFileName]).Add(fileRow);
722 }
723
724 // check for generated short file name collisions
725 foreach (DictionaryEntry entry in generatedShortFileNames)
726 {
727 string shortFileName = (string)entry.Key;
728 ArrayList fileRows = (ArrayList)entry.Value;
729
730 if (1 < fileRows.Count)
731 {
732 // sort the rows by DiskId
733 fileRows.Sort();
734
735 this.OnMessage(WixWarnings.GeneratedShortFileNameConflict(((FileRow)fileRows[0]).SourceLineNumbers, shortFileName));
736
737 for (int i = 1; i < fileRows.Count; i++)
738 {
739 FileRow fileRow = (FileRow)fileRows[i];
740
741 if (null != fileRow.SourceLineNumbers)
742 {
743 this.OnMessage(WixWarnings.GeneratedShortFileNameConflict2(fileRow.SourceLineNumbers));
744 }
745 }
746 }
747 }
748
749 // copy the wix variable rows to the output after all overriding has been accounted for.
750 if (0 < wixVariables.Count)
751 {
752 Table wixVariableTable = output.EnsureTable(this.tableDefinitions["WixVariable"]);
753
754 foreach (WixVariableRow row in wixVariables.Values)
755 {
756 wixVariableTable.Rows.Add(row);
757 }
758 }
759
760 // Bundles have groups of data that must be flattened in a way different from other types.
761 this.FlattenBundleTables(output);
762
763 if (Messaging.Instance.EncounteredError)
764 {
765 return null;
766 }
767
768 this.CheckOutputConsistency(output);
769
770 // inspect the output
771 InspectorCore inspectorCore = new InspectorCore();
772 foreach (InspectorExtension inspectorExtension in this.inspectorExtensions)
773 {
774 inspectorExtension.Core = inspectorCore;
775 inspectorExtension.InspectOutput(output);
776
777 // reset
778 inspectorExtension.Core = null;
779 }
780 }
781 finally
782 {
783 this.activeOutput = null;
784 }
785
786 return Messaging.Instance.EncounteredError ? null : output;
787 }
788
789 /// <summary>
790 /// Links the definition of a custom table.
791 /// </summary>
792 /// <param name="table">The table to link.</param>
793 /// <param name="customTableDefinitions">Receives the linked definition of the custom table.</param>
794 private void LinkCustomTable(Table table, TableDefinitionCollection customTableDefinitions)
795 {
796 foreach (Row row in table.Rows)
797 {
798 bool bootstrapperApplicationData = (null != row[13] && 1 == (int)row[13]);
799
800 if (null == row[4])
801 {
802 this.OnMessage(WixErrors.ExpectedAttribute(row.SourceLineNumbers, "CustomTable/Column", "PrimaryKey"));
803 }
804
805 string[] columnNames = row[2].ToString().Split('\t');
806 string[] columnTypes = row[3].ToString().Split('\t');
807 string[] primaryKeys = row[4].ToString().Split('\t');
808 string[] minValues = row[5] == null ? null : row[5].ToString().Split('\t');
809 string[] maxValues = row[6] == null ? null : row[6].ToString().Split('\t');
810 string[] keyTables = row[7] == null ? null : row[7].ToString().Split('\t');
811 string[] keyColumns = row[8] == null ? null : row[8].ToString().Split('\t');
812 string[] categories = row[9] == null ? null : row[9].ToString().Split('\t');
813 string[] sets = row[10] == null ? null : row[10].ToString().Split('\t');
814 string[] descriptions = row[11] == null ? null : row[11].ToString().Split('\t');
815 string[] modularizations = row[12] == null ? null : row[12].ToString().Split('\t');
816
817 int currentPrimaryKey = 0;
818
819 List<ColumnDefinition> columns = new List<ColumnDefinition>(columnNames.Length);
820 for (int i = 0; i < columnNames.Length; ++i)
821 {
822 string name = columnNames[i];
823 ColumnType type = ColumnType.Unknown;
824
825 if (columnTypes[i].StartsWith("s", StringComparison.OrdinalIgnoreCase))
826 {
827 type = ColumnType.String;
828 }
829 else if (columnTypes[i].StartsWith("l", StringComparison.OrdinalIgnoreCase))
830 {
831 type = ColumnType.Localized;
832 }
833 else if (columnTypes[i].StartsWith("i", StringComparison.OrdinalIgnoreCase))
834 {
835 type = ColumnType.Number;
836 }
837 else if (columnTypes[i].StartsWith("v", StringComparison.OrdinalIgnoreCase))
838 {
839 type = ColumnType.Object;
840 }
841 else
842 {
843 throw new WixException(WixErrors.UnknownCustomTableColumnType(row.SourceLineNumbers, columnTypes[i]));
844 }
845
846 bool nullable = columnTypes[i].Substring(0, 1) == columnTypes[i].Substring(0, 1).ToUpper(CultureInfo.InvariantCulture);
847 int length = Convert.ToInt32(columnTypes[i].Substring(1), CultureInfo.InvariantCulture);
848
849 bool primaryKey = false;
850 if (currentPrimaryKey < primaryKeys.Length && primaryKeys[currentPrimaryKey] == columnNames[i])
851 {
852 primaryKey = true;
853 currentPrimaryKey++;
854 }
855
856 bool minValSet = null != minValues && null != minValues[i] && 0 < minValues[i].Length;
857 int minValue = 0;
858 if (minValSet)
859 {
860 minValue = Convert.ToInt32(minValues[i], CultureInfo.InvariantCulture);
861 }
862
863 bool maxValSet = null != maxValues && null != maxValues[i] && 0 < maxValues[i].Length;
864 int maxValue = 0;
865 if (maxValSet)
866 {
867 maxValue = Convert.ToInt32(maxValues[i], CultureInfo.InvariantCulture);
868 }
869
870 bool keyColumnSet = null != keyColumns && null != keyColumns[i] && 0 < keyColumns[i].Length;
871 int keyColumn = 0;
872 if (keyColumnSet)
873 {
874 keyColumn = Convert.ToInt32(keyColumns[i], CultureInfo.InvariantCulture);
875 }
876
877 ColumnCategory category = ColumnCategory.Unknown;
878 if (null != categories && null != categories[i] && 0 < categories[i].Length)
879 {
880 switch (categories[i])
881 {
882 case "Text":
883 category = ColumnCategory.Text;
884 break;
885 case "UpperCase":
886 category = ColumnCategory.UpperCase;
887 break;
888 case "LowerCase":
889 category = ColumnCategory.LowerCase;
890 break;
891 case "Integer":
892 category = ColumnCategory.Integer;
893 break;
894 case "DoubleInteger":
895 category = ColumnCategory.DoubleInteger;
896 break;
897 case "TimeDate":
898 category = ColumnCategory.TimeDate;
899 break;
900 case "Identifier":
901 category = ColumnCategory.Identifier;
902 break;
903 case "Property":
904 category = ColumnCategory.Property;
905 break;
906 case "Filename":
907 category = ColumnCategory.Filename;
908 break;
909 case "WildCardFilename":
910 category = ColumnCategory.WildCardFilename;
911 break;
912 case "Path":
913 category = ColumnCategory.Path;
914 break;
915 case "Paths":
916 category = ColumnCategory.Paths;
917 break;
918 case "AnyPath":
919 category = ColumnCategory.AnyPath;
920 break;
921 case "DefaultDir":
922 category = ColumnCategory.DefaultDir;
923 break;
924 case "RegPath":
925 category = ColumnCategory.RegPath;
926 break;
927 case "Formatted":
928 category = ColumnCategory.Formatted;
929 break;
930 case "FormattedSddl":
931 category = ColumnCategory.FormattedSDDLText;
932 break;
933 case "Template":
934 category = ColumnCategory.Template;
935 break;
936 case "Condition":
937 category = ColumnCategory.Condition;
938 break;
939 case "Guid":
940 category = ColumnCategory.Guid;
941 break;
942 case "Version":
943 category = ColumnCategory.Version;
944 break;
945 case "Language":
946 category = ColumnCategory.Language;
947 break;
948 case "Binary":
949 category = ColumnCategory.Binary;
950 break;
951 case "CustomSource":
952 category = ColumnCategory.CustomSource;
953 break;
954 case "Cabinet":
955 category = ColumnCategory.Cabinet;
956 break;
957 case "Shortcut":
958 category = ColumnCategory.Shortcut;
959 break;
960 default:
961 break;
962 }
963 }
964
965 string keyTable = keyTables != null ? keyTables[i] : null;
966 string setValue = sets != null ? sets[i] : null;
967 string description = descriptions != null ? descriptions[i] : null;
968 string modString = modularizations != null ? modularizations[i] : null;
969 ColumnModularizeType modularization = ColumnModularizeType.None;
970 if (modString != null)
971 {
972 switch (modString)
973 {
974 case "None":
975 modularization = ColumnModularizeType.None;
976 break;
977 case "Column":
978 modularization = ColumnModularizeType.Column;
979 break;
980 case "Property":
981 modularization = ColumnModularizeType.Property;
982 break;
983 case "Condition":
984 modularization = ColumnModularizeType.Condition;
985 break;
986 case "CompanionFile":
987 modularization = ColumnModularizeType.CompanionFile;
988 break;
989 case "SemicolonDelimited":
990 modularization = ColumnModularizeType.SemicolonDelimited;
991 break;
992 }
993 }
994
995 ColumnDefinition columnDefinition = new ColumnDefinition(name, type, length, primaryKey, nullable, modularization, ColumnType.Localized == type, minValSet, minValue, maxValSet, maxValue, keyTable, keyColumnSet, keyColumn, category, setValue, description, true, true);
996 columns.Add(columnDefinition);
997 }
998
999 TableDefinition customTable = new TableDefinition((string)row[0], columns, false, bootstrapperApplicationData, bootstrapperApplicationData);
1000 customTableDefinitions.Add(customTable);
1001 }
1002 }
1003
1004 /// <summary>
1005 /// Checks for any tables in the output which are not allowed in the output type.
1006 /// </summary>
1007 /// <param name="output">The output to check.</param>
1008 private void CheckForIllegalTables(Output output)
1009 {
1010 foreach (Table table in output.Tables)
1011 {
1012 switch (output.Type)
1013 {
1014 case OutputType.Module:
1015 if ("BBControl" == table.Name ||
1016 "Billboard" == table.Name ||
1017 "CCPSearch" == table.Name ||
1018 "Feature" == table.Name ||
1019 "LaunchCondition" == table.Name ||
1020 "Media" == table.Name ||
1021 "Patch" == table.Name ||
1022 "Upgrade" == table.Name ||
1023 "WixMerge" == table.Name)
1024 {
1025 foreach (Row row in table.Rows)
1026 {
1027 this.OnMessage(WixErrors.UnexpectedTableInMergeModule(row.SourceLineNumbers, table.Name));
1028 }
1029 }
1030 else if ("Error" == table.Name)
1031 {
1032 foreach (Row row in table.Rows)
1033 {
1034 this.OnMessage(WixWarnings.DangerousTableInMergeModule(row.SourceLineNumbers, table.Name));
1035 }
1036 }
1037 break;
1038 case OutputType.PatchCreation:
1039 if (!table.Definition.Unreal &&
1040 "_SummaryInformation" != table.Name &&
1041 "ExternalFiles" != table.Name &&
1042 "FamilyFileRanges" != table.Name &&
1043 "ImageFamilies" != table.Name &&
1044 "PatchMetadata" != table.Name &&
1045 "PatchSequence" != table.Name &&
1046 "Properties" != table.Name &&
1047 "TargetFiles_OptionalData" != table.Name &&
1048 "TargetImages" != table.Name &&
1049 "UpgradedFiles_OptionalData" != table.Name &&
1050 "UpgradedFilesToIgnore" != table.Name &&
1051 "UpgradedImages" != table.Name)
1052 {
1053 foreach (Row row in table.Rows)
1054 {
1055 this.OnMessage(WixErrors.UnexpectedTableInPatchCreationPackage(row.SourceLineNumbers, table.Name));
1056 }
1057 }
1058 break;
1059 case OutputType.Patch:
1060 if (!table.Definition.Unreal &&
1061 "_SummaryInformation" != table.Name &&
1062 "Media" != table.Name &&
1063 "MsiPatchMetadata" != table.Name &&
1064 "MsiPatchSequence" != table.Name)
1065 {
1066 foreach (Row row in table.Rows)
1067 {
1068 this.OnMessage(WixErrors.UnexpectedTableInPatch(row.SourceLineNumbers, table.Name));
1069 }
1070 }
1071 break;
1072 case OutputType.Product:
1073 if ("ModuleAdminExecuteSequence" == table.Name ||
1074 "ModuleAdminUISequence" == table.Name ||
1075 "ModuleAdvtExecuteSequence" == table.Name ||
1076 "ModuleAdvtUISequence" == table.Name ||
1077 "ModuleComponents" == table.Name ||
1078 "ModuleConfiguration" == table.Name ||
1079 "ModuleDependency" == table.Name ||
1080 "ModuleExclusion" == table.Name ||
1081 "ModuleIgnoreTable" == table.Name ||
1082 "ModuleInstallExecuteSequence" == table.Name ||
1083 "ModuleInstallUISequence" == table.Name ||
1084 "ModuleSignature" == table.Name ||
1085 "ModuleSubstitution" == table.Name)
1086 {
1087 foreach (Row row in table.Rows)
1088 {
1089 this.OnMessage(WixWarnings.UnexpectedTableInProduct(row.SourceLineNumbers, table.Name));
1090 }
1091 }
1092 break;
1093 }
1094 }
1095 }
1096
1097 /// <summary>
1098 /// Performs various consistency checks on the output.
1099 /// </summary>
1100 /// <param name="output">Output containing instance transform definitions.</param>
1101 private void CheckOutputConsistency(Output output)
1102 {
1103 // Get the output's minimum installer version
1104 int outputInstallerVersion = int.MinValue;
1105 Table summaryInformationTable = output.Tables["_SummaryInformation"];
1106 if (null != summaryInformationTable)
1107 {
1108 foreach (Row row in summaryInformationTable.Rows)
1109 {
1110 if (14 == (int)row[0])
1111 {
1112 outputInstallerVersion = Convert.ToInt32(row[1], CultureInfo.InvariantCulture);
1113 break;
1114 }
1115 }
1116 }
1117
1118 // ensure the Error table exists if output is marked for MSI 1.0 or below (see ICE40)
1119 if (100 >= outputInstallerVersion && OutputType.Product == output.Type)
1120 {
1121 output.EnsureTable(this.tableDefinitions["Error"]);
1122 }
1123
1124 // check for the presence of tables/rows/columns that require MSI 1.1 or later
1125 if (110 > outputInstallerVersion)
1126 {
1127 Table isolatedComponentTable = output.Tables["IsolatedComponent"];
1128 if (null != isolatedComponentTable)
1129 {
1130 foreach (Row row in isolatedComponentTable.Rows)
1131 {
1132 this.OnMessage(WixWarnings.TableIncompatibleWithInstallerVersion(row.SourceLineNumbers, "IsolatedComponent", outputInstallerVersion));
1133 }
1134 }
1135 }
1136
1137 // check for the presence of tables/rows/columns that require MSI 4.0 or later
1138 if (400 > outputInstallerVersion)
1139 {
1140 Table shortcutTable = output.Tables["Shortcut"];
1141 if (null != shortcutTable)
1142 {
1143 foreach (Row row in shortcutTable.Rows)
1144 {
1145 if (null != row[12] || null != row[13] || null != row[14] || null != row[15])
1146 {
1147 this.OnMessage(WixWarnings.ColumnsIncompatibleWithInstallerVersion(row.SourceLineNumbers, "Shortcut", outputInstallerVersion));
1148 }
1149 }
1150 }
1151 }
1152 }
1153
1154 /// <summary>
1155 /// Sends a message to the message delegate if there is one.
1156 /// </summary>
1157 /// <param name="mea">Message event arguments.</param>
1158 public void OnMessage(MessageEventArgs e)
1159 {
1160 Messaging.Instance.OnMessage(e);
1161 }
1162
1163 /// <summary>
1164 /// Load the standard action symbols.
1165 /// </summary>
1166 /// <param name="allSymbols">Collection of symbols.</param>
1167 private void LoadStandardActionSymbols(IDictionary<string, Symbol> allSymbols)
1168 {
1169 foreach (WixActionRow actionRow in this.standardActions)
1170 {
1171 Symbol actionSymbol = new Symbol(actionRow);
1172
1173 // If the action's symbol has not already been defined (i.e. overriden by the user), add it now.
1174 if (!allSymbols.ContainsKey(actionSymbol.Name))
1175 {
1176 allSymbols.Add(actionSymbol.Name, actionSymbol);
1177 }
1178 }
1179 }
1180
1181 /// <summary>
1182 /// Process the complex references.
1183 /// </summary>
1184 /// <param name="output">Active output to add sections to.</param>
1185 /// <param name="sections">Sections that are referenced during the link process.</param>
1186 /// <param name="referencedComponents">Collection of all components referenced by complex reference.</param>
1187 /// <param name="componentsToFeatures">Component to feature complex references.</param>
1188 /// <param name="featuresToFeatures">Feature to feature complex references.</param>
1189 /// <param name="modulesToFeatures">Module to feature complex references.</param>
1190 private void ProcessComplexReferences(Output output, IEnumerable<Section> sections, ISet<string> referencedComponents, ConnectToFeatureCollection componentsToFeatures, ConnectToFeatureCollection featuresToFeatures, ConnectToFeatureCollection modulesToFeatures)
1191 {
1192 Hashtable componentsToModules = new Hashtable();
1193
1194 foreach (Section section in sections)
1195 {
1196 Table wixComplexReferenceTable = section.Tables["WixComplexReference"];
1197
1198 if (null != wixComplexReferenceTable)
1199 {
1200 foreach (WixComplexReferenceRow wixComplexReferenceRow in wixComplexReferenceTable.Rows)
1201 {
1202 ConnectToFeature connection;
1203 switch (wixComplexReferenceRow.ParentType)
1204 {
1205 case ComplexReferenceParentType.Feature:
1206 switch (wixComplexReferenceRow.ChildType)
1207 {
1208 case ComplexReferenceChildType.Component:
1209 connection = componentsToFeatures[wixComplexReferenceRow.ChildId];
1210 if (null == connection)
1211 {
1212 componentsToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentId, wixComplexReferenceRow.IsPrimary));
1213 }
1214 else if (wixComplexReferenceRow.IsPrimary)
1215 {
1216 if (connection.IsExplicitPrimaryFeature)
1217 {
1218 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1219 continue;
1220 }
1221 else
1222 {
1223 connection.ConnectFeatures.Add(connection.PrimaryFeature); // move the guessed primary feature to the list of connects
1224 connection.PrimaryFeature = wixComplexReferenceRow.ParentId; // set the new primary feature
1225 connection.IsExplicitPrimaryFeature = true; // and make sure we remember that we set it so we can fail if we try to set it again
1226 }
1227 }
1228 else
1229 {
1230 connection.ConnectFeatures.Add(wixComplexReferenceRow.ParentId);
1231 }
1232
1233 // add a row to the FeatureComponents table
1234 Table featureComponentsTable = output.EnsureTable(this.tableDefinitions["FeatureComponents"]);
1235 Row row = featureComponentsTable.CreateRow(null);
1236 if (this.sectionIdOnRows)
1237 {
1238 row.SectionId = section.Id;
1239 }
1240 row[0] = wixComplexReferenceRow.ParentId;
1241 row[1] = wixComplexReferenceRow.ChildId;
1242
1243 // index the component for finding orphaned records
1244 string symbolName = String.Concat("Component:", wixComplexReferenceRow.ChildId);
1245 referencedComponents.Add(symbolName);
1246
1247 break;
1248
1249 case ComplexReferenceChildType.Feature:
1250 connection = featuresToFeatures[wixComplexReferenceRow.ChildId];
1251 if (null != connection)
1252 {
1253 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1254 continue;
1255 }
1256
1257 featuresToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentId, wixComplexReferenceRow.IsPrimary));
1258 break;
1259
1260 case ComplexReferenceChildType.Module:
1261 connection = modulesToFeatures[wixComplexReferenceRow.ChildId];
1262 if (null == connection)
1263 {
1264 modulesToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentId, wixComplexReferenceRow.IsPrimary));
1265 }
1266 else if (wixComplexReferenceRow.IsPrimary)
1267 {
1268 if (connection.IsExplicitPrimaryFeature)
1269 {
1270 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1271 continue;
1272 }
1273 else
1274 {
1275 connection.ConnectFeatures.Add(connection.PrimaryFeature); // move the guessed primary feature to the list of connects
1276 connection.PrimaryFeature = wixComplexReferenceRow.ParentId; // set the new primary feature
1277 connection.IsExplicitPrimaryFeature = true; // and make sure we remember that we set it so we can fail if we try to set it again
1278 }
1279 }
1280 else
1281 {
1282 connection.ConnectFeatures.Add(wixComplexReferenceRow.ParentId);
1283 }
1284 break;
1285
1286 default:
1287 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1288 }
1289 break;
1290
1291 case ComplexReferenceParentType.Module:
1292 switch (wixComplexReferenceRow.ChildType)
1293 {
1294 case ComplexReferenceChildType.Component:
1295 if (componentsToModules.ContainsKey(wixComplexReferenceRow.ChildId))
1296 {
1297 this.OnMessage(WixErrors.ComponentReferencedTwice(section.SourceLineNumbers, wixComplexReferenceRow.ChildId));
1298 continue;
1299 }
1300 else
1301 {
1302 componentsToModules.Add(wixComplexReferenceRow.ChildId, wixComplexReferenceRow); // should always be new
1303
1304 // add a row to the ModuleComponents table
1305 Table moduleComponentsTable = output.EnsureTable(this.tableDefinitions["ModuleComponents"]);
1306 Row row = moduleComponentsTable.CreateRow(null);
1307 if (this.sectionIdOnRows)
1308 {
1309 row.SectionId = section.Id;
1310 }
1311 row[0] = wixComplexReferenceRow.ChildId;
1312 row[1] = wixComplexReferenceRow.ParentId;
1313 row[2] = wixComplexReferenceRow.ParentLanguage;
1314 }
1315
1316 // index the component for finding orphaned records
1317 string componentSymbolName = String.Concat("Component:", wixComplexReferenceRow.ChildId);
1318 referencedComponents.Add(componentSymbolName);
1319
1320 break;
1321
1322 default:
1323 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1324 }
1325 break;
1326
1327 case ComplexReferenceParentType.Patch:
1328 switch (wixComplexReferenceRow.ChildType)
1329 {
1330 case ComplexReferenceChildType.PatchFamily:
1331 case ComplexReferenceChildType.PatchFamilyGroup:
1332 break;
1333
1334 default:
1335 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1336 }
1337 break;
1338
1339 case ComplexReferenceParentType.Product:
1340 switch (wixComplexReferenceRow.ChildType)
1341 {
1342 case ComplexReferenceChildType.Feature:
1343 connection = featuresToFeatures[wixComplexReferenceRow.ChildId];
1344 if (null != connection)
1345 {
1346 this.OnMessage(WixErrors.MultiplePrimaryReferences(section.SourceLineNumbers, wixComplexReferenceRow.ChildType.ToString(), wixComplexReferenceRow.ChildId, wixComplexReferenceRow.ParentType.ToString(), wixComplexReferenceRow.ParentId, (null != connection.PrimaryFeature ? "Feature" : "Product"), (null != connection.PrimaryFeature ? connection.PrimaryFeature : this.activeOutput.EntrySection.Id)));
1347 continue;
1348 }
1349
1350 featuresToFeatures.Add(new ConnectToFeature(section, wixComplexReferenceRow.ChildId, null, wixComplexReferenceRow.IsPrimary));
1351 break;
1352
1353 default:
1354 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceChildType), wixComplexReferenceRow.ChildType)));
1355 }
1356 break;
1357
1358 default:
1359 // Note: Groups have been processed before getting here so they are not handled by any case above.
1360 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedComplexReferenceChildType, Enum.GetName(typeof(ComplexReferenceParentType), wixComplexReferenceRow.ParentType)));
1361 }
1362 }
1363 }
1364 }
1365 }
1366
1367 /// <summary>
1368 /// Flattens all complex references in all sections in the collection.
1369 /// </summary>
1370 /// <param name="sections">Sections that are referenced during the link process.</param>
1371 private void FlattenSectionsComplexReferences(IEnumerable<Section> sections)
1372 {
1373 Hashtable parentGroups = new Hashtable();
1374 Hashtable parentGroupsSections = new Hashtable();
1375 Hashtable parentGroupsNeedingProcessing = new Hashtable();
1376
1377 // DisplaySectionComplexReferences("--- section's complex references before flattening ---", sections);
1378
1379 // Step 1: Gather all of the complex references that are going participate
1380 // in the flatting process. This means complex references that have "grouping
1381 // parents" of Features, Modules, and, of course, Groups. These references
1382 // that participate in a "grouping parent" will be removed from their section
1383 // now and after processing added back in Step 3 below.
1384 foreach (Section section in sections)
1385 {
1386 Table wixComplexReferenceTable = section.Tables["WixComplexReference"];
1387
1388 if (null != wixComplexReferenceTable)
1389 {
1390 // Count down because we'll sometimes remove items from the list.
1391 for (int i = wixComplexReferenceTable.Rows.Count - 1; i >= 0; --i)
1392 {
1393 WixComplexReferenceRow wixComplexReferenceRow = (WixComplexReferenceRow)wixComplexReferenceTable.Rows[i];
1394
1395 // Only process the "grouping parents" such as FeatureGroup, ComponentGroup, Feature,
1396 // and Module. Non-grouping complex references are simple and
1397 // resolved during normal complex reference resolutions.
1398 if (ComplexReferenceParentType.FeatureGroup == wixComplexReferenceRow.ParentType ||
1399 ComplexReferenceParentType.ComponentGroup == wixComplexReferenceRow.ParentType ||
1400 ComplexReferenceParentType.Feature == wixComplexReferenceRow.ParentType ||
1401 ComplexReferenceParentType.Module == wixComplexReferenceRow.ParentType ||
1402 ComplexReferenceParentType.PatchFamilyGroup == wixComplexReferenceRow.ParentType ||
1403 ComplexReferenceParentType.Product == wixComplexReferenceRow.ParentType)
1404 {
1405 string parentTypeAndId = CombineTypeAndId(wixComplexReferenceRow.ParentType, wixComplexReferenceRow.ParentId);
1406
1407 // Group all complex references with a common parent
1408 // together so we can find them quickly while processing in
1409 // Step 2.
1410 ArrayList childrenComplexRefs = parentGroups[parentTypeAndId] as ArrayList;
1411 if (null == childrenComplexRefs)
1412 {
1413 childrenComplexRefs = new ArrayList();
1414 parentGroups.Add(parentTypeAndId, childrenComplexRefs);
1415 }
1416
1417 childrenComplexRefs.Add(wixComplexReferenceRow);
1418 wixComplexReferenceTable.Rows.RemoveAt(i);
1419
1420 // Remember the mapping from set of complex references with a common
1421 // parent to their section. We'll need this to add them back to the
1422 // correct section in Step 3.
1423 Section parentSection = parentGroupsSections[parentTypeAndId] as Section;
1424 if (null == parentSection)
1425 {
1426 parentGroupsSections.Add(parentTypeAndId, section);
1427 }
1428 // Debug.Assert(section == (Section)parentGroupsSections[parentTypeAndId]);
1429
1430 // If the child of the complex reference is another group, then in Step 2
1431 // we're going to have to process this complex reference again to copy
1432 // the child group's references into the parent group.
1433 if ((ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1434 (ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1435 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
1436 {
1437 if (!parentGroupsNeedingProcessing.ContainsKey(parentTypeAndId))
1438 {
1439 parentGroupsNeedingProcessing.Add(parentTypeAndId, section);
1440 }
1441 // Debug.Assert(section == (Section)parentGroupsNeedingProcessing[parentTypeAndId]);
1442 }
1443 }
1444 }
1445 }
1446 }
1447 Debug.Assert(parentGroups.Count == parentGroupsSections.Count);
1448 Debug.Assert(parentGroupsNeedingProcessing.Count <= parentGroups.Count);
1449
1450 // DisplaySectionComplexReferences("\r\n\r\n--- section's complex references middle of flattening ---", sections);
1451
1452 // Step 2: Loop through the parent groups that have nested groups removing
1453 // them from the hash table as they are processed. At the end of this the
1454 // complex references should all be flattened.
1455 string[] keys = new string[parentGroupsNeedingProcessing.Keys.Count];
1456 parentGroupsNeedingProcessing.Keys.CopyTo(keys, 0);
1457
1458 foreach (string key in keys)
1459 {
1460 if (parentGroupsNeedingProcessing.Contains(key))
1461 {
1462 Stack loopDetector = new Stack();
1463 this.FlattenGroup(key, loopDetector, parentGroups, parentGroupsNeedingProcessing);
1464 }
1465 else
1466 {
1467 // the group must have allready been procesed and removed from the hash table
1468 }
1469 }
1470 Debug.Assert(0 == parentGroupsNeedingProcessing.Count);
1471
1472 // Step 3: Finally, ensure that all of the groups that were removed
1473 // in Step 1 and flattened in Step 2 are added to their appropriate
1474 // section. This is where we will toss out the final no-longer-needed
1475 // groups.
1476 foreach (string parentGroup in parentGroups.Keys)
1477 {
1478 Section section = (Section)parentGroupsSections[parentGroup];
1479 Table wixComplexReferenceTable = section.Tables["WixComplexReference"];
1480
1481 foreach (WixComplexReferenceRow wixComplexReferenceRow in (ArrayList)parentGroups[parentGroup])
1482 {
1483 if ((ComplexReferenceParentType.FeatureGroup != wixComplexReferenceRow.ParentType) &&
1484 (ComplexReferenceParentType.ComponentGroup != wixComplexReferenceRow.ParentType) &&
1485 (ComplexReferenceParentType.PatchFamilyGroup != wixComplexReferenceRow.ParentType))
1486 {
1487 wixComplexReferenceTable.Rows.Add(wixComplexReferenceRow);
1488 }
1489 }
1490 }
1491
1492 // DisplaySectionComplexReferences("\r\n\r\n--- section's complex references after flattening ---", sections);
1493 }
1494
1495 private string CombineTypeAndId(ComplexReferenceParentType type, string id)
1496 {
1497 return String.Concat(type.ToString(), ":", id);
1498 }
1499
1500 private string CombineTypeAndId(ComplexReferenceChildType type, string id)
1501 {
1502 return String.Concat(type.ToString(), ":", id);
1503 }
1504
1505 /// <summary>
1506 /// Recursively processes the group.
1507 /// </summary>
1508 /// <param name="parentTypeAndId">String combination type and id of group to process next.</param>
1509 /// <param name="loopDetector">Stack of groups processed thus far. Used to detect loops.</param>
1510 /// <param name="parentGroups">Hash table of complex references grouped by parent id.</param>
1511 /// <param name="parentGroupsNeedingProcessing">Hash table of parent groups that still have nested groups that need to be flattened.</param>
1512 private void FlattenGroup(string parentTypeAndId, Stack loopDetector, Hashtable parentGroups, Hashtable parentGroupsNeedingProcessing)
1513 {
1514 Debug.Assert(parentGroupsNeedingProcessing.Contains(parentTypeAndId));
1515 loopDetector.Push(parentTypeAndId); // push this complex reference parent identfier into the stack for loop verifying
1516
1517 ArrayList allNewChildComplexReferences = new ArrayList();
1518 ArrayList referencesToParent = (ArrayList)parentGroups[parentTypeAndId];
1519 foreach (WixComplexReferenceRow wixComplexReferenceRow in referencesToParent)
1520 {
1521 Debug.Assert(ComplexReferenceParentType.ComponentGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.FeatureGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Feature == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Module == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Product == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.PatchFamilyGroup == wixComplexReferenceRow.ParentType || ComplexReferenceParentType.Patch == wixComplexReferenceRow.ParentType);
1522 Debug.Assert(parentTypeAndId == CombineTypeAndId(wixComplexReferenceRow.ParentType, wixComplexReferenceRow.ParentId));
1523
1524 // We are only interested processing when the child is a group.
1525 if ((ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1526 (ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1527 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
1528 {
1529 string childTypeAndId = CombineTypeAndId(wixComplexReferenceRow.ChildType, wixComplexReferenceRow.ChildId);
1530 if (loopDetector.Contains(childTypeAndId))
1531 {
1532 // Create a comma delimited list of the references that participate in the
1533 // loop for the error message. Start at the bottom of the stack and work the
1534 // way up to present the loop as a directed graph.
1535 object[] stack = loopDetector.ToArray();
1536 StringBuilder loop = new StringBuilder();
1537 for (int i = stack.Length - 1; i >= 0; --i)
1538 {
1539 loop.Append((string)stack[i]);
1540 if (0 < i)
1541 {
1542 loop.Append(" -> ");
1543 }
1544 }
1545
1546 this.OnMessage(WixErrors.ReferenceLoopDetected(wixComplexReferenceRow.Table.Section == null ? null : wixComplexReferenceRow.Table.Section.SourceLineNumbers, loop.ToString()));
1547
1548 // Cleanup the parentGroupsNeedingProcessing and the loopDetector just like the
1549 // exit of this method does at the end because we are exiting early.
1550 loopDetector.Pop();
1551 parentGroupsNeedingProcessing.Remove(parentTypeAndId);
1552 return; // bail
1553 }
1554
1555 // Check to see if the child group still needs to be processed. If so,
1556 // go do that so that we'll get all of that children's (and children's
1557 // children) complex references correctly merged into our parent group.
1558 if (parentGroupsNeedingProcessing.ContainsKey(childTypeAndId))
1559 {
1560 this.FlattenGroup(childTypeAndId, loopDetector, parentGroups, parentGroupsNeedingProcessing);
1561 }
1562
1563 // If the child is a parent to anything (i.e. the parent has grandchildren)
1564 // clone each of the children's complex references, repoint them to the parent
1565 // complex reference (because we're moving references up the tree), and finally
1566 // add the cloned child's complex reference to the list of complex references
1567 // that we'll eventually add to the parent group.
1568 ArrayList referencesToChild = (ArrayList)parentGroups[childTypeAndId];
1569 if (null != referencesToChild)
1570 {
1571 foreach (WixComplexReferenceRow crefChild in referencesToChild)
1572 {
1573 // Only merge up the non-group items since groups are purged
1574 // after this part of the processing anyway (cloning them would
1575 // be a complete waste of time).
1576 if ((ComplexReferenceChildType.FeatureGroup != crefChild.ChildType) ||
1577 (ComplexReferenceChildType.ComponentGroup != crefChild.ChildType) ||
1578 (ComplexReferenceChildType.PatchFamilyGroup != crefChild.ChildType))
1579 {
1580 WixComplexReferenceRow crefChildClone = crefChild.Clone();
1581 Debug.Assert(crefChildClone.ParentId == wixComplexReferenceRow.ChildId);
1582
1583 crefChildClone.Reparent(wixComplexReferenceRow);
1584 allNewChildComplexReferences.Add(crefChildClone);
1585 }
1586 }
1587 }
1588 }
1589 }
1590
1591 // Add the children group's complex references to the parent
1592 // group. Clean out any left over groups and quietly remove any
1593 // duplicate complex references that occurred during the merge.
1594 referencesToParent.AddRange(allNewChildComplexReferences);
1595 referencesToParent.Sort();
1596 for (int i = referencesToParent.Count - 1; i >= 0; --i)
1597 {
1598 WixComplexReferenceRow wixComplexReferenceRow = (WixComplexReferenceRow)referencesToParent[i];
1599 if ((ComplexReferenceChildType.FeatureGroup == wixComplexReferenceRow.ChildType) ||
1600 (ComplexReferenceChildType.ComponentGroup == wixComplexReferenceRow.ChildType) ||
1601 (ComplexReferenceChildType.PatchFamilyGroup == wixComplexReferenceRow.ChildType))
1602 {
1603 referencesToParent.RemoveAt(i);
1604 }
1605 else if (i > 0)
1606 {
1607 // Since the list is already sorted, we can find duplicates by simply
1608 // looking at the next sibling in the list and tossing out one if they
1609 // match.
1610 WixComplexReferenceRow crefCompare = (WixComplexReferenceRow)referencesToParent[i - 1];
1611 if (0 == wixComplexReferenceRow.CompareToWithoutConsideringPrimary(crefCompare))
1612 {
1613 referencesToParent.RemoveAt(i);
1614 }
1615 }
1616 }
1617
1618 loopDetector.Pop(); // pop this complex reference off the stack since we're done verify the loop here
1619 parentGroupsNeedingProcessing.Remove(parentTypeAndId); // remove the newly processed complex reference
1620 }
1621
1622 /*
1623 /// <summary>
1624 /// Debugging method for displaying the section complex references.
1625 /// </summary>
1626 /// <param name="header">The header.</param>
1627 /// <param name="sections">The sections to display.</param>
1628 private void DisplaySectionComplexReferences(string header, SectionCollection sections)
1629 {
1630 Console.WriteLine(header);
1631 foreach (Section section in sections)
1632 {
1633 Table wixComplexReferenceTable = section.Tables["WixComplexReference"];
1634
1635 foreach (WixComplexReferenceRow cref in wixComplexReferenceTable.Rows)
1636 {
1637 Console.WriteLine("Section: {0} Parent: {1} Type: {2} Child: {3} Primary: {4}", section.Id, cref.ParentId, cref.ParentType, cref.ChildId, cref.IsPrimary);
1638 }
1639 }
1640 }
1641 */
1642
1643 /// <summary>
1644 /// Flattens the tables used in a Bundle.
1645 /// </summary>
1646 /// <param name="output">Output containing the tables to process.</param>
1647 private void FlattenBundleTables(Output output)
1648 {
1649 if (OutputType.Bundle != output.Type)
1650 {
1651 return;
1652 }
1653
1654 // We need to flatten the nested PayloadGroups and PackageGroups under
1655 // UX, Chain, and any Containers. When we're done, the WixGroups table
1656 // will hold Payloads under UX, ChainPackages (references?) under Chain,
1657 // and ChainPackages/Payloads under the attached and any detatched
1658 // Containers.
1659 WixGroupingOrdering groups = new WixGroupingOrdering(output, this);
1660
1661 // Create UX payloads and Package payloads
1662 groups.UseTypes(new string[] { "Container", "Layout", "PackageGroup", "PayloadGroup", "Package" }, new string[] { "PackageGroup", "Package", "PayloadGroup", "Payload" });
1663 groups.FlattenAndRewriteGroups("Package", false);
1664 groups.FlattenAndRewriteGroups("Container", false);
1665 groups.FlattenAndRewriteGroups("Layout", false);
1666
1667 // Create Chain packages...
1668 groups.UseTypes(new string[] { "PackageGroup" }, new string[] { "Package", "PackageGroup" });
1669 groups.FlattenAndRewriteRows("PackageGroup", "WixChain", false);
1670
1671 groups.RemoveUsedGroupRows();
1672 }
1673
1674 /// <summary>
1675 /// Resolves the features connected to other features in the active output.
1676 /// </summary>
1677 /// <param name="featuresToFeatures">Feature to feature complex references.</param>
1678 /// <param name="allSymbols">All symbols loaded from the sections.</param>
1679 private void ResolveFeatureToFeatureConnects(ConnectToFeatureCollection featuresToFeatures, IDictionary<string, Symbol> allSymbols)
1680 {
1681 foreach (ConnectToFeature connection in featuresToFeatures)
1682 {
1683 WixSimpleReferenceRow wixSimpleReferenceRow = new WixSimpleReferenceRow(null, this.tableDefinitions["WixSimpleReference"]);
1684 wixSimpleReferenceRow.TableName = "Feature";
1685 wixSimpleReferenceRow.PrimaryKeys = connection.ChildId;
1686
1687 Symbol symbol;
1688 if (!allSymbols.TryGetValue(wixSimpleReferenceRow.SymbolicName, out symbol))
1689 {
1690 continue;
1691 }
1692
1693 Row row = symbol.Row;
1694 row[1] = connection.PrimaryFeature;
1695 }
1696 }
1697
1698 /// <summary>
1699 /// Copies a table's rows to an output table.
1700 /// </summary>
1701 /// <param name="table">Source table to copy rows from.</param>
1702 /// <param name="outputTable">Destination table in output to copy rows into.</param>
1703 /// <param name="sectionId">Id of the section that the table lives in.</param>
1704 private void CopyTableRowsToOutputTable(Table table, Table outputTable, string sectionId)
1705 {
1706 int[] localizedColumns = new int[table.Definition.Columns.Count];
1707 int localizedColumnCount = 0;
1708
1709 // if there are localization strings, figure out which columns can be localized in this table
1710 if (null != this.localizer)
1711 {
1712 for (int i = 0; i < table.Definition.Columns.Count; i++)
1713 {
1714 if (table.Definition.Columns[i].IsLocalizable)
1715 {
1716 localizedColumns[localizedColumnCount++] = i;
1717 }
1718 }
1719 }
1720
1721 // process each row in the table doing the string resource substitutions
1722 // then add the row to the output
1723 foreach (Row row in table.Rows)
1724 {
1725 for (int j = 0; j < localizedColumnCount; j++)
1726 {
1727 Field field = row.Fields[localizedColumns[j]];
1728
1729 if (null != field.Data)
1730 {
1731 field.Data = this.WixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)field.Data, true);
1732 }
1733 }
1734
1735 row.SectionId = (this.sectionIdOnRows ? sectionId : null);
1736 outputTable.Rows.Add(row);
1737 }
1738 }
1739
1740 /// <summary>
1741 /// Set sequence numbers for all the actions and create rows in the output object.
1742 /// </summary>
1743 /// <param name="actionRows">Collection of actions to schedule.</param>
1744 /// <param name="suppressActionRows">Collection of actions to suppress.</param>
1745 private void SequenceActions(List<Row> actionRows, List<Row> suppressActionRows)
1746 {
1747 WixActionRowCollection overridableActionRows = new WixActionRowCollection();
1748 WixActionRowCollection requiredActionRows = new WixActionRowCollection();
1749 ArrayList scheduledActionRows = new ArrayList();
1750
1751 // gather the required actions for the output type
1752 if (OutputType.Product == this.activeOutput.Type)
1753 {
1754 // AdminExecuteSequence table
1755 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "CostFinalize"]);
1756 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "CostInitialize"]);
1757 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "FileCost"]);
1758 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallAdminPackage"]);
1759 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallFiles"]);
1760 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallFinalize"]);
1761 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallInitialize"]);
1762 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallValidate"]);
1763
1764 // AdminUISequence table
1765 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "CostFinalize"]);
1766 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "CostInitialize"]);
1767 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "ExecuteAction"]);
1768 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "FileCost"]);
1769
1770 // AdvtExecuteSequence table
1771 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "CostFinalize"]);
1772 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "CostInitialize"]);
1773 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "InstallFinalize"]);
1774 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "InstallInitialize"]);
1775 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "InstallValidate"]);
1776 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "PublishFeatures"]);
1777 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "PublishProduct"]);
1778
1779 // InstallExecuteSequence table
1780 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CostFinalize"]);
1781 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CostInitialize"]);
1782 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "FileCost"]);
1783 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallFinalize"]);
1784 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallInitialize"]);
1785 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallValidate"]);
1786 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "ProcessComponents"]);
1787 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "PublishFeatures"]);
1788 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "PublishProduct"]);
1789 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterProduct"]);
1790 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterUser"]);
1791 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnpublishFeatures"]);
1792 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "ValidateProductID"]);
1793
1794 // InstallUISequence table
1795 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "CostFinalize"]);
1796 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "CostInitialize"]);
1797 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "ExecuteAction"]);
1798 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "FileCost"]);
1799 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "ValidateProductID"]);
1800 }
1801
1802 // gather the required actions for each table
1803 foreach (Table table in this.activeOutput.Tables)
1804 {
1805 switch (table.Name)
1806 {
1807 case "AppSearch":
1808 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "AppSearch"], true);
1809 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "AppSearch"], true);
1810 break;
1811 case "BindImage":
1812 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "BindImage"], true);
1813 break;
1814 case "CCPSearch":
1815 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "AppSearch"], true);
1816 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CCPSearch"], true);
1817 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RMCCPSearch"], true);
1818 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "AppSearch"], true);
1819 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "CCPSearch"], true);
1820 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "RMCCPSearch"], true);
1821 break;
1822 case "Class":
1823 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterClassInfo"], true);
1824 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterClassInfo"], true);
1825 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterClassInfo"], true);
1826 break;
1827 case "Complus":
1828 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterComPlus"], true);
1829 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterComPlus"], true);
1830 break;
1831 case "CreateFolder":
1832 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CreateFolders"], true);
1833 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveFolders"], true);
1834 break;
1835 case "DuplicateFile":
1836 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "DuplicateFiles"], true);
1837 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveDuplicateFiles"], true);
1838 break;
1839 case "Environment":
1840 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "WriteEnvironmentStrings"], true);
1841 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveEnvironmentStrings"], true);
1842 break;
1843 case "Extension":
1844 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterExtensionInfo"], true);
1845 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterExtensionInfo"], true);
1846 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterExtensionInfo"], true);
1847 break;
1848 case "File":
1849 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallFiles"], true);
1850 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveFiles"], true);
1851 break;
1852 case "Font":
1853 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterFonts"], true);
1854 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterFonts"], true);
1855 break;
1856 case "IniFile":
1857 case "RemoveIniFile":
1858 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "WriteIniValues"], true);
1859 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveIniValues"], true);
1860 break;
1861 case "IsolatedComponent":
1862 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "IsolateComponents"], true);
1863 break;
1864 case "LaunchCondition":
1865 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "LaunchConditions"], true);
1866 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "LaunchConditions"], true);
1867 break;
1868 case "MIME":
1869 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterMIMEInfo"], true);
1870 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterMIMEInfo"], true);
1871 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterMIMEInfo"], true);
1872 break;
1873 case "MoveFile":
1874 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MoveFiles"], true);
1875 break;
1876 case "MsiAssembly":
1877 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "MsiPublishAssemblies"], true);
1878 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MsiPublishAssemblies"], true);
1879 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MsiUnpublishAssemblies"], true);
1880 break;
1881 case "MsiServiceConfig":
1882 case "MsiServiceConfigFailureActions":
1883 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MsiConfigureServices"], true);
1884 break;
1885 case "ODBCDataSource":
1886 case "ODBCTranslator":
1887 case "ODBCDriver":
1888 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "SetODBCFolders"], true);
1889 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallODBC"], true);
1890 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveODBC"], true);
1891 break;
1892 case "ProgId":
1893 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterProgIdInfo"], true);
1894 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterProgIdInfo"], true);
1895 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterProgIdInfo"], true);
1896 break;
1897 case "PublishComponent":
1898 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "PublishComponents"], true);
1899 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "PublishComponents"], true);
1900 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnpublishComponents"], true);
1901 break;
1902 case "Registry":
1903 case "RemoveRegistry":
1904 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "WriteRegistryValues"], true);
1905 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveRegistryValues"], true);
1906 break;
1907 case "RemoveFile":
1908 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveFiles"], true);
1909 break;
1910 case "SelfReg":
1911 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "SelfRegModules"], true);
1912 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "SelfUnregModules"], true);
1913 break;
1914 case "ServiceControl":
1915 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "StartServices"], true);
1916 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "StopServices"], true);
1917 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "DeleteServices"], true);
1918 break;
1919 case "ServiceInstall":
1920 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallServices"], true);
1921 break;
1922 case "Shortcut":
1923 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "CreateShortcuts"], true);
1924 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CreateShortcuts"], true);
1925 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveShortcuts"], true);
1926 break;
1927 case "TypeLib":
1928 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterTypeLibraries"], true);
1929 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterTypeLibraries"], true);
1930 break;
1931 case "Upgrade":
1932 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "FindRelatedProducts"], true);
1933 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "FindRelatedProducts"], true);
1934 // Only add the MigrateFeatureStates action if MigrateFeature attribute is set to yes on at least one UpgradeVersion element.
1935 foreach (Row row in table.Rows)
1936 {
1937 int options = (int)row[4];
1938 if (MsiInterop.MsidbUpgradeAttributesMigrateFeatures == (options & MsiInterop.MsidbUpgradeAttributesMigrateFeatures))
1939 {
1940 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MigrateFeatureStates"], true);
1941 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "MigrateFeatureStates"], true);
1942 break;
1943 }
1944 }
1945 break;
1946 }
1947 }
1948
1949 // index all the action rows (look for collisions)
1950 foreach (WixActionRow actionRow in actionRows)
1951 {
1952 if (actionRow.Overridable) // overridable action
1953 {
1954 WixActionRow collidingActionRow = overridableActionRows[actionRow.SequenceTable, actionRow.Action];
1955
1956 if (null != collidingActionRow)
1957 {
1958 this.OnMessage(WixErrors.OverridableActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
1959 if (null != collidingActionRow.SourceLineNumbers)
1960 {
1961 this.OnMessage(WixErrors.OverridableActionCollision2(collidingActionRow.SourceLineNumbers));
1962 }
1963 }
1964 else
1965 {
1966 overridableActionRows.Add(actionRow);
1967 }
1968 }
1969 else // unscheduled/scheduled action
1970 {
1971 // unscheduled action (allowed for certain standard actions)
1972 if (null == actionRow.Before && null == actionRow.After && 0 == actionRow.Sequence)
1973 {
1974 WixActionRow standardAction = this.standardActions[actionRow.SequenceTable, actionRow.Action];
1975
1976 if (null != standardAction)
1977 {
1978 // populate the sequence from the standard action
1979 actionRow.Sequence = standardAction.Sequence;
1980 }
1981 else // not a supported unscheduled action
1982 {
1983 throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet);
1984 }
1985 }
1986
1987 WixActionRow collidingActionRow = requiredActionRows[actionRow.SequenceTable, actionRow.Action];
1988
1989 if (null != collidingActionRow)
1990 {
1991 this.OnMessage(WixErrors.ActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
1992 if (null != collidingActionRow.SourceLineNumbers)
1993 {
1994 this.OnMessage(WixErrors.ActionCollision2(collidingActionRow.SourceLineNumbers));
1995 }
1996 }
1997 else
1998 {
1999 requiredActionRows.Add(actionRow.Clone());
2000 }
2001 }
2002 }
2003
2004 // add the overridable action rows that are not overridden to the required action rows
2005 foreach (WixActionRow actionRow in overridableActionRows)
2006 {
2007 if (null == requiredActionRows[actionRow.SequenceTable, actionRow.Action])
2008 {
2009 requiredActionRows.Add(actionRow.Clone());
2010 }
2011 }
2012
2013 // suppress the required actions that are overridable
2014 foreach (Row suppressActionRow in suppressActionRows)
2015 {
2016 SequenceTable sequenceTable = (SequenceTable)Enum.Parse(typeof(SequenceTable), (string)suppressActionRow[0]);
2017 string action = (string)suppressActionRow[1];
2018
2019 // get the action being suppressed (if it exists)
2020 WixActionRow requiredActionRow = requiredActionRows[sequenceTable, action];
2021
2022 // if there is an overridable row to suppress; suppress it
2023 // there is no warning if there is no action to suppress because the action may be suppressed from a merge module in the binder
2024 if (null != requiredActionRow)
2025 {
2026 if (requiredActionRow.Overridable)
2027 {
2028 this.OnMessage(WixWarnings.SuppressAction(suppressActionRow.SourceLineNumbers, action, sequenceTable.ToString()));
2029 if (null != requiredActionRow.SourceLineNumbers)
2030 {
2031 this.OnMessage(WixWarnings.SuppressAction2(requiredActionRow.SourceLineNumbers));
2032 }
2033 requiredActionRows.Remove(sequenceTable, action);
2034 }
2035 else // suppressing a non-overridable action row
2036 {
2037 this.OnMessage(WixErrors.SuppressNonoverridableAction(suppressActionRow.SourceLineNumbers, sequenceTable.ToString(), action));
2038 if (null != requiredActionRow.SourceLineNumbers)
2039 {
2040 this.OnMessage(WixErrors.SuppressNonoverridableAction2(requiredActionRow.SourceLineNumbers));
2041 }
2042 }
2043 }
2044 }
2045
2046 // create a copy of the required action rows so that new rows can be added while enumerating
2047 WixActionRow[] copyOfRequiredActionRows = new WixActionRow[requiredActionRows.Count];
2048 requiredActionRows.CopyTo(copyOfRequiredActionRows, 0);
2049
2050 // build up dependency trees of the relatively scheduled actions
2051 foreach (WixActionRow actionRow in copyOfRequiredActionRows)
2052 {
2053 if (0 == actionRow.Sequence)
2054 {
2055 // check for standard actions that don't have a sequence number in a merge module
2056 if (OutputType.Module == this.activeOutput.Type && WindowsInstallerStandard.IsStandardAction(actionRow.Action))
2057 {
2058 this.OnMessage(WixErrors.StandardActionRelativelyScheduledInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
2059 }
2060
2061 this.SequenceActionRow(actionRow, requiredActionRows);
2062 }
2063 else if (OutputType.Module == this.activeOutput.Type && 0 < actionRow.Sequence && !WindowsInstallerStandard.IsStandardAction(actionRow.Action)) // check for custom actions and dialogs that have a sequence number
2064 {
2065 this.OnMessage(WixErrors.CustomActionSequencedInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
2066 }
2067 }
2068
2069 // look for standard actions with sequence restrictions that aren't necessarily scheduled based on the presence of a particular table
2070 if (requiredActionRows.Contains(SequenceTable.InstallExecuteSequence, "DuplicateFiles") && !requiredActionRows.Contains(SequenceTable.InstallExecuteSequence, "InstallFiles"))
2071 {
2072 requiredActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallFiles"], true);
2073 }
2074
2075 // schedule actions
2076 if (OutputType.Module == this.activeOutput.Type)
2077 {
2078 // add the action row to the list of scheduled action rows
2079 scheduledActionRows.AddRange(requiredActionRows);
2080 }
2081 else
2082 {
2083 // process each sequence table individually
2084 foreach (SequenceTable sequenceTable in Enum.GetValues(typeof(SequenceTable)))
2085 {
2086 // create a collection of just the action rows in this sequence
2087 WixActionRowCollection sequenceActionRows = new WixActionRowCollection();
2088 foreach (WixActionRow actionRow in requiredActionRows)
2089 {
2090 if (sequenceTable == actionRow.SequenceTable)
2091 {
2092 sequenceActionRows.Add(actionRow);
2093 }
2094 }
2095
2096 // schedule the absolutely scheduled actions (by sorting them by their sequence numbers)
2097 ArrayList absoluteActionRows = new ArrayList();
2098 foreach (WixActionRow actionRow in sequenceActionRows)
2099 {
2100 if (0 != actionRow.Sequence)
2101 {
2102 // look for sequence number collisions
2103 foreach (WixActionRow sequenceScheduledActionRow in absoluteActionRows)
2104 {
2105 if (sequenceScheduledActionRow.Sequence == actionRow.Sequence)
2106 {
2107 this.OnMessage(WixWarnings.ActionSequenceCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, sequenceScheduledActionRow.Action, actionRow.Sequence));
2108 if (null != sequenceScheduledActionRow.SourceLineNumbers)
2109 {
2110 this.OnMessage(WixWarnings.ActionSequenceCollision2(sequenceScheduledActionRow.SourceLineNumbers));
2111 }
2112 }
2113 }
2114
2115 absoluteActionRows.Add(actionRow);
2116 }
2117 }
2118 absoluteActionRows.Sort();
2119
2120 // schedule the relatively scheduled actions (by resolving the dependency trees)
2121 int previousUsedSequence = 0;
2122 ArrayList relativeActionRows = new ArrayList();
2123 for (int j = 0; j < absoluteActionRows.Count; j++)
2124 {
2125 WixActionRow absoluteActionRow = (WixActionRow)absoluteActionRows[j];
2126 int unusedSequence;
2127
2128 // get all the relatively scheduled action rows occuring before this absolutely scheduled action row
2129 RowIndexedList<WixActionRow> allPreviousActionRows = new RowIndexedList<WixActionRow>();
2130 absoluteActionRow.GetAllPreviousActionRows(sequenceTable, allPreviousActionRows);
2131
2132 // get all the relatively scheduled action rows occuring after this absolutely scheduled action row
2133 RowIndexedList<WixActionRow> allNextActionRows = new RowIndexedList<WixActionRow>();
2134 absoluteActionRow.GetAllNextActionRows(sequenceTable, allNextActionRows);
2135
2136 // check for relatively scheduled actions occuring before/after a special action (these have a negative sequence number)
2137 if (0 > absoluteActionRow.Sequence && (0 < allPreviousActionRows.Count || 0 < allNextActionRows.Count))
2138 {
2139 // create errors for all the before actions
2140 foreach (WixActionRow actionRow in allPreviousActionRows)
2141 {
2142 this.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action));
2143 }
2144
2145 // create errors for all the after actions
2146 foreach (WixActionRow actionRow in allNextActionRows)
2147 {
2148 this.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action));
2149 }
2150
2151 // if there is source line information for the absolutely scheduled action display it
2152 if (null != absoluteActionRow.SourceLineNumbers)
2153 {
2154 this.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction2(absoluteActionRow.SourceLineNumbers));
2155 }
2156
2157 continue;
2158 }
2159
2160 // schedule the action rows before this one
2161 unusedSequence = absoluteActionRow.Sequence - 1;
2162 for (int i = allPreviousActionRows.Count - 1; i >= 0; i--)
2163 {
2164 WixActionRow relativeActionRow = (WixActionRow)allPreviousActionRows[i];
2165
2166 // look for collisions
2167 if (unusedSequence == previousUsedSequence)
2168 {
2169 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action));
2170 if (null != absoluteActionRow.SourceLineNumbers)
2171 {
2172 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers));
2173 }
2174
2175 unusedSequence++;
2176 }
2177
2178 relativeActionRow.Sequence = unusedSequence;
2179 relativeActionRows.Add(relativeActionRow);
2180
2181 unusedSequence--;
2182 }
2183
2184 // determine the next used action sequence number
2185 int nextUsedSequence;
2186 if (absoluteActionRows.Count > j + 1)
2187 {
2188 nextUsedSequence = ((WixActionRow)absoluteActionRows[j + 1]).Sequence;
2189 }
2190 else
2191 {
2192 nextUsedSequence = short.MaxValue + 1;
2193 }
2194
2195 // schedule the action rows after this one
2196 unusedSequence = absoluteActionRow.Sequence + 1;
2197 for (int i = 0; i < allNextActionRows.Count; i++)
2198 {
2199 WixActionRow relativeActionRow = (WixActionRow)allNextActionRows[i];
2200
2201 if (unusedSequence == nextUsedSequence)
2202 {
2203 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action));
2204 if (null != absoluteActionRow.SourceLineNumbers)
2205 {
2206 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers));
2207 }
2208
2209 unusedSequence--;
2210 }
2211
2212 relativeActionRow.Sequence = unusedSequence;
2213 relativeActionRows.Add(relativeActionRow);
2214
2215 unusedSequence++;
2216 }
2217
2218 // keep track of this sequence number as the previous used sequence number for the next iteration
2219 previousUsedSequence = absoluteActionRow.Sequence;
2220 }
2221
2222 // add the absolutely and relatively scheduled actions to the list of scheduled actions
2223 scheduledActionRows.AddRange(absoluteActionRows);
2224 scheduledActionRows.AddRange(relativeActionRows);
2225 }
2226 }
2227
2228 // create the action rows for sequences that are not suppressed
2229 foreach (WixActionRow actionRow in scheduledActionRows)
2230 {
2231 // get the table definition for the action (and ensure the proper table exists for a module)
2232 TableDefinition sequenceTableDefinition = null;
2233 switch (actionRow.SequenceTable)
2234 {
2235 case SequenceTable.AdminExecuteSequence:
2236 if (OutputType.Module == this.activeOutput.Type)
2237 {
2238 this.activeOutput.EnsureTable(this.tableDefinitions["AdminExecuteSequence"]);
2239 sequenceTableDefinition = this.tableDefinitions["ModuleAdminExecuteSequence"];
2240 }
2241 else
2242 {
2243 sequenceTableDefinition = this.tableDefinitions["AdminExecuteSequence"];
2244 }
2245 break;
2246 case SequenceTable.AdminUISequence:
2247 if (OutputType.Module == this.activeOutput.Type)
2248 {
2249 this.activeOutput.EnsureTable(this.tableDefinitions["AdminUISequence"]);
2250 sequenceTableDefinition = this.tableDefinitions["ModuleAdminUISequence"];
2251 }
2252 else
2253 {
2254 sequenceTableDefinition = this.tableDefinitions["AdminUISequence"];
2255 }
2256 break;
2257 case SequenceTable.AdvtExecuteSequence:
2258 if (OutputType.Module == this.activeOutput.Type)
2259 {
2260 this.activeOutput.EnsureTable(this.tableDefinitions["AdvtExecuteSequence"]);
2261 sequenceTableDefinition = this.tableDefinitions["ModuleAdvtExecuteSequence"];
2262 }
2263 else
2264 {
2265 sequenceTableDefinition = this.tableDefinitions["AdvtExecuteSequence"];
2266 }
2267 break;
2268 case SequenceTable.InstallExecuteSequence:
2269 if (OutputType.Module == this.activeOutput.Type)
2270 {
2271 this.activeOutput.EnsureTable(this.tableDefinitions["InstallExecuteSequence"]);
2272 sequenceTableDefinition = this.tableDefinitions["ModuleInstallExecuteSequence"];
2273 }
2274 else
2275 {
2276 sequenceTableDefinition = this.tableDefinitions["InstallExecuteSequence"];
2277 }
2278 break;
2279 case SequenceTable.InstallUISequence:
2280 if (OutputType.Module == this.activeOutput.Type)
2281 {
2282 this.activeOutput.EnsureTable(this.tableDefinitions["InstallUISequence"]);
2283 sequenceTableDefinition = this.tableDefinitions["ModuleInstallUISequence"];
2284 }
2285 else
2286 {
2287 sequenceTableDefinition = this.tableDefinitions["InstallUISequence"];
2288 }
2289 break;
2290 }
2291
2292 // create the action sequence row in the output
2293 Table sequenceTable = this.activeOutput.EnsureTable(sequenceTableDefinition);
2294 Row row = sequenceTable.CreateRow(actionRow.SourceLineNumbers);
2295 if (this.sectionIdOnRows)
2296 {
2297 row.SectionId = actionRow.SectionId;
2298 }
2299
2300 if (OutputType.Module == this.activeOutput.Type)
2301 {
2302 row[0] = actionRow.Action;
2303 if (0 != actionRow.Sequence)
2304 {
2305 row[1] = actionRow.Sequence;
2306 }
2307 else
2308 {
2309 bool after = (null == actionRow.Before);
2310 row[2] = after ? actionRow.After : actionRow.Before;
2311 row[3] = after ? 1 : 0;
2312 }
2313 row[4] = actionRow.Condition;
2314 }
2315 else
2316 {
2317 row[0] = actionRow.Action;
2318 row[1] = actionRow.Condition;
2319 row[2] = actionRow.Sequence;
2320 }
2321 }
2322 }
2323
2324 /// <summary>
2325 /// Sequence an action before or after a standard action.
2326 /// </summary>
2327 /// <param name="actionRow">The action row to be sequenced.</param>
2328 /// <param name="requiredActionRows">Collection of actions which must be included.</param>
2329 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
2330 private void SequenceActionRow(WixActionRow actionRow, WixActionRowCollection requiredActionRows)
2331 {
2332 bool after = false;
2333 if (actionRow.After != null)
2334 {
2335 after = true;
2336 }
2337 else if (actionRow.Before == null)
2338 {
2339 throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet);
2340 }
2341
2342 string parentActionName = (after ? actionRow.After : actionRow.Before);
2343 WixActionRow parentActionRow = requiredActionRows[actionRow.SequenceTable, parentActionName];
2344
2345 if (null == parentActionRow)
2346 {
2347 parentActionRow = this.standardActions[actionRow.SequenceTable, parentActionName];
2348
2349 // if the missing parent action is a standard action (with a suggested sequence number), add it
2350 if (null != parentActionRow)
2351 {
2352 // Create a clone to avoid modifying the static copy of the object.
2353 parentActionRow = parentActionRow.Clone();
2354 requiredActionRows.Add(parentActionRow);
2355 }
2356 else
2357 {
2358 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_FoundActionRowWinNonExistentAction, (after ? "After" : "Before"), parentActionName));
2359 }
2360 }
2361 else if (actionRow == parentActionRow || actionRow.ContainsChildActionRow(parentActionRow)) // cycle detected
2362 {
2363 throw new WixException(WixErrors.ActionCircularDependency(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, parentActionRow.Action));
2364 }
2365
2366 // Add this action to the appropriate list of dependent action rows.
2367 WixActionRowCollection relatedRows = (after ? parentActionRow.NextActionRows : parentActionRow.PreviousActionRows);
2368 relatedRows.Add(actionRow);
2369 }
2370
2371 /// <summary>
2372 /// Resolve features for columns that have null guid placeholders.
2373 /// </summary>
2374 /// <param name="rows">Rows to resolve.</param>
2375 /// <param name="connectionColumn">Number of the column containing the connection identifier.</param>
2376 /// <param name="featureColumn">Number of the column containing the feature.</param>
2377 /// <param name="connectToFeatures">Connect to feature complex references.</param>
2378 /// <param name="multipleFeatureComponents">Hashtable of known components under multiple features.</param>
2379 private void ResolveFeatures(IEnumerable<Row> rows, int connectionColumn, int featureColumn, ConnectToFeatureCollection connectToFeatures, Hashtable multipleFeatureComponents)
2380 {
2381 foreach (Row row in rows)
2382 {
2383 string connectionId = (string)row[connectionColumn];
2384 string featureId = (string)row[featureColumn];
2385
2386 if (emptyGuid == featureId)
2387 {
2388 ConnectToFeature connection = connectToFeatures[connectionId];
2389
2390 if (null == connection)
2391 {
2392 // display an error for the component or merge module as approrpriate
2393 if (null != multipleFeatureComponents)
2394 {
2395 this.OnMessage(WixErrors.ComponentExpectedFeature(row.SourceLineNumbers, connectionId, row.Table.Name, row.GetPrimaryKey('/')));
2396 }
2397 else
2398 {
2399 this.OnMessage(WixErrors.MergeModuleExpectedFeature(row.SourceLineNumbers, connectionId));
2400 }
2401 }
2402 else
2403 {
2404 // check for unique, implicit, primary feature parents with multiple possible parent features
2405 if (this.ShowPedanticMessages &&
2406 !connection.IsExplicitPrimaryFeature &&
2407 0 < connection.ConnectFeatures.Count)
2408 {
2409 // display a warning for the component or merge module as approrpriate
2410 if (null != multipleFeatureComponents)
2411 {
2412 if (!multipleFeatureComponents.Contains(connectionId))
2413 {
2414 this.OnMessage(WixWarnings.ImplicitComponentPrimaryFeature(connectionId));
2415
2416 // remember this component so only one warning is generated for it
2417 multipleFeatureComponents[connectionId] = null;
2418 }
2419 }
2420 else
2421 {
2422 this.OnMessage(WixWarnings.ImplicitMergeModulePrimaryFeature(connectionId));
2423 }
2424 }
2425
2426 // set the feature
2427 row[featureColumn] = connection.PrimaryFeature;
2428 }
2429 }
2430 }
2431 }
2432
2433 }
2434}