main
cs 764 lines 39.6 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core.WindowsInstaller.Bind
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data;
9 using WixToolset.Data.Symbols;
10 using WixToolset.Data.WindowsInstaller;
11 using WixToolset.Extensibility.Services;
12
13 /// <summary>
14 /// Set sequence numbers for all the actions and create symbols in the output object.
15 /// </summary>
16 internal class SequenceActionsCommand
17 {
18 public SequenceActionsCommand(IMessaging messaging, IntermediateSection section)
19 {
20 this.Messaging = messaging;
21 this.Section = section;
22
23 this.RelativeActionsForActions = new Dictionary<string, RelativeActions>();
24 }
25
26 private IMessaging Messaging { get; }
27
28 private IntermediateSection Section { get; }
29
30 private Dictionary<string, RelativeActions> RelativeActionsForActions { get; }
31
32 public Dictionary<string, SymbolDefinitionType> SymbolTypeForTable { get; } = new Dictionary<string, SymbolDefinitionType>
33 {
34 { "AppSearch", SymbolDefinitionType.AppSearch },
35 { "CCPSearch", SymbolDefinitionType.CCPSearch },
36 { "Class", SymbolDefinitionType.Class },
37 { "Complus", SymbolDefinitionType.Complus},
38 { "CreateFolder", SymbolDefinitionType.CreateFolder },
39 { "DuplicateFile", SymbolDefinitionType.DuplicateFile },
40 { "Environment", SymbolDefinitionType.Environment },
41 { "Extension", SymbolDefinitionType.Extension },
42 { "File", SymbolDefinitionType.File },
43 { "IniFile", SymbolDefinitionType.IniFile },
44 { "IsolatedComponent", SymbolDefinitionType.IsolatedComponent },
45 { "LaunchCondition", SymbolDefinitionType.LaunchCondition },
46 { "MIME", SymbolDefinitionType.MIME },
47 { "MoveFile", SymbolDefinitionType.MoveFile },
48 { "MsiAssembly", SymbolDefinitionType.Assembly },
49 { "MsiAssemblyName", SymbolDefinitionType.Assembly },
50 { "MsiServiceConfig", SymbolDefinitionType.LaunchCondition },
51 { "MsiServiceConfigFailureActions", SymbolDefinitionType.MsiServiceConfigFailureActions },
52 { "ODBCAttribute", SymbolDefinitionType.ODBCAttribute },
53 { "ODBCDataSource", SymbolDefinitionType.ODBCDataSource },
54 { "ODBCDriver", SymbolDefinitionType.ODBCDriver },
55 { "ODBCTranslator", SymbolDefinitionType.ODBCTranslator },
56 { "ODBCSourceAttribute", SymbolDefinitionType.ODBCSourceAttribute },
57 { "PublishComponent", SymbolDefinitionType.PublishComponent },
58 { "Registry", SymbolDefinitionType.Registry },
59 { "RemoveRegistry", SymbolDefinitionType.RemoveRegistry },
60 { "RemoveFile", SymbolDefinitionType.RemoveFile },
61 { "ServiceControl", SymbolDefinitionType.ServiceControl },
62 { "ServiceInstall", SymbolDefinitionType.ServiceInstall },
63 { "Shortcut", SymbolDefinitionType.Shortcut },
64 { "TypeLib", SymbolDefinitionType.TypeLib },
65 { "Upgrade", SymbolDefinitionType.Upgrade },
66 };
67
68 public void Execute()
69 {
70 var requiredActionSymbols = new Dictionary<string, WixActionSymbol>();
71
72 // Index all the action symbols and look for collisions.
73 foreach (var actionSymbol in this.Section.Symbols.OfType<WixActionSymbol>())
74 {
75 if (actionSymbol.Overridable) // overridable action
76 {
77 if (requiredActionSymbols.TryGetValue(actionSymbol.Id.Id, out var collidingActionSymbol))
78 {
79 if (collidingActionSymbol.Overridable)
80 {
81 this.Messaging.Write(ErrorMessages.OverridableActionCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
82 if (null != collidingActionSymbol.SourceLineNumbers)
83 {
84 this.Messaging.Write(ErrorMessages.OverridableActionCollision2(collidingActionSymbol.SourceLineNumbers));
85 }
86 }
87 }
88 else
89 {
90 requiredActionSymbols.Add(actionSymbol.Id.Id, actionSymbol);
91 }
92 }
93 else // unsequenced or sequenced action.
94 {
95 // Unsequenced action (allowed for certain standard actions).
96 if (null == actionSymbol.Before && null == actionSymbol.After && !actionSymbol.Sequence.HasValue)
97 {
98 if (WindowsInstallerStandard.TryGetStandardAction(actionSymbol.Id.Id, out var standardAction))
99 {
100 // Populate the sequence from the standard action
101 actionSymbol.Sequence = standardAction.Sequence;
102 }
103 else // not a supported unscheduled action.
104 {
105 throw new WixException($"Found action '{actionSymbol.Id.Id}' at {actionSymbol.SourceLineNumbers}' with no Sequence, Before, or After column set. The compiler should have prevented this.");
106 }
107 }
108
109 if (requiredActionSymbols.TryGetValue(actionSymbol.Id.Id, out var collidingActionSymbol) && !collidingActionSymbol.Overridable)
110 {
111 this.Messaging.Write(ErrorMessages.ActionCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
112 if (null != collidingActionSymbol.SourceLineNumbers)
113 {
114 this.Messaging.Write(ErrorMessages.ActionCollision2(collidingActionSymbol.SourceLineNumbers));
115 }
116 }
117 else
118 {
119 requiredActionSymbols[actionSymbol.Id.Id] = actionSymbol;
120 }
121 }
122 }
123
124 // Get the standard actions required based on symbols in the section.
125 var requiredStandardActions = this.GetRequiredStandardActions();
126
127 // Add the overridable action symbols that are not overridden to the required action symbols.
128 foreach (var actionSymbol in requiredStandardActions.Values)
129 {
130 if (!requiredActionSymbols.ContainsKey(actionSymbol.Id.Id))
131 {
132 requiredActionSymbols.Add(actionSymbol.Id.Id, actionSymbol);
133 }
134 }
135
136 // Suppress the required actions that are overridable.
137 foreach (var suppressActionSymbol in this.Section.Symbols.OfType<WixSuppressActionSymbol>())
138 {
139 var key = suppressActionSymbol.Id.Id;
140
141 // If there is an overridable symbol to suppress; suppress it. There is no warning if there
142 // is no action to suppress because the action may be suppressed from a merge module in
143 // the binder.
144 if (requiredActionSymbols.TryGetValue(key, out var requiredActionSymbol))
145 {
146 if (requiredActionSymbol.Overridable || requiredActionSymbol.Id.Access == AccessModifier.Virtual)
147 {
148 this.Messaging.Write(WarningMessages.SuppressAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.Action, suppressActionSymbol.SequenceTable.ToString()));
149 if (null != requiredActionSymbol.SourceLineNumbers)
150 {
151 this.Messaging.Write(WarningMessages.SuppressAction2(requiredActionSymbol.SourceLineNumbers));
152 }
153
154 requiredActionSymbols.Remove(key);
155 }
156 else // suppressing a non-overridable action symbol
157 {
158 this.Messaging.Write(ErrorMessages.SuppressNonoverridableAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.SequenceTable.ToString(), suppressActionSymbol.Action));
159 if (null != requiredActionSymbol.SourceLineNumbers)
160 {
161 this.Messaging.Write(ErrorMessages.SuppressNonoverridableAction2(requiredActionSymbol.SourceLineNumbers));
162 }
163 }
164 }
165 }
166
167 // A dictionary used for detecting cyclic references among action symbols.
168 var firstReference = new Dictionary<WixActionSymbol, WixActionSymbol>();
169
170 // Build up dependency trees of the relatively scheduled actions.
171 // Use ToList() to create a copy of the required action symbols so that new symbols can
172 // be added while enumerating.
173 foreach (var actionSymbol in requiredActionSymbols.Values.ToList())
174 {
175 if (!actionSymbol.Sequence.HasValue)
176 {
177 // check for standard actions that don't have a sequence number in a merge module
178 if (SectionType.Module == this.Section.Type && WindowsInstallerStandard.IsStandardAction(actionSymbol.Action))
179 {
180 this.Messaging.Write(ErrorMessages.StandardActionRelativelyScheduledInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
181 }
182
183 this.SequenceActionSymbol(actionSymbol, requiredActionSymbols, firstReference);
184 }
185 else if (SectionType.Module == this.Section.Type && 0 < actionSymbol.Sequence && !WindowsInstallerStandard.IsStandardAction(actionSymbol.Action)) // check for custom actions and dialogs that have a sequence number
186 {
187 this.Messaging.Write(ErrorMessages.CustomActionSequencedInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
188 }
189 }
190
191 // Look for standard actions with sequence restrictions that aren't necessarily scheduled based
192 // on the presence of a particular table.
193 if (requiredActionSymbols.ContainsKey("InstallExecuteSequence/DuplicateFiles") && !requiredActionSymbols.ContainsKey("InstallExecuteSequence/InstallFiles"))
194 {
195 WindowsInstallerStandard.TryGetStandardAction("InstallExecuteSequence/InstallFiles", out var standardAction);
196 requiredActionSymbols.Add(standardAction.Id.Id, standardAction);
197 }
198
199 // Schedule actions.
200 List<WixActionSymbol> scheduledActionSymbols;
201 if (SectionType.Module == this.Section.Type)
202 {
203 scheduledActionSymbols = requiredActionSymbols.Values.ToList();
204 }
205 else
206 {
207 scheduledActionSymbols = this.ScheduleActions(requiredActionSymbols);
208 }
209
210 // Remove all existing WixActionSymbols from the section then add the
211 // scheduled actions back to the section.
212 var removeActionSymbols = this.Section.Symbols.Where(s => s.Definition.Type == SymbolDefinitionType.WixAction).ToList();
213
214 foreach (var removeSymbol in removeActionSymbols)
215 {
216 this.Section.RemoveSymbol(removeSymbol);
217 }
218
219 foreach (var action in scheduledActionSymbols)
220 {
221 this.Section.AddSymbol(action);
222 }
223 }
224
225 private Dictionary<string, WixActionSymbol> GetRequiredStandardActions()
226 {
227 var overridableActionSymbols = new Dictionary<string, WixActionSymbol>();
228
229 var requiredActionIds = this.GetRequiredActionIds();
230
231 foreach (var actionId in requiredActionIds)
232 {
233 WindowsInstallerStandard.TryGetStandardAction(actionId, out var standardAction);
234 overridableActionSymbols.Add(standardAction.Id.Id, standardAction);
235 }
236
237 return overridableActionSymbols;
238 }
239
240 private List<WixActionSymbol> ScheduleActions(Dictionary<string, WixActionSymbol> requiredActionSymbols)
241 {
242 var scheduledActionSymbols = new List<WixActionSymbol>();
243
244 // Process each sequence table individually.
245 foreach (SequenceTable sequenceTable in Enum.GetValues(typeof(SequenceTable)))
246 {
247 // Create a collection of just the action symbols in this sequence
248 var sequenceActionSymbols = requiredActionSymbols.Values.Where(a => a.SequenceTable == sequenceTable).ToList();
249
250 // Schedule the absolutely scheduled actions (by sorting them by their sequence numbers).
251 var absoluteActionSymbols = new List<WixActionSymbol>();
252 foreach (var actionSymbol in sequenceActionSymbols)
253 {
254 if (actionSymbol.Sequence.HasValue)
255 {
256 // Look for sequence number collisions
257 foreach (var sequenceScheduledActionSymbol in absoluteActionSymbols)
258 {
259 if (sequenceScheduledActionSymbol.Sequence == actionSymbol.Sequence)
260 {
261 this.Messaging.Write(WarningMessages.ActionSequenceCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, sequenceScheduledActionSymbol.Action, actionSymbol.Sequence ?? 0));
262 if (null != sequenceScheduledActionSymbol.SourceLineNumbers)
263 {
264 this.Messaging.Write(WarningMessages.ActionSequenceCollision2(sequenceScheduledActionSymbol.SourceLineNumbers));
265 }
266 }
267 }
268
269 absoluteActionSymbols.Add(actionSymbol);
270 }
271 }
272
273 absoluteActionSymbols.Sort((x, y) => (x.Sequence ?? 0).CompareTo(y.Sequence ?? 0));
274
275 // Schedule the relatively scheduled actions (by resolving the dependency trees).
276 var previousUsedSequence = 0;
277 var relativeActionSymbols = new List<WixActionSymbol>();
278 for (var j = 0; j < absoluteActionSymbols.Count; j++)
279 {
280 var absoluteActionSymbol = absoluteActionSymbols[j];
281
282 // Get all the relatively scheduled action symbols occuring before and after this absolutely scheduled action symbol.
283 var relativeActions = this.GetAllRelativeActionsForSequenceType(sequenceTable, absoluteActionSymbol);
284
285 // Check for relatively scheduled actions occuring before/after a special action
286 // (those actions with a negative sequence number).
287 if (absoluteActionSymbol.Sequence < 0 && (relativeActions.PreviousActions.Any() || relativeActions.NextActions.Any()))
288 {
289 // Create errors for all the before actions.
290 foreach (var actionSymbol in relativeActions.PreviousActions)
291 {
292 this.Messaging.Write(ErrorMessages.ActionScheduledRelativeToTerminationAction(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, absoluteActionSymbol.Action));
293 }
294
295 // Create errors for all the after actions.
296 foreach (var actionSymbol in relativeActions.NextActions)
297 {
298 this.Messaging.Write(ErrorMessages.ActionScheduledRelativeToTerminationAction(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, absoluteActionSymbol.Action));
299 }
300
301 // If there is source line information for the absolutely scheduled action display it
302 if (absoluteActionSymbol.SourceLineNumbers != null)
303 {
304 this.Messaging.Write(ErrorMessages.ActionScheduledRelativeToTerminationAction2(absoluteActionSymbol.SourceLineNumbers));
305 }
306
307 continue;
308 }
309
310 // Schedule the action symbols before this one.
311 var unusedSequence = absoluteActionSymbol.Sequence - 1;
312 for (var i = relativeActions.PreviousActions.Count - 1; i >= 0; i--)
313 {
314 var relativeActionSymbol = relativeActions.PreviousActions[i];
315
316 // look for collisions
317 if (unusedSequence == previousUsedSequence)
318 {
319 this.Messaging.Write(ErrorMessages.NoUniqueActionSequenceNumber(relativeActionSymbol.SourceLineNumbers, relativeActionSymbol.SequenceTable.ToString(), relativeActionSymbol.Action, absoluteActionSymbol.Action));
320 if (absoluteActionSymbol.SourceLineNumbers != null)
321 {
322 this.Messaging.Write(ErrorMessages.NoUniqueActionSequenceNumber2(absoluteActionSymbol.SourceLineNumbers));
323 }
324
325 unusedSequence++;
326 }
327
328 relativeActionSymbol.Sequence = unusedSequence;
329 relativeActionSymbols.Add(relativeActionSymbol);
330
331 unusedSequence--;
332 }
333
334 // Determine the next used action sequence number.
335 var nextUsedSequence = Int16.MaxValue + 1;
336 if (absoluteActionSymbols.Count > j + 1)
337 {
338 nextUsedSequence = absoluteActionSymbols[j + 1].Sequence ?? 0;
339 }
340
341 // Schedule the action symbols after this one.
342 unusedSequence = absoluteActionSymbol.Sequence + 1;
343 for (var i = 0; i < relativeActions.NextActions.Count; i++)
344 {
345 var relativeActionSymbol = relativeActions.NextActions[i];
346
347 if (unusedSequence == nextUsedSequence)
348 {
349 this.Messaging.Write(ErrorMessages.NoUniqueActionSequenceNumber(relativeActionSymbol.SourceLineNumbers, relativeActionSymbol.SequenceTable.ToString(), relativeActionSymbol.Action, absoluteActionSymbol.Action));
350 if (absoluteActionSymbol.SourceLineNumbers != null)
351 {
352 this.Messaging.Write(ErrorMessages.NoUniqueActionSequenceNumber2(absoluteActionSymbol.SourceLineNumbers));
353 }
354
355 unusedSequence--;
356 }
357
358 relativeActionSymbol.Sequence = unusedSequence;
359 relativeActionSymbols.Add(relativeActionSymbol);
360
361 unusedSequence++;
362 }
363
364 // keep track of this sequence number as the previous used sequence number for the next iteration
365 previousUsedSequence = absoluteActionSymbol.Sequence ?? 0;
366 }
367
368 // add the absolutely and relatively scheduled actions to the list of scheduled actions
369 scheduledActionSymbols.AddRange(absoluteActionSymbols);
370 scheduledActionSymbols.AddRange(relativeActionSymbols);
371 }
372
373 return scheduledActionSymbols;
374 }
375
376 private IEnumerable<string> GetRequiredActionIds()
377 {
378 var set = new HashSet<string>();
379
380 // gather the required actions for the output type
381 if (SectionType.Package == this.Section.Type)
382 {
383 // AdminExecuteSequence table
384 set.Add("AdminExecuteSequence/CostFinalize");
385 set.Add("AdminExecuteSequence/CostInitialize");
386 set.Add("AdminExecuteSequence/FileCost");
387 set.Add("AdminExecuteSequence/InstallAdminPackage");
388 set.Add("AdminExecuteSequence/InstallFiles");
389 set.Add("AdminExecuteSequence/InstallFinalize");
390 set.Add("AdminExecuteSequence/InstallInitialize");
391 set.Add("AdminExecuteSequence/InstallValidate");
392
393 // AdminUISequence table
394 set.Add("AdminUISequence/CostFinalize");
395 set.Add("AdminUISequence/CostInitialize");
396 set.Add("AdminUISequence/ExecuteAction");
397 set.Add("AdminUISequence/FileCost");
398
399 // AdvtExecuteSequence table
400 set.Add("AdvertiseExecuteSequence/CostFinalize");
401 set.Add("AdvertiseExecuteSequence/CostInitialize");
402 set.Add("AdvertiseExecuteSequence/InstallInitialize");
403 set.Add("AdvertiseExecuteSequence/InstallFinalize");
404 set.Add("AdvertiseExecuteSequence/InstallValidate");
405 set.Add("AdvertiseExecuteSequence/PublishFeatures");
406 set.Add("AdvertiseExecuteSequence/PublishProduct");
407
408 // InstallExecuteSequence table
409 set.Add("InstallExecuteSequence/CostFinalize");
410 set.Add("InstallExecuteSequence/CostInitialize");
411 set.Add("InstallExecuteSequence/FileCost");
412 set.Add("InstallExecuteSequence/InstallFinalize");
413 set.Add("InstallExecuteSequence/InstallInitialize");
414 set.Add("InstallExecuteSequence/InstallValidate");
415 set.Add("InstallExecuteSequence/ProcessComponents");
416 set.Add("InstallExecuteSequence/PublishFeatures");
417 set.Add("InstallExecuteSequence/PublishProduct");
418 set.Add("InstallExecuteSequence/RegisterProduct");
419 set.Add("InstallExecuteSequence/RegisterUser");
420 set.Add("InstallExecuteSequence/UnpublishFeatures");
421 set.Add("InstallExecuteSequence/ValidateProductID");
422
423 // InstallUISequence table
424 set.Add("InstallUISequence/CostFinalize");
425 set.Add("InstallUISequence/CostInitialize");
426 set.Add("InstallUISequence/ExecuteAction");
427 set.Add("InstallUISequence/FileCost");
428 set.Add("InstallUISequence/ValidateProductID");
429 }
430
431 // Gather the required actions for each symbol type.
432 foreach (var symbolType in this.Section.Symbols.Select(t => t.Definition.Type).Distinct())
433 {
434 this.GetActionsForSymbolType(symbolType, set);
435 }
436
437 // Gather the required actions for each ensured table.
438 foreach (var ensureTable in this.Section.Symbols.OfType<WixEnsureTableSymbol>())
439 {
440 if (this.SymbolTypeForTable.TryGetValue(ensureTable.Table, out var symbolType))
441 {
442 this.GetActionsForSymbolType(symbolType, set);
443 }
444 }
445
446 return set;
447 }
448
449 private void GetActionsForSymbolType(SymbolDefinitionType symbolType, HashSet<string> set)
450 {
451 switch (symbolType)
452 {
453 case SymbolDefinitionType.AppSearch:
454 set.Add("InstallExecuteSequence/AppSearch");
455 set.Add("InstallUISequence/AppSearch");
456 break;
457 case SymbolDefinitionType.CCPSearch:
458 set.Add("InstallExecuteSequence/AppSearch");
459 set.Add("InstallExecuteSequence/CCPSearch");
460 set.Add("InstallExecuteSequence/RMCCPSearch");
461 set.Add("InstallUISequence/AppSearch");
462 set.Add("InstallUISequence/CCPSearch");
463 set.Add("InstallUISequence/RMCCPSearch");
464 break;
465 case SymbolDefinitionType.Class:
466 set.Add("AdvertiseExecuteSequence/RegisterClassInfo");
467 set.Add("InstallExecuteSequence/RegisterClassInfo");
468 set.Add("InstallExecuteSequence/UnregisterClassInfo");
469 break;
470 case SymbolDefinitionType.Complus:
471 set.Add("InstallExecuteSequence/RegisterComPlus");
472 set.Add("InstallExecuteSequence/UnregisterComPlus");
473 break;
474 case SymbolDefinitionType.Component:
475 case SymbolDefinitionType.CreateFolder:
476 set.Add("InstallExecuteSequence/CreateFolders");
477 set.Add("InstallExecuteSequence/RemoveFolders");
478 break;
479 case SymbolDefinitionType.DuplicateFile:
480 set.Add("InstallExecuteSequence/DuplicateFiles");
481 set.Add("InstallExecuteSequence/RemoveDuplicateFiles");
482 break;
483 case SymbolDefinitionType.Environment:
484 set.Add("InstallExecuteSequence/WriteEnvironmentStrings");
485 set.Add("InstallExecuteSequence/RemoveEnvironmentStrings");
486 break;
487 case SymbolDefinitionType.Extension:
488 set.Add("AdvertiseExecuteSequence/RegisterExtensionInfo");
489 set.Add("InstallExecuteSequence/RegisterExtensionInfo");
490 set.Add("InstallExecuteSequence/UnregisterExtensionInfo");
491 break;
492 case SymbolDefinitionType.File:
493 set.Add("InstallExecuteSequence/InstallFiles");
494 set.Add("InstallExecuteSequence/RemoveFiles");
495
496 var foundFont = false;
497 var foundSelfReg = false;
498 var foundBindPath = false;
499 foreach (var file in this.Section.Symbols.OfType<FileSymbol>())
500 {
501 // Note that TrueType fonts are denoted by the empty string in the FontTitle
502 // field. So, non-null means a font is present.
503 if (!foundFont && file.FontTitle != null)
504 {
505 set.Add("InstallExecuteSequence/RegisterFonts");
506 set.Add("InstallExecuteSequence/UnregisterFonts");
507 foundFont = true;
508 }
509
510 if (!foundSelfReg && file.SelfRegCost.HasValue)
511 {
512 set.Add("InstallExecuteSequence/SelfRegModules");
513 set.Add("InstallExecuteSequence/SelfUnregModules");
514 foundSelfReg = true;
515 }
516
517 if (!foundBindPath && !String.IsNullOrEmpty(file.BindPath))
518 {
519 set.Add("InstallExecuteSequence/BindImage");
520 foundBindPath = true;
521 }
522 }
523 break;
524 case SymbolDefinitionType.IniFile:
525 set.Add("InstallExecuteSequence/WriteIniValues");
526 set.Add("InstallExecuteSequence/RemoveIniValues");
527 break;
528 case SymbolDefinitionType.IsolatedComponent:
529 set.Add("InstallExecuteSequence/IsolateComponents");
530 break;
531 case SymbolDefinitionType.LaunchCondition:
532 set.Add("InstallExecuteSequence/LaunchConditions");
533 set.Add("InstallUISequence/LaunchConditions");
534 break;
535 case SymbolDefinitionType.MIME:
536 set.Add("AdvertiseExecuteSequence/RegisterMIMEInfo");
537 set.Add("InstallExecuteSequence/RegisterMIMEInfo");
538 set.Add("InstallExecuteSequence/UnregisterMIMEInfo");
539 break;
540 case SymbolDefinitionType.MoveFile:
541 set.Add("InstallExecuteSequence/MoveFiles");
542 break;
543 case SymbolDefinitionType.Assembly:
544 set.Add("AdvertiseExecuteSequence/MsiPublishAssemblies");
545 set.Add("InstallExecuteSequence/MsiPublishAssemblies");
546 set.Add("InstallExecuteSequence/MsiUnpublishAssemblies");
547 break;
548 case SymbolDefinitionType.MsiServiceConfig:
549 case SymbolDefinitionType.MsiServiceConfigFailureActions:
550 set.Add("InstallExecuteSequence/MsiConfigureServices");
551 break;
552 case SymbolDefinitionType.ODBCDataSource:
553 case SymbolDefinitionType.ODBCTranslator:
554 case SymbolDefinitionType.ODBCDriver:
555 set.Add("InstallExecuteSequence/SetODBCFolders");
556 set.Add("InstallExecuteSequence/InstallODBC");
557 set.Add("InstallExecuteSequence/RemoveODBC");
558 break;
559 case SymbolDefinitionType.ProgId:
560 set.Add("AdvertiseExecuteSequence/RegisterProgIdInfo");
561 set.Add("InstallExecuteSequence/RegisterProgIdInfo");
562 set.Add("InstallExecuteSequence/UnregisterProgIdInfo");
563 break;
564 case SymbolDefinitionType.PublishComponent:
565 set.Add("AdvertiseExecuteSequence/PublishComponents");
566 set.Add("InstallExecuteSequence/PublishComponents");
567 set.Add("InstallExecuteSequence/UnpublishComponents");
568 break;
569 case SymbolDefinitionType.Registry:
570 case SymbolDefinitionType.RemoveRegistry:
571 set.Add("InstallExecuteSequence/WriteRegistryValues");
572 set.Add("InstallExecuteSequence/RemoveRegistryValues");
573 break;
574 case SymbolDefinitionType.RemoveFile:
575 set.Add("InstallExecuteSequence/RemoveFiles");
576 break;
577 case SymbolDefinitionType.ServiceControl:
578 set.Add("InstallExecuteSequence/StartServices");
579 set.Add("InstallExecuteSequence/StopServices");
580 set.Add("InstallExecuteSequence/DeleteServices");
581 break;
582 case SymbolDefinitionType.ServiceInstall:
583 set.Add("InstallExecuteSequence/InstallServices");
584 break;
585 case SymbolDefinitionType.Shortcut:
586 set.Add("AdvertiseExecuteSequence/CreateShortcuts");
587 set.Add("InstallExecuteSequence/CreateShortcuts");
588 set.Add("InstallExecuteSequence/RemoveShortcuts");
589 break;
590 case SymbolDefinitionType.TypeLib:
591 set.Add("InstallExecuteSequence/RegisterTypeLibraries");
592 set.Add("InstallExecuteSequence/UnregisterTypeLibraries");
593 break;
594 case SymbolDefinitionType.Upgrade:
595 set.Add("InstallExecuteSequence/FindRelatedProducts");
596 set.Add("InstallUISequence/FindRelatedProducts");
597
598 // Only add the MigrateFeatureStates action if MigrateFeature attribute is set on
599 // at least one UpgradeVersion element.
600 if (this.Section.Symbols.OfType<UpgradeSymbol>().Any(t => t.MigrateFeatures))
601 {
602 set.Add("InstallExecuteSequence/MigrateFeatureStates");
603 set.Add("InstallUISequence/MigrateFeatureStates");
604 }
605 break;
606 }
607 }
608
609 /// <summary>
610 /// Sequence an action before or after a standard action.
611 /// </summary>
612 /// <param name="actionSymbol">The action symbol to be sequenced.</param>
613 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
614 /// <param name="firstReference">A dictionary used for detecting cyclic references among action symbols.</param>
615 private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols, Dictionary<WixActionSymbol, WixActionSymbol> firstReference)
616 {
617 var after = false;
618
619 if (actionSymbol.After != null)
620 {
621 after = true;
622 }
623 else if (actionSymbol.Before == null)
624 {
625 throw new WixException($"Found action '{actionSymbol.Id.Id}' at {actionSymbol.SourceLineNumbers}' with no Sequence, Before, or After column set. The compiler should have prevented this.");
626 }
627
628 var parentActionName = (after ? actionSymbol.After : actionSymbol.Before);
629 var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + parentActionName;
630
631 if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
632 {
633 // If the missing parent action is a standard action (with a suggested sequence number), add it.
634 if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol))
635 {
636 // Create a clone to avoid modifying the static copy of the object.
637 // TODO: consider this: parentActionSymbol = parentActionSymbol.Clone();
638
639 requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol);
640 }
641 else
642 {
643 throw new WixException($"Found action {actionSymbol.Id.Id} with a non-existent {(after ? "After" : "Before")} action '{parentActionName}'. The linker should have prevented this.");
644 }
645 }
646
647 this.CheckForCircularActionReference(actionSymbol, requiredActionSymbols, firstReference);
648
649 // Add this action to the appropriate list of dependent action symbols.
650 var relativeActions = this.GetRelativeActions(parentActionSymbol);
651 var relatedSymbols = (after ? relativeActions.NextActions : relativeActions.PreviousActions);
652 relatedSymbols.Add(actionSymbol);
653 }
654
655 /// <summary>
656 /// Check the specified action symbol to see if it leads to a cycle.
657 /// </summary>
658 /// <para> Use the provided dictionary to note the initial action symbol that first led to each action
659 /// symbol. Any action symbol encountered that has already been encountered starting from a different
660 /// initial action symbol inherits the loop characteristics of that initial action symbol, and thus is
661 /// also not part of a cycle. However, any action symbol encountered that has already been encountered
662 /// starting from the same initial action symbol is an indication that the current action symbol is
663 /// part of a cycle.
664 /// </para>
665 /// <param name="actionSymbol">The action symbol to be checked.</param>
666 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
667 /// <param name="firstReference">The first encountered action symbol that led to each action symbol.</param>
668 private void CheckForCircularActionReference(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols, Dictionary<WixActionSymbol, WixActionSymbol> firstReference)
669 {
670 WixActionSymbol currentActionSymbol = null;
671 var parentActionSymbol = actionSymbol;
672
673 do
674 {
675 var previousActionSymbol = currentActionSymbol ?? parentActionSymbol;
676 currentActionSymbol = parentActionSymbol;
677
678 if (!firstReference.TryGetValue(currentActionSymbol, out var existingInitialActionSymbol))
679 {
680 firstReference[currentActionSymbol] = actionSymbol;
681 }
682 else if (existingInitialActionSymbol == actionSymbol)
683 {
684 this.Messaging.Write(ErrorMessages.ActionCircularDependency(currentActionSymbol.SourceLineNumbers, currentActionSymbol.SequenceTable.ToString(), currentActionSymbol.Action, previousActionSymbol.Action));
685 }
686
687 parentActionSymbol = this.GetParentActionSymbol(currentActionSymbol, requiredActionSymbols);
688 } while (null != parentActionSymbol && !this.Messaging.EncounteredError);
689 }
690
691 /// <summary>
692 /// Get the action symbol that is the parent of the given action symbol.
693 /// </summary>
694 /// <param name="actionSymbol">The given action symbol.</param>
695 /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
696 /// <returns>Null if there is no parent. Used for loop termination.</returns>
697 private WixActionSymbol GetParentActionSymbol(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols)
698 {
699 if (null == actionSymbol.Before && null == actionSymbol.After)
700 {
701 return null;
702 }
703
704 var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + (actionSymbol.After ?? actionSymbol.Before);
705
706 if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
707 {
708 WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol);
709 }
710
711 return parentActionSymbol;
712 }
713
714 private RelativeActions GetRelativeActions(WixActionSymbol action)
715 {
716 if (!this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var relativeActions))
717 {
718 relativeActions = new RelativeActions();
719 this.RelativeActionsForActions.Add(action.Id.Id, relativeActions);
720 }
721
722 return relativeActions;
723 }
724
725 private RelativeActions GetAllRelativeActionsForSequenceType(SequenceTable sequenceType, WixActionSymbol action)
726 {
727 var relativeActions = new RelativeActions();
728
729 if (this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var actionRelatives))
730 {
731 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.PreviousActions, relativeActions.PreviousActions);
732
733 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.NextActions, relativeActions.NextActions);
734 }
735
736 return relativeActions;
737 }
738
739 private void RecurseRelativeActionsForSequenceType(SequenceTable sequenceType, List<WixActionSymbol> actions, List<WixActionSymbol> visitedActions)
740 {
741 foreach (var action in actions.Where(a => a.SequenceTable == sequenceType))
742 {
743 if (this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var actionRelatives))
744 {
745 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.PreviousActions, visitedActions);
746 }
747
748 visitedActions.Add(action);
749
750 if (actionRelatives != null)
751 {
752 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.NextActions, visitedActions);
753 }
754 }
755 }
756
757 private class RelativeActions
758 {
759 public List<WixActionSymbol> PreviousActions { get; } = new List<WixActionSymbol>();
760
761 public List<WixActionSymbol> NextActions { get; } = new List<WixActionSymbol>();
762 }
763 }
764 }