diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs')
-rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs new file mode 100644 index 00000000..cf9c0332 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs | |||
@@ -0,0 +1,671 @@ | |||
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.Globalization; | ||
8 | using System.Linq; | ||
9 | using WixToolset.Core.Native; | ||
10 | using WixToolset.Data; | ||
11 | using WixToolset.Data.Tuples; | ||
12 | |||
13 | internal class SequenceActionsCommand | ||
14 | { | ||
15 | public SequenceActionsCommand(IntermediateSection section) | ||
16 | { | ||
17 | this.Section = section; | ||
18 | |||
19 | this.RelativeActionsForActions = new Dictionary<string, RelativeActions>(); | ||
20 | |||
21 | this.StandardActionsById = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id); | ||
22 | } | ||
23 | |||
24 | private IntermediateSection Section { get; } | ||
25 | |||
26 | private Dictionary<string, RelativeActions> RelativeActionsForActions { get; } | ||
27 | |||
28 | private Dictionary<string, WixActionTuple> StandardActionsById { get; } | ||
29 | |||
30 | public Messaging Messaging { private get; set; } | ||
31 | |||
32 | public void Execute() | ||
33 | { | ||
34 | var actions = this.Section.Tuples.OfType<WixActionTuple>().ToList(); | ||
35 | var suppressActions = this.Section.Tuples.OfType<WixSuppressActionTuple>().ToList(); | ||
36 | |||
37 | this.SequenceActions(actions, suppressActions); | ||
38 | } | ||
39 | |||
40 | /// <summary> | ||
41 | /// Set sequence numbers for all the actions and create rows in the output object. | ||
42 | /// </summary> | ||
43 | /// <param name="actionRows">Collection of actions to schedule.</param> | ||
44 | /// <param name="suppressActionRows">Collection of actions to suppress.</param> | ||
45 | private void SequenceActions(List<WixActionTuple> actionRows, List<WixSuppressActionTuple> suppressActionRows) | ||
46 | { | ||
47 | var overridableActionRows = new Dictionary<string, WixActionTuple>(); | ||
48 | var requiredActionRows = new Dictionary<string, WixActionTuple>(); | ||
49 | |||
50 | // Get the standard actions required based on tuples in the section. | ||
51 | var requiredActionIds = this.GetRequiredActionIds(); | ||
52 | |||
53 | foreach (var actionId in requiredActionIds) | ||
54 | { | ||
55 | var standardAction = this.StandardActionsById[actionId]; | ||
56 | |||
57 | overridableActionRows.Add(standardAction.Id.Id, standardAction); | ||
58 | } | ||
59 | |||
60 | // Index all the action rows and look for collisions. | ||
61 | foreach (var actionRow in this.Section.Tuples.OfType<WixActionTuple>()) | ||
62 | { | ||
63 | if (actionRow.Overridable) // overridable action | ||
64 | { | ||
65 | if (overridableActionRows.TryGetValue(actionRow.Id.Id, out var collidingActionRow)) | ||
66 | { | ||
67 | this.Messaging.OnMessage(WixErrors.OverridableActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action)); | ||
68 | if (null != collidingActionRow.SourceLineNumbers) | ||
69 | { | ||
70 | this.Messaging.OnMessage(WixErrors.OverridableActionCollision2(collidingActionRow.SourceLineNumbers)); | ||
71 | } | ||
72 | } | ||
73 | else | ||
74 | { | ||
75 | overridableActionRows.Add(actionRow.Id.Id, actionRow); | ||
76 | } | ||
77 | } | ||
78 | else // unsequenced or sequenced action. | ||
79 | { | ||
80 | // Unsequenced action (allowed for certain standard actions). | ||
81 | if (null == actionRow.Before && null == actionRow.After && 0 == actionRow.Sequence) | ||
82 | { | ||
83 | if (this.StandardActionsById.TryGetValue(actionRow.Id.Id, out var standardAction)) | ||
84 | { | ||
85 | // Populate the sequence from the standard action | ||
86 | actionRow.Sequence = standardAction.Sequence; | ||
87 | } | ||
88 | else // not a supported unscheduled action. | ||
89 | { | ||
90 | throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | if (overridableActionRows.TryGetValue(actionRow.Id.Id, out var collidingActionRow)) | ||
95 | { | ||
96 | this.Messaging.OnMessage(WixErrors.ActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action)); | ||
97 | if (null != collidingActionRow.SourceLineNumbers) | ||
98 | { | ||
99 | this.Messaging.OnMessage(WixErrors.ActionCollision2(collidingActionRow.SourceLineNumbers)); | ||
100 | } | ||
101 | } | ||
102 | else | ||
103 | { | ||
104 | requiredActionRows.Add(actionRow.Id.Id, actionRow); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | // Add the overridable action rows that are not overridden to the required action rows. | ||
110 | foreach (var actionRow in overridableActionRows.Values) | ||
111 | { | ||
112 | if (!requiredActionRows.ContainsKey(actionRow.Id.Id)) | ||
113 | { | ||
114 | requiredActionRows.Add(actionRow.Id.Id, actionRow); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | // Suppress the required actions that are overridable. | ||
119 | foreach (var suppressActionRow in suppressActionRows) | ||
120 | { | ||
121 | var key = suppressActionRow.Id.Id; | ||
122 | |||
123 | // If there is an overridable row to suppress; suppress it. There is no warning if there | ||
124 | // is no action to suppress because the action may be suppressed from a merge module in | ||
125 | // the binder. | ||
126 | if (requiredActionRows.TryGetValue(key, out var requiredActionRow)) | ||
127 | { | ||
128 | if (requiredActionRow.Overridable) | ||
129 | { | ||
130 | this.Messaging.OnMessage(WixWarnings.SuppressAction(suppressActionRow.SourceLineNumbers, suppressActionRow.Action, suppressActionRow.SequenceTable.ToString())); | ||
131 | if (null != requiredActionRow.SourceLineNumbers) | ||
132 | { | ||
133 | this.Messaging.OnMessage(WixWarnings.SuppressAction2(requiredActionRow.SourceLineNumbers)); | ||
134 | } | ||
135 | |||
136 | requiredActionRows.Remove(key); | ||
137 | } | ||
138 | else // suppressing a non-overridable action row | ||
139 | { | ||
140 | this.Messaging.OnMessage(WixErrors.SuppressNonoverridableAction(suppressActionRow.SourceLineNumbers, suppressActionRow.SequenceTable.ToString(), suppressActionRow.Action)); | ||
141 | if (null != requiredActionRow.SourceLineNumbers) | ||
142 | { | ||
143 | this.Messaging.OnMessage(WixErrors.SuppressNonoverridableAction2(requiredActionRow.SourceLineNumbers)); | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Build up dependency trees of the relatively scheduled actions. | ||
150 | // Use ToList() to create a copy of the required action rows so that new tuples can | ||
151 | // be added while enumerating. | ||
152 | foreach (var actionRow in requiredActionRows.Values.ToList()) | ||
153 | { | ||
154 | if (0 == actionRow.Sequence) | ||
155 | { | ||
156 | // check for standard actions that don't have a sequence number in a merge module | ||
157 | if (SectionType.Module == this.Section.Type && WindowsInstallerStandard.IsStandardAction(actionRow.Action)) | ||
158 | { | ||
159 | this.Messaging.OnMessage(WixErrors.StandardActionRelativelyScheduledInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action)); | ||
160 | } | ||
161 | |||
162 | this.SequenceActionRow(actionRow, requiredActionRows); | ||
163 | } | ||
164 | else if (SectionType.Module == this.Section.Type && 0 < actionRow.Sequence && !WindowsInstallerStandard.IsStandardAction(actionRow.Action)) // check for custom actions and dialogs that have a sequence number | ||
165 | { | ||
166 | this.Messaging.OnMessage(WixErrors.CustomActionSequencedInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action)); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | // Look for standard actions with sequence restrictions that aren't necessarily scheduled based | ||
171 | // on the presence of a particular table. | ||
172 | if (requiredActionRows.ContainsKey("InstallExecuteSequence/DuplicateFiles") && !requiredActionRows.ContainsKey("InstallExecuteSequence/InstallFiles")) | ||
173 | { | ||
174 | var standardAction = this.StandardActionsById["InstallExecuteSequence/InstallFiles"]; | ||
175 | requiredActionRows.Add(standardAction.Id.Id, standardAction); | ||
176 | } | ||
177 | |||
178 | // Schedule actions. | ||
179 | List<WixActionTuple> scheduledActionRows; | ||
180 | if (SectionType.Module == this.Section.Type) | ||
181 | { | ||
182 | scheduledActionRows = requiredActionRows.Values.ToList(); | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | scheduledActionRows = ScheduleActions(requiredActionRows); | ||
187 | } | ||
188 | |||
189 | // Remove all existing WixActionTuples from the section then add the | ||
190 | // scheduled actions back to the section. Note: we add the indices in | ||
191 | // reverse order to make it easy to remove them from the list later. | ||
192 | var removeIndices = new List<int>(); | ||
193 | for (var i = this.Section.Tuples.Count - 1; i >= 0; --i) | ||
194 | { | ||
195 | var tuple = this.Section.Tuples[i]; | ||
196 | if (tuple.Definition.Type == TupleDefinitionType.WixAction) | ||
197 | { | ||
198 | removeIndices.Add(i); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | foreach (var removeIndex in removeIndices) | ||
203 | { | ||
204 | this.Section.Tuples.RemoveAt(removeIndex); | ||
205 | } | ||
206 | |||
207 | foreach (var action in scheduledActionRows) | ||
208 | { | ||
209 | this.Section.Tuples.Add(action); | ||
210 | } | ||
211 | } | ||
212 | |||
213 | private List<WixActionTuple> ScheduleActions(Dictionary<string, WixActionTuple> requiredActionRows) | ||
214 | { | ||
215 | var scheduledActionRows = new List<WixActionTuple>(); | ||
216 | |||
217 | // Process each sequence table individually. | ||
218 | foreach (SequenceTable sequenceTable in Enum.GetValues(typeof(SequenceTable))) | ||
219 | { | ||
220 | // Create a collection of just the action rows in this sequence | ||
221 | var sequenceActionRows = requiredActionRows.Values.Where(a => a.SequenceTable == sequenceTable).ToList(); | ||
222 | |||
223 | // Schedule the absolutely scheduled actions (by sorting them by their sequence numbers). | ||
224 | var absoluteActionRows = new List<WixActionTuple>(); | ||
225 | foreach (var actionRow in sequenceActionRows) | ||
226 | { | ||
227 | if (0 != actionRow.Sequence) | ||
228 | { | ||
229 | // Look for sequence number collisions | ||
230 | foreach (var sequenceScheduledActionRow in absoluteActionRows) | ||
231 | { | ||
232 | if (sequenceScheduledActionRow.Sequence == actionRow.Sequence) | ||
233 | { | ||
234 | this.Messaging.OnMessage(WixWarnings.ActionSequenceCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, sequenceScheduledActionRow.Action, actionRow.Sequence)); | ||
235 | if (null != sequenceScheduledActionRow.SourceLineNumbers) | ||
236 | { | ||
237 | this.Messaging.OnMessage(WixWarnings.ActionSequenceCollision2(sequenceScheduledActionRow.SourceLineNumbers)); | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | absoluteActionRows.Add(actionRow); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | absoluteActionRows.Sort((x, y) => x.Sequence.CompareTo(y.Sequence)); | ||
247 | |||
248 | // Schedule the relatively scheduled actions (by resolving the dependency trees). | ||
249 | var previousUsedSequence = 0; | ||
250 | var relativeActionRows = new List<WixActionTuple>(); | ||
251 | for (int j = 0; j < absoluteActionRows.Count; j++) | ||
252 | { | ||
253 | var absoluteActionRow = absoluteActionRows[j]; | ||
254 | |||
255 | // Get all the relatively scheduled action rows occuring before and after this absolutely scheduled action row. | ||
256 | var relativeActions = this.GetAllRelativeActionsForSequenceType(sequenceTable, absoluteActionRow); | ||
257 | |||
258 | // Check for relatively scheduled actions occuring before/after a special action | ||
259 | // (those actions with a negative sequence number). | ||
260 | if (absoluteActionRow.Sequence < 0 && (relativeActions.PreviousActions.Any() || relativeActions.NextActions.Any())) | ||
261 | { | ||
262 | // Create errors for all the before actions. | ||
263 | foreach (var actionRow in relativeActions.PreviousActions) | ||
264 | { | ||
265 | this.Messaging.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action)); | ||
266 | } | ||
267 | |||
268 | // Create errors for all the after actions. | ||
269 | foreach (var actionRow in relativeActions.NextActions) | ||
270 | { | ||
271 | this.Messaging.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action)); | ||
272 | } | ||
273 | |||
274 | // If there is source line information for the absolutely scheduled action display it | ||
275 | if (absoluteActionRow.SourceLineNumbers != null) | ||
276 | { | ||
277 | this.Messaging.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction2(absoluteActionRow.SourceLineNumbers)); | ||
278 | } | ||
279 | |||
280 | continue; | ||
281 | } | ||
282 | |||
283 | // Schedule the action rows before this one. | ||
284 | var unusedSequence = absoluteActionRow.Sequence - 1; | ||
285 | for (var i = relativeActions.PreviousActions.Count - 1; i >= 0; i--) | ||
286 | { | ||
287 | var relativeActionRow = relativeActions.PreviousActions[i]; | ||
288 | |||
289 | // look for collisions | ||
290 | if (unusedSequence == previousUsedSequence) | ||
291 | { | ||
292 | this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action)); | ||
293 | if (absoluteActionRow.SourceLineNumbers != null) | ||
294 | { | ||
295 | this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers)); | ||
296 | } | ||
297 | |||
298 | unusedSequence++; | ||
299 | } | ||
300 | |||
301 | relativeActionRow.Sequence = unusedSequence; | ||
302 | relativeActionRows.Add(relativeActionRow); | ||
303 | |||
304 | unusedSequence--; | ||
305 | } | ||
306 | |||
307 | // Determine the next used action sequence number. | ||
308 | var nextUsedSequence = Int16.MaxValue + 1; | ||
309 | if (absoluteActionRows.Count > j + 1) | ||
310 | { | ||
311 | nextUsedSequence = absoluteActionRows[j + 1].Sequence; | ||
312 | } | ||
313 | |||
314 | // Schedule the action rows after this one. | ||
315 | unusedSequence = absoluteActionRow.Sequence + 1; | ||
316 | for (var i = 0; i < relativeActions.NextActions.Count; i++) | ||
317 | { | ||
318 | var relativeActionRow = relativeActions.NextActions[i]; | ||
319 | |||
320 | if (unusedSequence == nextUsedSequence) | ||
321 | { | ||
322 | this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action)); | ||
323 | if (absoluteActionRow.SourceLineNumbers != null) | ||
324 | { | ||
325 | this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers)); | ||
326 | } | ||
327 | |||
328 | unusedSequence--; | ||
329 | } | ||
330 | |||
331 | relativeActionRow.Sequence = unusedSequence; | ||
332 | relativeActionRows.Add(relativeActionRow); | ||
333 | |||
334 | unusedSequence++; | ||
335 | } | ||
336 | |||
337 | // keep track of this sequence number as the previous used sequence number for the next iteration | ||
338 | previousUsedSequence = absoluteActionRow.Sequence; | ||
339 | } | ||
340 | |||
341 | // add the absolutely and relatively scheduled actions to the list of scheduled actions | ||
342 | scheduledActionRows.AddRange(absoluteActionRows); | ||
343 | scheduledActionRows.AddRange(relativeActionRows); | ||
344 | } | ||
345 | |||
346 | return scheduledActionRows; | ||
347 | } | ||
348 | |||
349 | private IEnumerable<string> GetRequiredActionIds() | ||
350 | { | ||
351 | var set = new HashSet<string>(); | ||
352 | |||
353 | // gather the required actions for the output type | ||
354 | if (SectionType.Product == this.Section.Type) | ||
355 | { | ||
356 | // AdminExecuteSequence table | ||
357 | set.Add("AdminExecuteSequence/CostFinalize"); | ||
358 | set.Add("AdminExecuteSequence/CostInitialize"); | ||
359 | set.Add("AdminExecuteSequence/FileCost"); | ||
360 | set.Add("AdminExecuteSequence/InstallAdminPackage"); | ||
361 | set.Add("AdminExecuteSequence/InstallFiles"); | ||
362 | set.Add("AdminExecuteSequence/InstallFinalize"); | ||
363 | set.Add("AdminExecuteSequence/InstallInitialize"); | ||
364 | set.Add("AdminExecuteSequence/InstallValidate"); | ||
365 | |||
366 | // AdminUISequence table | ||
367 | set.Add("AdminUISequence/CostFinalize"); | ||
368 | set.Add("AdminUISequence/CostInitialize"); | ||
369 | set.Add("AdminUISequence/ExecuteAction"); | ||
370 | set.Add("AdminUISequence/FileCost"); | ||
371 | |||
372 | // AdvtExecuteSequence table | ||
373 | set.Add("AdvtExecuteSequence/CostFinalize"); | ||
374 | set.Add("AdvtExecuteSequence/CostInitialize"); | ||
375 | set.Add("AdvtExecuteSequence/InstallFinalize"); | ||
376 | set.Add("AdvtExecuteSequence/InstallValidate"); | ||
377 | set.Add("AdvtExecuteSequence/PublishFeatures"); | ||
378 | set.Add("AdvtExecuteSequence/PublishProduct"); | ||
379 | |||
380 | // InstallExecuteSequence table | ||
381 | set.Add("InstallExecuteSequence/CostFinalize"); | ||
382 | set.Add("InstallExecuteSequence/CostInitialize"); | ||
383 | set.Add("InstallExecuteSequence/FileCost"); | ||
384 | set.Add("InstallExecuteSequence/InstallFinalize"); | ||
385 | set.Add("InstallExecuteSequence/InstallInitialize"); | ||
386 | set.Add("InstallExecuteSequence/InstallValidate"); | ||
387 | set.Add("InstallExecuteSequence/ProcessComponents"); | ||
388 | set.Add("InstallExecuteSequence/PublishFeatures"); | ||
389 | set.Add("InstallExecuteSequence/PublishProduct"); | ||
390 | set.Add("InstallExecuteSequence/RegisterProduct"); | ||
391 | set.Add("InstallExecuteSequence/RegisterUser"); | ||
392 | set.Add("InstallExecuteSequence/UnpublishFeatures"); | ||
393 | set.Add("InstallExecuteSequence/ValidateProductID"); | ||
394 | |||
395 | // InstallUISequence table | ||
396 | set.Add("InstallUISequence/CostFinalize"); | ||
397 | set.Add("InstallUISequence/CostInitialize"); | ||
398 | set.Add("InstallUISequence/ExecuteAction"); | ||
399 | set.Add("InstallUISequence/FileCost"); | ||
400 | set.Add("InstallUISequence/ValidateProductID"); | ||
401 | } | ||
402 | |||
403 | // Gather the required actions for each tuple type. | ||
404 | foreach (var tupleType in this.Section.Tuples.Select(t => t.Definition.Type).Distinct()) | ||
405 | { | ||
406 | switch (tupleType) | ||
407 | { | ||
408 | case TupleDefinitionType.AppSearch: | ||
409 | set.Add("InstallExecuteSequence/AppSearch"); | ||
410 | set.Add("InstallUISequence/AppSearch"); | ||
411 | break; | ||
412 | case TupleDefinitionType.BindImage: | ||
413 | set.Add("InstallExecuteSequence/BindImage"); | ||
414 | break; | ||
415 | case TupleDefinitionType.CCPSearch: | ||
416 | set.Add("InstallExecuteSequence/AppSearch"); | ||
417 | set.Add("InstallExecuteSequence/CCPSearch"); | ||
418 | set.Add("InstallExecuteSequence/RMCCPSearch"); | ||
419 | set.Add("InstallUISequence/AppSearch"); | ||
420 | set.Add("InstallUISequence/CCPSearch"); | ||
421 | set.Add("InstallUISequence/RMCCPSearch"); | ||
422 | break; | ||
423 | case TupleDefinitionType.Class: | ||
424 | set.Add("AdvtExecuteSequence/RegisterClassInfo"); | ||
425 | set.Add("InstallExecuteSequence/RegisterClassInfo"); | ||
426 | set.Add("InstallExecuteSequence/UnregisterClassInfo"); | ||
427 | break; | ||
428 | case TupleDefinitionType.Complus: | ||
429 | set.Add("InstallExecuteSequence/RegisterComPlus"); | ||
430 | set.Add("InstallExecuteSequence/UnregisterComPlus"); | ||
431 | break; | ||
432 | case TupleDefinitionType.CreateFolder: | ||
433 | set.Add("InstallExecuteSequence/CreateFolders"); | ||
434 | set.Add("InstallExecuteSequence/RemoveFolders"); | ||
435 | break; | ||
436 | case TupleDefinitionType.DuplicateFile: | ||
437 | set.Add("InstallExecuteSequence/DuplicateFiles"); | ||
438 | set.Add("InstallExecuteSequence/RemoveDuplicateFiles"); | ||
439 | break; | ||
440 | case TupleDefinitionType.Environment: | ||
441 | set.Add("InstallExecuteSequence/WriteEnvironmentStrings"); | ||
442 | set.Add("InstallExecuteSequence/RemoveEnvironmentStrings"); | ||
443 | break; | ||
444 | case TupleDefinitionType.Extension: | ||
445 | set.Add("AdvtExecuteSequence/RegisterExtensionInfo"); | ||
446 | set.Add("InstallExecuteSequence/RegisterExtensionInfo"); | ||
447 | set.Add("InstallExecuteSequence/UnregisterExtensionInfo"); | ||
448 | break; | ||
449 | case TupleDefinitionType.File: | ||
450 | set.Add("InstallExecuteSequence/InstallFiles"); | ||
451 | set.Add("InstallExecuteSequence/RemoveFiles"); | ||
452 | break; | ||
453 | case TupleDefinitionType.Font: | ||
454 | set.Add("InstallExecuteSequence/RegisterFonts"); | ||
455 | set.Add("InstallExecuteSequence/UnregisterFonts"); | ||
456 | break; | ||
457 | case TupleDefinitionType.IniFile: | ||
458 | case TupleDefinitionType.RemoveIniFile: | ||
459 | set.Add("InstallExecuteSequence/WriteIniValues"); | ||
460 | set.Add("InstallExecuteSequence/RemoveIniValues"); | ||
461 | break; | ||
462 | case TupleDefinitionType.IsolatedComponent: | ||
463 | set.Add("InstallExecuteSequence/IsolateComponents"); | ||
464 | break; | ||
465 | case TupleDefinitionType.LaunchCondition: | ||
466 | set.Add("InstallExecuteSequence/LaunchConditions"); | ||
467 | set.Add("InstallUISequence/LaunchConditions"); | ||
468 | break; | ||
469 | case TupleDefinitionType.MIME: | ||
470 | set.Add("AdvtExecuteSequence/RegisterMIMEInfo"); | ||
471 | set.Add("InstallExecuteSequence/RegisterMIMEInfo"); | ||
472 | set.Add("InstallExecuteSequence/UnregisterMIMEInfo"); | ||
473 | break; | ||
474 | case TupleDefinitionType.MoveFile: | ||
475 | set.Add("InstallExecuteSequence/MoveFiles"); | ||
476 | break; | ||
477 | case TupleDefinitionType.MsiAssembly: | ||
478 | set.Add("AdvtExecuteSequence/MsiPublishAssemblies"); | ||
479 | set.Add("InstallExecuteSequence/MsiPublishAssemblies"); | ||
480 | set.Add("InstallExecuteSequence/MsiUnpublishAssemblies"); | ||
481 | break; | ||
482 | case TupleDefinitionType.MsiServiceConfig: | ||
483 | case TupleDefinitionType.MsiServiceConfigFailureActions: | ||
484 | set.Add("InstallExecuteSequence/MsiConfigureServices"); | ||
485 | break; | ||
486 | case TupleDefinitionType.ODBCDataSource: | ||
487 | case TupleDefinitionType.ODBCTranslator: | ||
488 | case TupleDefinitionType.ODBCDriver: | ||
489 | set.Add("InstallExecuteSequence/SetODBCFolders"); | ||
490 | set.Add("InstallExecuteSequence/InstallODBC"); | ||
491 | set.Add("InstallExecuteSequence/RemoveODBC"); | ||
492 | break; | ||
493 | case TupleDefinitionType.ProgId: | ||
494 | set.Add("AdvtExecuteSequence/RegisterProgIdInfo"); | ||
495 | set.Add("InstallExecuteSequence/RegisterProgIdInfo"); | ||
496 | set.Add("InstallExecuteSequence/UnregisterProgIdInfo"); | ||
497 | break; | ||
498 | case TupleDefinitionType.PublishComponent: | ||
499 | set.Add("AdvtExecuteSequence/PublishComponents"); | ||
500 | set.Add("InstallExecuteSequence/PublishComponents"); | ||
501 | set.Add("InstallExecuteSequence/UnpublishComponents"); | ||
502 | break; | ||
503 | case TupleDefinitionType.Registry: | ||
504 | case TupleDefinitionType.RemoveRegistry: | ||
505 | set.Add("InstallExecuteSequence/WriteRegistryValues"); | ||
506 | set.Add("InstallExecuteSequence/RemoveRegistryValues"); | ||
507 | break; | ||
508 | case TupleDefinitionType.RemoveFile: | ||
509 | set.Add("InstallExecuteSequence/RemoveFiles"); | ||
510 | break; | ||
511 | case TupleDefinitionType.SelfReg: | ||
512 | set.Add("InstallExecuteSequence/SelfRegModules"); | ||
513 | set.Add("InstallExecuteSequence/SelfUnregModules"); | ||
514 | break; | ||
515 | case TupleDefinitionType.ServiceControl: | ||
516 | set.Add("InstallExecuteSequence/StartServices"); | ||
517 | set.Add("InstallExecuteSequence/StopServices"); | ||
518 | set.Add("InstallExecuteSequence/DeleteServices"); | ||
519 | break; | ||
520 | case TupleDefinitionType.ServiceInstall: | ||
521 | set.Add("InstallExecuteSequence/InstallServices"); | ||
522 | break; | ||
523 | case TupleDefinitionType.Shortcut: | ||
524 | set.Add("AdvtExecuteSequence/CreateShortcuts"); | ||
525 | set.Add("InstallExecuteSequence/CreateShortcuts"); | ||
526 | set.Add("InstallExecuteSequence/RemoveShortcuts"); | ||
527 | break; | ||
528 | case TupleDefinitionType.TypeLib: | ||
529 | set.Add("InstallExecuteSequence/RegisterTypeLibraries"); | ||
530 | set.Add("InstallExecuteSequence/UnregisterTypeLibraries"); | ||
531 | break; | ||
532 | case TupleDefinitionType.Upgrade: | ||
533 | set.Add("InstallExecuteSequence/FindRelatedProducts"); | ||
534 | set.Add("InstallUISequence/FindRelatedProducts"); | ||
535 | |||
536 | // Only add the MigrateFeatureStates action if MigrateFeature attribute is set on | ||
537 | // at least one UpgradeVersion element. | ||
538 | if (this.Section.Tuples.OfType<UpgradeTuple>().Any(t => (t.Attributes & MsiInterop.MsidbUpgradeAttributesMigrateFeatures) == MsiInterop.MsidbUpgradeAttributesMigrateFeatures)) | ||
539 | { | ||
540 | set.Add("InstallExecuteSequence/MigrateFeatureStates"); | ||
541 | set.Add("InstallUISequence/MigrateFeatureStates"); | ||
542 | } | ||
543 | break; | ||
544 | } | ||
545 | } | ||
546 | |||
547 | return set; | ||
548 | } | ||
549 | |||
550 | private IEnumerable<WixActionTuple> GetActions(SequenceTable sequence, string[] actionNames) | ||
551 | { | ||
552 | foreach (var action in WindowsInstallerStandard.StandardActions()) | ||
553 | { | ||
554 | if (action.SequenceTable == sequence && actionNames.Contains(action.Action)) | ||
555 | { | ||
556 | yield return action; | ||
557 | } | ||
558 | } | ||
559 | } | ||
560 | |||
561 | /// <summary> | ||
562 | /// Sequence an action before or after a standard action. | ||
563 | /// </summary> | ||
564 | /// <param name="actionRow">The action row to be sequenced.</param> | ||
565 | /// <param name="requiredActionRows">Collection of actions which must be included.</param> | ||
566 | private void SequenceActionRow(WixActionTuple actionRow, Dictionary<string, WixActionTuple> requiredActionRows) | ||
567 | { | ||
568 | var after = false; | ||
569 | |||
570 | if (actionRow.After != null) | ||
571 | { | ||
572 | after = true; | ||
573 | } | ||
574 | else if (actionRow.Before == null) | ||
575 | { | ||
576 | throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet); | ||
577 | } | ||
578 | |||
579 | var parentActionName = (after ? actionRow.After : actionRow.Before); | ||
580 | var parentActionKey = actionRow.SequenceTable.ToString() + "/" + parentActionName; | ||
581 | |||
582 | if (!requiredActionRows.TryGetValue(parentActionKey, out var parentActionRow)) | ||
583 | { | ||
584 | // If the missing parent action is a standard action (with a suggested sequence number), add it. | ||
585 | if (this.StandardActionsById.TryGetValue(parentActionKey, out parentActionRow)) | ||
586 | { | ||
587 | // Create a clone to avoid modifying the static copy of the object. | ||
588 | // TODO: consider this: parentActionRow = parentActionRow.Clone(); | ||
589 | |||
590 | requiredActionRows.Add(parentActionRow.Id.Id, parentActionRow); | ||
591 | } | ||
592 | else | ||
593 | { | ||
594 | throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_FoundActionRowWinNonExistentAction, (after ? "After" : "Before"), parentActionName)); | ||
595 | } | ||
596 | } | ||
597 | else if (actionRow == parentActionRow || this.ContainsChildActionRow(actionRow, parentActionRow)) // cycle detected | ||
598 | { | ||
599 | throw new WixException(WixErrors.ActionCircularDependency(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, parentActionRow.Action)); | ||
600 | } | ||
601 | |||
602 | // Add this action to the appropriate list of dependent action rows. | ||
603 | var relativeActions = this.GetRelativeActions(parentActionRow); | ||
604 | var relatedRows = (after ? relativeActions.NextActions : relativeActions.PreviousActions); | ||
605 | relatedRows.Add(actionRow); | ||
606 | } | ||
607 | |||
608 | private bool ContainsChildActionRow(WixActionTuple childTuple, WixActionTuple parentTuple) | ||
609 | { | ||
610 | var result = false; | ||
611 | |||
612 | if (this.RelativeActionsForActions.TryGetValue(childTuple.Id.Id, out var relativeActions)) | ||
613 | { | ||
614 | result = relativeActions.NextActions.Any(a => a.SequenceTable == parentTuple.SequenceTable && a.Id.Id == parentTuple.Id.Id) || | ||
615 | relativeActions.PreviousActions.Any(a => a.SequenceTable == parentTuple.SequenceTable && a.Id.Id == parentTuple.Id.Id); | ||
616 | } | ||
617 | |||
618 | return result; | ||
619 | } | ||
620 | |||
621 | private RelativeActions GetRelativeActions(WixActionTuple action) | ||
622 | { | ||
623 | if (!this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var relativeActions)) | ||
624 | { | ||
625 | relativeActions = new RelativeActions(); | ||
626 | this.RelativeActionsForActions.Add(action.Id.Id, relativeActions); | ||
627 | } | ||
628 | |||
629 | return relativeActions; | ||
630 | } | ||
631 | |||
632 | private RelativeActions GetAllRelativeActionsForSequenceType(SequenceTable sequenceType, WixActionTuple action) | ||
633 | { | ||
634 | var relativeActions = new RelativeActions(); | ||
635 | |||
636 | if (this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var actionRelatives)) | ||
637 | { | ||
638 | this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.PreviousActions, relativeActions.PreviousActions); | ||
639 | |||
640 | this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.NextActions, relativeActions.NextActions); | ||
641 | } | ||
642 | |||
643 | return relativeActions; | ||
644 | } | ||
645 | |||
646 | private void RecurseRelativeActionsForSequenceType(SequenceTable sequenceType, List<WixActionTuple> actions, List<WixActionTuple> visitedActions) | ||
647 | { | ||
648 | foreach (var action in actions.Where(a => a.SequenceTable == sequenceType)) | ||
649 | { | ||
650 | if (this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var actionRelatives)) | ||
651 | { | ||
652 | this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.PreviousActions, visitedActions); | ||
653 | } | ||
654 | |||
655 | visitedActions.Add(action); | ||
656 | |||
657 | if (actionRelatives != null) | ||
658 | { | ||
659 | this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.NextActions, visitedActions); | ||
660 | } | ||
661 | } | ||
662 | } | ||
663 | |||
664 | private class RelativeActions | ||
665 | { | ||
666 | public List<WixActionTuple> PreviousActions { get; } = new List<WixActionTuple>(); | ||
667 | |||
668 | public List<WixActionTuple> NextActions { get; } = new List<WixActionTuple>(); | ||
669 | } | ||
670 | } | ||
671 | } | ||