diff --git a/PanelSwCustomActions/ExecOnComponent.cpp b/PanelSwCustomActions/ExecOnComponent.cpp index 4d3b34d3..50a30ab5 100644 --- a/PanelSwCustomActions/ExecOnComponent.cpp +++ b/PanelSwCustomActions/ExecOnComponent.cpp @@ -117,8 +117,8 @@ extern "C" UINT __stdcall ExecuteCommand(MSIHANDLE hInstall) hr = cad.AddExec(szCommand, (LPCWSTR)szWorkingFolder, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, isAsync ? Flags::ASync : 0, errorHandling); ExitOnFailure(hr, "Failed to create command"); - hr = cad.DoDeferredAction((LPCWSTR)szId); - ExitOnFailure(hr, "Failed to schedule deferred custom action"); + hr = cad.SetCustomActionData((LPCWSTR)szId); + ExitOnFailure(hr, "Failed to set CustomActionData"); LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; diff --git a/PanelSwWixExtension/MessageResources.Designer.cs b/PanelSwWixExtension/MessageResources.Designer.cs new file mode 100644 index 00000000..48994282 --- /dev/null +++ b/PanelSwWixExtension/MessageResources.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace PanelSw.Wix.Extensions { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MessageResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MessageResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PanelSw.Wix.Extensions.MessageResources", typeof(MessageResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The InstallExecuteSequence table contains action '{1}' in between actions required for ExecuteCommand action '{0}'. Please schedule commands to execute after '{0}' or before 'Prepare{0}'. + /// + internal static string ExecuteCommandSequence { + get { + return ResourceManager.GetString("ExecuteCommandSequence", resourceCulture); + } + } + } +} diff --git a/PanelSwWixExtension/MessageResources.resx b/PanelSwWixExtension/MessageResources.resx new file mode 100644 index 00000000..824c9431 --- /dev/null +++ b/PanelSwWixExtension/MessageResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The InstallExecuteSequence table contains action '{1}' in between actions required for ExecuteCommand action '{0}'. Please schedule commands to execute after '{0}' or before 'Prepare{0}' + + \ No newline at end of file diff --git a/PanelSwWixExtension/PanelSwWiBackendBinder.cs b/PanelSwWixExtension/PanelSwWiBackendBinder.cs index 88a99264..d998bc15 100644 --- a/PanelSwWixExtension/PanelSwWiBackendBinder.cs +++ b/PanelSwWixExtension/PanelSwWiBackendBinder.cs @@ -485,6 +485,7 @@ public override IReadOnlyCollection TableDefinitions new TableDefinition(nameof(PSW_ExecOnComponent), PSW_ExecOnComponent.SymbolDefinition, PSW_ExecOnComponent.ColumnDefinitions, symbolIdIsPrimaryKey: true), new TableDefinition(nameof(PSW_ExecOnComponent_Environment), PSW_ExecOnComponent_Environment.SymbolDefinition, PSW_ExecOnComponent_Environment.ColumnDefinitions, symbolIdIsPrimaryKey: false), new TableDefinition(nameof(PSW_ExecOnComponent_ExitCode), PSW_ExecOnComponent_ExitCode.SymbolDefinition, PSW_ExecOnComponent_ExitCode.ColumnDefinitions, symbolIdIsPrimaryKey: false), + new TableDefinition(nameof(PSW_ExecuteCommand), PSW_ExecuteCommand.SymbolDefinition, PSW_ExecuteCommand.ColumnDefinitions, symbolIdIsPrimaryKey: true, unreal: true), new TableDefinition(nameof(PSW_FileRegex), PSW_FileRegex.SymbolDefinition, PSW_FileRegex.ColumnDefinitions, symbolIdIsPrimaryKey: true), new TableDefinition(nameof(PSW_ForceVersion), PSW_ForceVersion.SymbolDefinition, PSW_ForceVersion.ColumnDefinitions, symbolIdIsPrimaryKey: false), new TableDefinition(nameof(PSW_InstallUtil), PSW_InstallUtil.SymbolDefinition, PSW_InstallUtil.ColumnDefinitions, symbolIdIsPrimaryKey: false), @@ -547,8 +548,49 @@ public override void SymbolsFinalized(IntermediateSection section) GetSplitFiles(); ResolveTaskScheduler(); DuplicateFolder(section); + CheckExecuteCommandSequences(); } + private void CheckExecuteCommandSequences() + { + List executeCommands = new List(); + List wixActions = new List(); + foreach (IntermediateSection intermediate in base.Context.IntermediateRepresentation.Sections) + { + foreach (IntermediateSymbol symbol in intermediate.Symbols) + { + if (symbol is PSW_ExecuteCommand exc) + { + executeCommands.Add(exc); + } + if (symbol is WixActionSymbol act) + { + wixActions.Add(act); + } + } + } + + foreach (PSW_ExecuteCommand executeCommand in executeCommands) + { + string prepareId = $"Prepare{executeCommand.Id.Id}"; + string schedId = $"Sched{executeCommand.Id.Id}"; + string prepareFullId = $"InstallExecuteSequence/{prepareId}"; + string schedFullId = $"InstallExecuteSequence/{schedId}"; + + IEnumerable actionRows = wixActions.Where(action => action.SequenceTable.Equals(SequenceTable.InstallExecuteSequence) && ( + executeCommand.Id.Id.Equals(action.Before) && !schedFullId.Equals(action.Id.Id)) /* Actions that might be sequnced before PSW_ExecuteCommand */ + || (schedId.Equals(action.Before) && !prepareFullId.Equals(action.Id.Id)) /* Before Sched, but not Prepare*/ + || (schedId.Equals(action.After) && !executeCommand.Id.Id.Equals(action.Id.Id)) /* After Sched, but not PSW_ExecuteCommand*/ + || (prepareId.Equals(action.After) && !schedFullId.Equals(action.Id.Id)) /* After Prepare, but not Sched*/ + ); + foreach (WixActionSymbol action in actionRows) + { + string otherId = action.Id.Id.Remove(0, "InstallExecuteSequence/".Length); + Messaging.Write(PanelSwWixErrorMessages.ExecuteCommandSequence(action.SourceLineNumbers, executeCommand.Id.Id, otherId)); + } + } + } + private void DuplicateFolder(IntermediateSection section) { List duplicateFolders = new List(); diff --git a/PanelSwWixExtension/PanelSwWixCompiler.cs b/PanelSwWixExtension/PanelSwWixCompiler.cs index c3781ede..17a4cd79 100644 --- a/PanelSwWixExtension/PanelSwWixCompiler.cs +++ b/PanelSwWixExtension/PanelSwWixCompiler.cs @@ -1043,6 +1043,8 @@ private void ParseExecuteCommand(IntermediateSection section, XElement element) string cad = json.ToString(); cad = Regex.Replace(cad, "([\\[\\]\\{\\}])", "[\\$1]"); + section.AddSymbol(new PSW_ExecuteCommand(sourceLineNumbers, new Identifier(AccessModifier.Global, id))); + CustomActionSymbol prepareCA = section.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, $"Prepare{id}"))); prepareCA.ExecutionType = CustomActionExecutionType.Immediate; prepareCA.Source = "PSW_ExecuteCommand"; @@ -1070,8 +1072,8 @@ private void ParseExecuteCommand(IntermediateSection section, XElement element) schedCadSched.Action = schedCAD.Id.Id; schedCadSched.SequenceTable = SequenceTable.InstallExecuteSequence; schedCadSched.Condition = condition; - schedCadSched.Before = before; - schedCadSched.After = after; + schedCadSched.Before = id; + schedCadSched.After = null; schedCadSched.Overridable = false; // 3. PanelSwCustomActions::CommonDeferred @@ -1083,6 +1085,14 @@ private void ParseExecuteCommand(IntermediateSection section, XElement element) deferredCA.TargetType = CustomActionTargetType.Dll; deferredCA.Impersonate = impersonate; deferredCA.Hidden = true; + + WixActionSymbol deferredCASched = section.AddSymbol(new WixActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, $"InstallExecuteSequence/{id}"))); + deferredCASched.Action = deferredCA.Id.Id; + deferredCASched.SequenceTable = SequenceTable.InstallExecuteSequence; + deferredCASched.Condition = condition; + deferredCASched.Before = before; + deferredCASched.After = after; + deferredCASched.Overridable = false; } private void ParseWebsiteConfigElement(IntermediateSection section, XElement element, string component) diff --git a/PanelSwWixExtension/PanelSwWixExtension.csproj b/PanelSwWixExtension/PanelSwWixExtension.csproj index 75561ea3..5929bda8 100644 --- a/PanelSwWixExtension/PanelSwWixExtension.csproj +++ b/PanelSwWixExtension/PanelSwWixExtension.csproj @@ -37,25 +37,25 @@ - - - - - + + + + + - + - - - - - - - - - - - + + + + + + + + + + + @@ -68,9 +68,24 @@ - - - + + + + + + + + True + True + MessageResources.resx + + + + + + ResXFileCodeGenerator + MessageResources.Designer.cs + diff --git a/PanelSwWixExtension/PanelSwWixExtensionMessage.cs b/PanelSwWixExtension/PanelSwWixExtensionMessage.cs new file mode 100644 index 00000000..3785cf45 --- /dev/null +++ b/PanelSwWixExtension/PanelSwWixExtensionMessage.cs @@ -0,0 +1,17 @@ +using WixToolset.Data; + +namespace PanelSw.Wix.Extensions +{ + public static class PanelSwWixErrorMessages + { + public static Message ExecuteCommandSequence(SourceLineNumber sourceLineNumber, string executeCommandId, string otherActionId) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)PswErrorId.ExecuteCommandSequence, MessageResources.ExecuteCommandSequence, executeCommandId, otherActionId); + } + } + + public enum PswErrorId + { + ExecuteCommandSequence = 9000, + } +} diff --git a/PanelSwWixExtension/Symbols/PSW_ExecuteCommand.cs b/PanelSwWixExtension/Symbols/PSW_ExecuteCommand.cs new file mode 100644 index 00000000..ddbbc4c0 --- /dev/null +++ b/PanelSwWixExtension/Symbols/PSW_ExecuteCommand.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using WixToolset.Data; +using WixToolset.Data.WindowsInstaller; + +namespace PanelSw.Wix.Extensions.Symbols +{ + internal class PSW_ExecuteCommand : BaseSymbol + { + public static IntermediateSymbolDefinition SymbolDefinition + { + get + { + return new IntermediateSymbolDefinition(nameof(PSW_ExecuteCommand), CreateFieldDefinitions(ColumnDefinitions), typeof(PSW_ExecuteCommand)); + } + } + public static IEnumerable ColumnDefinitions + { + get + { + return new ColumnDefinition[] + { + new ColumnDefinition(nameof(Id), ColumnType.String, 72, true, false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + }; + } + } + + public PSW_ExecuteCommand() : base(SymbolDefinition) + { } + + public PSW_ExecuteCommand(SourceLineNumber lineNumber, Identifier id) : base(SymbolDefinition, lineNumber, id) + { } + } +} diff --git a/TidyBuild.custom.props b/TidyBuild.custom.props index 9cff40a6..88e916c0 100644 --- a/TidyBuild.custom.props +++ b/TidyBuild.custom.props @@ -2,7 +2,7 @@ - 3.15.0 + 3.15.1 $(FullVersion).$(GITHUB_RUN_NUMBER) PanelSwWixExtension Panel::Software diff --git a/UnitTests/ExecuteCommandUT/ExecuteCommandUT.wxs b/UnitTests/ExecuteCommandUT/ExecuteCommandUT.wxs index 1c8f7fe2..600c74af 100644 --- a/UnitTests/ExecuteCommandUT/ExecuteCommandUT.wxs +++ b/UnitTests/ExecuteCommandUT/ExecuteCommandUT.wxs @@ -10,6 +10,7 @@