diff options
Diffstat (limited to 'src/WixToolset.Core/Linker.cs')
-rw-r--r-- | src/WixToolset.Core/Linker.cs | 2434 |
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 | |||
3 | namespace 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 | } | ||