diff options
| author | Rob Mensching <rob@firegiant.com> | 2017-11-11 01:45:59 -0800 |
|---|---|---|
| committer | Rob Mensching <rob@firegiant.com> | 2017-11-11 01:45:59 -0800 |
| commit | 9f8cb5374481b6c8a06eb2739858332350f72666 (patch) | |
| tree | 4b09b90d8a516cb5e7d8203759bd2489b6a5d20c /src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs | |
| parent | 2bb37beda887d120a0ddabf874ad25357101faa1 (diff) | |
| download | wix-9f8cb5374481b6c8a06eb2739858332350f72666.tar.gz wix-9f8cb5374481b6c8a06eb2739858332350f72666.tar.bz2 wix-9f8cb5374481b6c8a06eb2739858332350f72666.zip | |
Additional IR updates
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 | } | ||
