Example #1
0
        private Dictionary <string, WixActionSymbol> GetRequiredStandardActions()
        {
            var overridableActionSymbols = new Dictionary <string, WixActionSymbol>();

            var requiredActionIds = this.GetRequiredActionIds();

            foreach (var actionId in requiredActionIds)
            {
                WindowsInstallerStandard.TryGetStandardAction(actionId, out var standardAction);
                overridableActionSymbols.Add(standardAction.Id.Id, standardAction);
            }

            return(overridableActionSymbols);
        }
Example #2
0
        /// <summary>
        /// Get the action symbol that is the parent of the given action symbol.
        /// </summary>
        /// <param name="actionSymbol">The given action symbol.</param>
        /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
        /// <returns>Null if there is no parent. Used for loop termination.</returns>
        private WixActionSymbol GetParentActionSymbol(WixActionSymbol actionSymbol, Dictionary <string, WixActionSymbol> requiredActionSymbols)
        {
            if (null == actionSymbol.Before && null == actionSymbol.After)
            {
                return(null);
            }

            var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + (actionSymbol.After ?? actionSymbol.Before);

            if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
            {
                WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol);
            }

            return(parentActionSymbol);
        }
Example #3
0
        /// <summary>
        /// Sequence an action before or after a standard action.
        /// </summary>
        /// <param name="actionSymbol">The action symbol to be sequenced.</param>
        /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
        private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary <string, WixActionSymbol> requiredActionSymbols)
        {
            var after = false;

            if (actionSymbol.After != null)
            {
                after = true;
            }
            else if (actionSymbol.Before == null)
            {
                throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set.");
            }

            var parentActionName = (after ? actionSymbol.After : actionSymbol.Before);
            var parentActionKey  = actionSymbol.SequenceTable.ToString() + "/" + parentActionName;

            if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
            {
                // If the missing parent action is a standard action (with a suggested sequence number), add it.
                if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol))
                {
                    // Create a clone to avoid modifying the static copy of the object.
                    // TODO: consider this: parentActionSymbol = parentActionSymbol.Clone();

                    requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol);
                }
                else
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Found an action with a non-existent {0} action: {1}.", (after ? "After" : "Before"), parentActionName));
                }
            }
            else if (actionSymbol == parentActionSymbol || this.ContainsChildActionSymbol(actionSymbol, parentActionSymbol)) // cycle detected
            {
                throw new WixException(ErrorMessages.ActionCircularDependency(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, parentActionSymbol.Action));
            }

            // Add this action to the appropriate list of dependent action symbols.
            var relativeActions = this.GetRelativeActions(parentActionSymbol);
            var relatedSymbols  = (after ? relativeActions.NextActions : relativeActions.PreviousActions);

            relatedSymbols.Add(actionSymbol);
        }
Example #4
0
        /// <summary>
        /// Sequence an action before or after a standard action.
        /// </summary>
        /// <param name="actionSymbol">The action symbol to be sequenced.</param>
        /// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
        /// <param name="firstReference">A dictionary used for detecting cyclic references among action symbols.</param>
        private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary <string, WixActionSymbol> requiredActionSymbols, Dictionary <WixActionSymbol, WixActionSymbol> firstReference)
        {
            var after = false;

            if (actionSymbol.After != null)
            {
                after = true;
            }
            else if (actionSymbol.Before == null)
            {
                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.");
            }

            var parentActionName = (after ? actionSymbol.After : actionSymbol.Before);
            var parentActionKey  = actionSymbol.SequenceTable.ToString() + "/" + parentActionName;

            if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
            {
                // If the missing parent action is a standard action (with a suggested sequence number), add it.
                if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol))
                {
                    // Create a clone to avoid modifying the static copy of the object.
                    // TODO: consider this: parentActionSymbol = parentActionSymbol.Clone();

                    requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol);
                }
                else
                {
                    throw new WixException($"Found action {actionSymbol.Id.Id} with a non-existent {(after ? "After" : "Before")} action '{parentActionName}'. The linker should have prevented this.");
                }
            }

            this.CheckForCircularActionReference(actionSymbol, requiredActionSymbols, firstReference);

            // Add this action to the appropriate list of dependent action symbols.
            var relativeActions = this.GetRelativeActions(parentActionSymbol);
            var relatedSymbols  = (after ? relativeActions.NextActions : relativeActions.PreviousActions);

            relatedSymbols.Add(actionSymbol);
        }
Example #5
0
        public void Execute()
        {
            var requiredActionSymbols = new Dictionary <string, WixActionSymbol>();

            // Get the standard actions required based on symbols in the section.
            var overridableActionSymbols = this.GetRequiredStandardActions();

            // Index all the action symbols and look for collisions.
            foreach (var actionSymbol in this.Section.Symbols.OfType <WixActionSymbol>())
            {
                if (actionSymbol.Overridable) // overridable action
                {
                    if (overridableActionSymbols.TryGetValue(actionSymbol.Id.Id, out var collidingActionSymbol))
                    {
                        this.Messaging.Write(ErrorMessages.OverridableActionCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
                        if (null != collidingActionSymbol.SourceLineNumbers)
                        {
                            this.Messaging.Write(ErrorMessages.OverridableActionCollision2(collidingActionSymbol.SourceLineNumbers));
                        }
                    }
                    else
                    {
                        overridableActionSymbols.Add(actionSymbol.Id.Id, actionSymbol);
                    }
                }
                else // unsequenced or sequenced action.
                {
                    // Unsequenced action (allowed for certain standard actions).
                    if (null == actionSymbol.Before && null == actionSymbol.After && !actionSymbol.Sequence.HasValue)
                    {
                        if (WindowsInstallerStandard.TryGetStandardAction(actionSymbol.Id.Id, out var standardAction))
                        {
                            // Populate the sequence from the standard action
                            actionSymbol.Sequence = standardAction.Sequence;
                        }
                        else // not a supported unscheduled action.
                        {
                            throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set.");
                        }
                    }

                    if (requiredActionSymbols.TryGetValue(actionSymbol.Id.Id, out var collidingActionSymbol))
                    {
                        this.Messaging.Write(ErrorMessages.ActionCollision(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
                        if (null != collidingActionSymbol.SourceLineNumbers)
                        {
                            this.Messaging.Write(ErrorMessages.ActionCollision2(collidingActionSymbol.SourceLineNumbers));
                        }
                    }
                    else
                    {
                        requiredActionSymbols.Add(actionSymbol.Id.Id, actionSymbol);
                    }
                }
            }

            // Add the overridable action symbols that are not overridden to the required action symbols.
            foreach (var actionSymbol in overridableActionSymbols.Values)
            {
                if (!requiredActionSymbols.ContainsKey(actionSymbol.Id.Id))
                {
                    requiredActionSymbols.Add(actionSymbol.Id.Id, actionSymbol);
                }
            }

            // Suppress the required actions that are overridable.
            foreach (var suppressActionSymbol in this.Section.Symbols.OfType <WixSuppressActionSymbol>())
            {
                var key = suppressActionSymbol.Id.Id;

                // If there is an overridable symbol to suppress; suppress it. There is no warning if there
                // is no action to suppress because the action may be suppressed from a merge module in
                // the binder.
                if (requiredActionSymbols.TryGetValue(key, out var requiredActionSymbol))
                {
                    if (requiredActionSymbol.Overridable)
                    {
                        this.Messaging.Write(WarningMessages.SuppressAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.Action, suppressActionSymbol.SequenceTable.ToString()));
                        if (null != requiredActionSymbol.SourceLineNumbers)
                        {
                            this.Messaging.Write(WarningMessages.SuppressAction2(requiredActionSymbol.SourceLineNumbers));
                        }

                        requiredActionSymbols.Remove(key);
                    }
                    else // suppressing a non-overridable action symbol
                    {
                        this.Messaging.Write(ErrorMessages.SuppressNonoverridableAction(suppressActionSymbol.SourceLineNumbers, suppressActionSymbol.SequenceTable.ToString(), suppressActionSymbol.Action));
                        if (null != requiredActionSymbol.SourceLineNumbers)
                        {
                            this.Messaging.Write(ErrorMessages.SuppressNonoverridableAction2(requiredActionSymbol.SourceLineNumbers));
                        }
                    }
                }
            }

            // Build up dependency trees of the relatively scheduled actions.
            // Use ToList() to create a copy of the required action symbols so that new symbols can
            // be added while enumerating.
            foreach (var actionSymbol in requiredActionSymbols.Values.ToList())
            {
                if (!actionSymbol.Sequence.HasValue)
                {
                    // check for standard actions that don't have a sequence number in a merge module
                    if (SectionType.Module == this.Section.Type && WindowsInstallerStandard.IsStandardAction(actionSymbol.Action))
                    {
                        this.Messaging.Write(ErrorMessages.StandardActionRelativelyScheduledInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
                    }

                    this.SequenceActionSymbol(actionSymbol, requiredActionSymbols);
                }
                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
                {
                    this.Messaging.Write(ErrorMessages.CustomActionSequencedInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
                }
            }

            // Look for standard actions with sequence restrictions that aren't necessarily scheduled based
            // on the presence of a particular table.
            if (requiredActionSymbols.ContainsKey("InstallExecuteSequence/DuplicateFiles") && !requiredActionSymbols.ContainsKey("InstallExecuteSequence/InstallFiles"))
            {
                WindowsInstallerStandard.TryGetStandardAction("InstallExecuteSequence/InstallFiles", out var standardAction);
                requiredActionSymbols.Add(standardAction.Id.Id, standardAction);
            }

            // Schedule actions.
            List <WixActionSymbol> scheduledActionSymbols;

            if (SectionType.Module == this.Section.Type)
            {
                scheduledActionSymbols = requiredActionSymbols.Values.ToList();
            }
            else
            {
                scheduledActionSymbols = this.ScheduleActions(requiredActionSymbols);
            }

            // Remove all existing WixActionSymbols from the section then add the
            // scheduled actions back to the section. Note: we add the indices in
            // reverse order to make it easy to remove them from the list later.
            var removeIndices = new List <int>();

            for (var i = this.Section.Symbols.Count - 1; i >= 0; --i)
            {
                var symbol = this.Section.Symbols[i];
                if (symbol.Definition.Type == SymbolDefinitionType.WixAction)
                {
                    removeIndices.Add(i);
                }
            }

            foreach (var removeIndex in removeIndices)
            {
                this.Section.Symbols.RemoveAt(removeIndex);
            }

            foreach (var action in scheduledActionSymbols)
            {
                this.Section.AddSymbol(action);
            }
        }
        /// <summary>
        /// Adds the PatchFiles action to the sequence table if it does not already exist.
        /// </summary>
        /// <param name="table">The sequence table to check or modify.</param>
        /// <param name="mainTransform">The primary authoring transform.</param>
        /// <param name="pairedTransform">The secondary patch transform.</param>
        /// <param name="mainFileRow">The file row that contains information about the patched file.</param>
        private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow)
        {
            var tableName = table.ToString();

            // Find/add PatchFiles action (also determine sequence for it).
            // Search mainTransform first, then pairedTransform (pairedTransform overrides).
            var hasPatchFilesAction    = false;
            var installFilesSequence   = 0;
            var duplicateFilesSequence = 0;

            TestSequenceTableForPatchFilesAction(
                mainTransform.Tables[tableName],
                ref hasPatchFilesAction,
                ref installFilesSequence,
                ref duplicateFilesSequence);
            TestSequenceTableForPatchFilesAction(
                pairedTransform.Tables[tableName],
                ref hasPatchFilesAction,
                ref installFilesSequence,
                ref duplicateFilesSequence);
            if (!hasPatchFilesAction)
            {
                WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionSymbol);

                var sequence = patchFilesActionSymbol.Sequence;

                // Test for default sequence value's appropriateness
                if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence))
                {
                    if (0 != duplicateFilesSequence)
                    {
                        if (duplicateFilesSequence < installFilesSequence)
                        {
                            throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action));
                        }
                        else
                        {
                            sequence = (duplicateFilesSequence + installFilesSequence) / 2;
                            if (installFilesSequence == sequence || duplicateFilesSequence == sequence)
                            {
                                throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action));
                            }
                        }
                    }
                    else
                    {
                        sequence = installFilesSequence + 1;
                    }
                }

                var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]);
                if (0 == sequenceTable.Rows.Count)
                {
                    sequenceTable.Operation = TableOperation.Add;
                }

                var patchAction = sequenceTable.CreateRow(null);
                patchAction[0]        = patchFilesActionSymbol.Action;
                patchAction[1]        = patchFilesActionSymbol.Condition;
                patchAction[2]        = sequence;
                patchAction.Operation = RowOperation.Add;
            }
        }