Upgrade to .NET 7 (#1001)

pull/981/head v3.0.0-alpha.11
Next Turn 2023-01-29 00:27:45 +08:00 committed by GitHub
parent f5b44b958f
commit a6ba41681d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 206 additions and 149 deletions

View File

@ -25,11 +25,6 @@ strategy:
Release:
BuildConfiguration: Release
steps:
- task: UseDotNet@2
displayName: Install .NET SDK
inputs:
packageType: sdk
version: 6.x
- task: DotNetCoreCLI@2
displayName: Build
inputs:
@ -37,9 +32,9 @@ steps:
projects: src\WinSW.sln
arguments: -c $(BuildConfiguration) -p:Version=$(BuildVersion)
- script: |
dotnet publish src\WinSW\WinSW.csproj -c $(BuildConfiguration) -f net6.0-windows -r win-x64 -p:Version=$(BuildVersion)
dotnet publish src\WinSW\WinSW.csproj -c $(BuildConfiguration) -f net6.0-windows -r win-x86 -p:Version=$(BuildVersion)
dotnet publish src\WinSW\WinSW.csproj -c $(BuildConfiguration) -f net6.0-windows -r win-arm64 -p:Version=$(BuildVersion)
dotnet publish src\WinSW\WinSW.csproj -c $(BuildConfiguration) -f net7.0-windows -r win-x64 --sc -p:Version=$(BuildVersion)
dotnet publish src\WinSW\WinSW.csproj -c $(BuildConfiguration) -f net7.0-windows -r win-x86 --sc -p:Version=$(BuildVersion)
dotnet publish src\WinSW\WinSW.csproj -c $(BuildConfiguration) -f net7.0-windows -r win-arm64 --sc -p:Version=$(BuildVersion)
displayName: Build
- task: DotNetCoreCLI@2
displayName: Test
@ -73,7 +68,7 @@ steps:
- publish: artifacts\publish\WinSW-arm64.exe
artifact: WinSW-arm64.exe_$(BuildConfiguration)
displayName: Publish .NET arm64 .exe
displayName: Publish .NET Arm64 .exe
- publish: $(Build.ArtifactStagingDirectory)\WinSW.$(BuildVersion).nupkg
artifact: WinSW.nupkg_$(BuildConfiguration)

View File

@ -1,4 +1,6 @@
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyMetadata("IsTrimmable", "True")]
[assembly: InternalsVisibleTo("WinSW")]
[assembly: InternalsVisibleTo("WinSW.Tests")]

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;net6.0-windows</TargetFrameworks>
<LangVersion>preview</LangVersion>
<TargetFrameworks>net461;net7.0-windows</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
@ -15,13 +15,13 @@
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0-windows'">
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0-windows'">
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="System.Security.AccessControl" Version="6.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="5.0.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="7.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net6.0-windows'">
<ItemGroup Condition="'$(TargetFramework)' != 'net7.0-windows'">
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<Reference Include="System.ServiceProcess" />

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;net6.0-windows</TargetFrameworks>
<TargetFrameworks>net461;net7.0-windows</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -17,13 +17,18 @@ namespace WinSW.Tasks
{
using var module = ModuleDefinition.ReadModule(this.Path, new() { ReadWrite = true, ReadSymbols = true });
foreach (var t in module.CustomAttributeTypes())
{
this.WalkType(t);
}
this.WalkType(module.EntryPoint.DeclaringType);
var types = module.Types;
for (int i = types.Count - 1; i >= 0; i--)
{
var type = types[i];
if (type.FullName.Contains("WinSW.Plugins"))
if (type.FullName.StartsWith("WinSW.Plugins"))
{
this.WalkType(type);
}
@ -64,6 +69,10 @@ namespace WinSW.Tasks
this.WalkType(genericArg);
}
}
else if (typeRef is IModifierType modifierType)
{
this.WalkType(modifierType.ModifierType);
}
return;
}

View File

@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.2.0" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.4.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
</ItemGroup>

View File

@ -77,7 +77,7 @@ namespace WinSW.Tests
var result = Helper.ErrorTest(new[] { commandName });
Assert.Equal($"Unrecognized command or argument '{commandName}'\r\n\r\n", result.Error);
Assert.Equal($"Unrecognized command or argument '{commandName}'.\r\n\r\n", result.Error);
}
/// <summary>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net471;net6.0-windows</TargetFrameworks>
<TargetFrameworks>net471;net7.0-windows</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@ -12,15 +12,15 @@
</PackageReference>
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.0.226801" />
<PackageReference Include="Microsoft.Diagnostics.Runtime.Utilities" Version="2.0.0-rc.20303.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net6.0-windows'">
<ItemGroup Condition="'$(TargetFramework)' != 'net7.0-windows'">
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0" />
<Reference Include="System.ServiceProcess" />
</ItemGroup>
@ -39,11 +39,11 @@
<Target Name="Copy" BeforeTargets="AfterBuild">
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0-windows'">
<_FilesToCopy Include="$(ArtifactsBinDir)WinSW\$(Configuration)\net6.0-windows\WinSW.runtimeconfig*.json" />
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0-windows'">
<_FilesToCopy Include="$(ArtifactsBinDir)WinSW\$(Configuration)\net7.0-windows\WinSW.runtimeconfig*.json" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net6.0-windows'">
<ItemGroup Condition="'$(TargetFramework)' != 'net7.0-windows'">
<_FilesToCopy Include="$(ArtifactsBinDir)WinSW\$(Configuration)\net461\System.ValueTuple.dll" />
</ItemGroup>

View File

@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyMetadata("IsTrimmable", "True")]
[assembly: InternalsVisibleTo("WinSW.Tests")]

View File

@ -0,0 +1,41 @@
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
namespace WinSW
{
internal static class CommandExtensions
{
internal static void SetHandler<T>(this Command command, Action<T, InvocationContext> handle, Argument<T> symbol)
{
command.SetHandler(context =>
{
var value = context.ParseResult.GetValueForArgument(symbol);
handle(value!, context);
});
}
internal static void SetHandler<T1, T2, T3>(this Command command, Action<T1, T2, T3, InvocationContext> handle, Argument<T1> symbol1, Option<T2> symbol2, Option<T3> symbol3)
{
command.SetHandler(context =>
{
var value1 = context.ParseResult.GetValueForArgument(symbol1);
var value2 = context.ParseResult.GetValueForOption(symbol2);
var value3 = context.ParseResult.GetValueForOption(symbol3);
handle(value1!, value2!, value3!, context);
});
}
internal static void SetHandler<T1, T2, T3, T4>(this Command command, Action<T1, T2, T3, T4, InvocationContext> handle, Argument<T1> symbol1, Option<T2> symbol2, Option<T3> symbol3, Option<T4> symbol4)
{
command.SetHandler(context =>
{
var value1 = context.ParseResult.GetValueForArgument(symbol1);
var value2 = context.ParseResult.GetValueForOption(symbol2);
var value3 = context.ParseResult.GetValueForOption(symbol3);
var value4 = context.ParseResult.GetValueForOption(symbol4);
handle(value1!, value2!, value3!, value4!, context);
});
}
}
}

View File

@ -13,7 +13,6 @@ using System.Reflection;
using System.Security.AccessControl;
using System.Security.Principal;
using System.ServiceProcess;
using System.Threading;
using log4net;
using log4net.Appender;
using log4net.Config;
@ -107,9 +106,272 @@ namespace WinSW
elevated = IsProcessElevated();
}
var root = new RootCommand("A wrapper binary that can be used to host executables as Windows services. https://github.com/winsw/winsw")
var serviceConfig = new Argument<string?>("path-to-config")
{
Handler = CommandHandler.Create((string? pathToConfig) =>
Arity = ArgumentArity.ZeroOrOne,
IsHidden = true,
};
var root = new RootCommand("A wrapper binary that can be used to host executables as Windows services. https://github.com/winsw/winsw");
using (var identity = WindowsIdentity.GetCurrent())
{
var principal = new WindowsPrincipal(identity);
if (principal.IsInRole(new SecurityIdentifier(WellKnownSidType.ServiceSid, null)) ||
principal.IsInRole(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null)) ||
principal.IsInRole(new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null)) ||
principal.IsInRole(new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null)))
{
root.Add(serviceConfig);
}
}
root.SetHandler(Run, serviceConfig);
var config = new Argument<string?>("path-to-config", "The path to the configuration file.")
{
Arity = ArgumentArity.ZeroOrOne,
};
var noElevate = new Option<bool>("--no-elevate", "Doesn't automatically trigger a UAC prompt.");
{
var username = new Option<string?>(new[] { "--username", "--user" }, "Specifies the user name of the service account.");
var password = new Option<string?>(new[] { "--password", "--pass" }, "Specifies the password of the service account.");
var install = new Command("install", "Installs the service.")
{
config,
noElevate,
username,
password,
};
install.SetHandler(Install, config, noElevate, username, password);
root.Add(install);
}
{
var uninstall = new Command("uninstall", "Uninstalls the service.")
{
config,
noElevate,
};
uninstall.SetHandler(Uninstall, config, noElevate);
root.Add(uninstall);
}
{
var noWait = new Option<bool>("--no-wait", "Doesn't wait for the service to actually start.");
var start = new Command("start", "Starts the service.")
{
config,
noElevate,
noWait,
};
start.SetHandler(Start, config, noElevate, noWait);
root.Add(start);
}
{
var noWait = new Option<bool>("--no-wait", "Doesn't wait for the service to actually stop.");
var force = new Option<bool>("--force", "Stops the service even if it has started dependent services.");
var stop = new Command("stop", "Stops the service.")
{
config,
noElevate,
noWait,
force,
};
stop.SetHandler(Stop, config, noElevate, noWait, force);
root.Add(stop);
}
{
var force = new Option<bool>("--force", "Restarts the service even if it has started dependent services.");
var restart = new Command("restart", "Stops and then starts the service.")
{
config,
noElevate,
force,
};
restart.SetHandler(Restart, config, noElevate, force);
root.Add(restart);
}
{
var restartSelf = new Command("restart!", "self-restart (can be called from child processes)")
{
config,
};
restartSelf.SetHandler(RestartSelf, config);
root.Add(restartSelf);
}
{
var status = new Command("status", "Checks the status of the service.")
{
config,
};
status.SetHandler(Status, config);
root.Add(status);
}
{
var refresh = new Command("refresh", "Refreshes the service properties without reinstallation.")
{
config,
noElevate,
};
refresh.SetHandler(Refresh, config, noElevate);
root.Add(refresh);
}
{
var output = new Option<string>(new[] { "--output", "-o" })
{
IsRequired = true,
};
var manufacturer = new Option<string>("--manufacturer")
{
IsRequired = true,
};
manufacturer.AddValidator(result =>
{
const int minLength = 12;
const int maxLength = 15;
string token = result.Tokens.Single().Value;
int length = token.Length;
result.ErrorMessage =
length < minLength ? $"The length of argument '{token}' must be greater than or equal to {minLength}." :
length > maxLength ? $"The length of argument '{token}' must be less than or equal to {maxLength}." :
null;
});
var customize = new Command("customize", "Customizes the wrapper executable.")
{
output,
manufacturer,
};
customize.SetHandler(Customize, output, manufacturer);
root.Add(customize);
}
{
var dev = new Command("dev", "Experimental commands.");
root.Add(dev);
{
var all = new Option<bool>(new[] { "--all", "-a" });
var ps = new Command("ps", "Draws the process tree associated with the service.")
{
config,
all,
};
ps.SetHandler(DevPs, config, all);
dev.Add(ps);
}
{
var kill = new Command("kill", "Terminates the service if it has stopped responding.")
{
config,
noElevate,
};
kill.SetHandler(DevKill, config, noElevate);
dev.Add(kill);
}
{
var list = new Command("list", "Lists services managed by the current executable.");
list.SetHandler(DevList);
dev.Add(list);
}
}
return new CommandLineBuilder(root)
.UseVersionOption()
.UseHelp()
.RegisterWithDotnetSuggest()
.UseTypoCorrections()
.UseParseErrorReporting()
.UseExceptionHandler(TestExceptionHandler ?? OnException)
.CancelOnProcessTermination()
.Build()
.Invoke(args);
static void OnException(Exception exception, InvocationContext context)
{
switch (exception)
{
case InvalidDataException e:
{
string message = "The configuration file could not be loaded. " + e.Message;
Log.Fatal(message, e);
context.ExitCode = -1;
break;
}
case OperationCanceledException e:
{
Debug.Assert(e.CancellationToken == context.GetCancellationToken());
Log.Fatal(e.Message);
context.ExitCode = -1;
break;
}
case CommandException e:
{
string message = e.Message;
Log.Fatal(message);
context.ExitCode = e.InnerException is Win32Exception inner ? inner.NativeErrorCode : -1;
break;
}
case InvalidOperationException e when e.InnerException is Win32Exception inner:
{
string message = e.Message;
Log.Fatal(message);
context.ExitCode = inner.NativeErrorCode;
break;
}
case Win32Exception e:
{
string message = e.Message;
Log.Fatal(message, e);
context.ExitCode = e.NativeErrorCode;
break;
}
default:
{
Log.Fatal("Unhandled exception", exception);
context.ExitCode = -1;
break;
}
}
}
static void Run(string? pathToConfig)
{
XmlServiceConfig config = null!;
try
@ -134,270 +396,6 @@ namespace WinSW
{
// handled in OnStart
}
}),
};
using (var identity = WindowsIdentity.GetCurrent())
{
var principal = new WindowsPrincipal(identity);
if (principal.IsInRole(new SecurityIdentifier(WellKnownSidType.ServiceSid, null)) ||
principal.IsInRole(new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null)) ||
principal.IsInRole(new SecurityIdentifier(WellKnownSidType.LocalServiceSid, null)) ||
principal.IsInRole(new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null)))
{
root.Add(new Argument<string?>("path-to-config")
{
Arity = ArgumentArity.ZeroOrOne,
IsHidden = true,
});
}
}
var config = new Argument<string?>("path-to-config", "The path to the configuration file.")
{
Arity = ArgumentArity.ZeroOrOne,
};
var noElevate = new Option("--no-elevate", "Doesn't automatically trigger a UAC prompt.");
{
var install = new Command("install", "Installs the service.")
{
Handler = CommandHandler.Create<string?, bool, string?, string?>(Install),
};
install.Add(config);
install.Add(noElevate);
install.Add(new Option<string?>(new[] { "--username", "--user" }, "Specifies the user name of the service account."));
install.Add(new Option<string?>(new[] { "--password", "--pass" }, "Specifies the password of the service account."));
root.Add(install);
}
{
var uninstall = new Command("uninstall", "Uninstalls the service.")
{
Handler = CommandHandler.Create<string?, bool>(Uninstall),
};
uninstall.Add(config);
uninstall.Add(noElevate);
root.Add(uninstall);
}
{
var start = new Command("start", "Starts the service.")
{
Handler = CommandHandler.Create<string?, bool, bool, CancellationToken>(Start),
};
start.Add(config);
start.Add(noElevate);
start.Add(new Option("--no-wait", "Doesn't wait for the service to actually start."));
root.Add(start);
}
{
var stop = new Command("stop", "Stops the service.")
{
Handler = CommandHandler.Create<string?, bool, bool, bool, CancellationToken>(Stop),
};
stop.Add(config);
stop.Add(noElevate);
stop.Add(new Option("--no-wait", "Doesn't wait for the service to actually stop."));
stop.Add(new Option("--force", "Stops the service even if it has started dependent services."));
root.Add(stop);
}
{
var restart = new Command("restart", "Stops and then starts the service.")
{
Handler = CommandHandler.Create<string?, bool, bool, CancellationToken>(Restart),
};
restart.Add(config);
restart.Add(noElevate);
restart.Add(new Option("--force", "Restarts the service even if it has started dependent services."));
root.Add(restart);
}
{
var restartSelf = new Command("restart!", "self-restart (can be called from child processes)")
{
Handler = CommandHandler.Create<string?>(RestartSelf),
};
restartSelf.Add(config);
root.Add(restartSelf);
}
{
var status = new Command("status", "Checks the status of the service.")
{
Handler = CommandHandler.Create<string?>(Status),
};
status.Add(config);
root.Add(status);
}
{
var refresh = new Command("refresh", "Refreshes the service properties without reinstallation.")
{
Handler = CommandHandler.Create<string?, bool>(Refresh),
};
refresh.Add(config);
refresh.Add(noElevate);
root.Add(refresh);
}
{
var customize = new Command("customize", "Customizes the wrapper executable.")
{
Handler = CommandHandler.Create<string, string>(Customize),
};
customize.Add(new Option<string>(new[] { "--output", "-o" })
{
Required = true,
});
var manufacturer = new Option<string>("--manufacturer")
{
Required = true,
};
manufacturer.Argument.AddValidator(argument =>
{
const int minLength = 12;
const int maxLength = 15;
string token = argument.Tokens.Single().Value;
int length = token.Length;
return
length < minLength ? $"The length of argument '{token}' must be greater than or equal to {minLength}." :
length > maxLength ? $"The length of argument '{token}' must be less than or equal to {maxLength}." :
null;
});
customize.Add(manufacturer);
root.Add(customize);
}
{
var dev = new Command("dev", "Experimental commands.");
root.Add(dev);
{
var ps = new Command("ps", "Draws the process tree associated with the service.")
{
Handler = CommandHandler.Create<string?, bool>(DevPs),
};
ps.Add(config);
ps.Add(new Option(new[] { "--all", "-a" }));
dev.Add(ps);
}
{
var kill = new Command("kill", "Terminates the service if it has stopped responding.")
{
Handler = CommandHandler.Create<string?, bool>(DevKill),
};
kill.Add(config);
kill.Add(noElevate);
dev.Add(kill);
}
{
var list = new Command("list", "Lists services managed by the current executable.")
{
Handler = CommandHandler.Create(DevList),
};
dev.Add(list);
}
}
return new CommandLineBuilder(root)
.UseVersionOption()
.UseHelp()
.RegisterWithDotnetSuggest()
.UseTypoCorrections()
.UseParseErrorReporting()
.UseExceptionHandler(TestExceptionHandler ?? OnException)
.CancelOnProcessTermination()
.Build()
.Invoke(args);
static void OnException(Exception exception, InvocationContext context)
{
Debug.Assert(exception is TargetInvocationException);
Debug.Assert(exception.InnerException != null);
exception = exception.InnerException!;
switch (exception)
{
case InvalidDataException e:
{
string message = "The configuration file could not be loaded. " + e.Message;
Log.Fatal(message, e);
context.ResultCode = -1;
break;
}
case OperationCanceledException e:
{
Debug.Assert(e.CancellationToken == context.GetCancellationToken());
Log.Fatal(e.Message);
context.ResultCode = -1;
break;
}
case CommandException e:
{
string message = e.Message;
Log.Fatal(message);
context.ResultCode = e.InnerException is Win32Exception inner ? inner.NativeErrorCode : -1;
break;
}
case InvalidOperationException e when e.InnerException is Win32Exception inner:
{
string message = e.Message;
Log.Fatal(message);
context.ResultCode = inner.NativeErrorCode;
break;
}
case Win32Exception e:
{
string message = e.Message;
Log.Fatal(message, e);
context.ResultCode = e.NativeErrorCode;
break;
}
default:
{
Log.Fatal("Unhandled exception", exception);
context.ResultCode = -1;
break;
}
}
}
void Install(string? pathToConfig, bool noElevate, string? username, string? password)
@ -557,7 +555,7 @@ namespace WinSW
}
}
void Start(string? pathToConfig, bool noElevate, bool noWait, CancellationToken ct)
void Start(string? pathToConfig, bool noElevate, bool noWait, InvocationContext context)
{
var config = LoadConfigAndInitLoggers(pathToConfig, true);
@ -580,6 +578,7 @@ namespace WinSW
{
try
{
var ct = context.GetCancellationToken();
svc.WaitForStatus(ServiceControllerStatus.Running, ServiceControllerStatus.StartPending, ct);
}
catch (TimeoutException)
@ -602,7 +601,7 @@ namespace WinSW
}
}
void Stop(string? pathToConfig, bool noElevate, bool noWait, bool force, CancellationToken ct)
void Stop(string? pathToConfig, bool noElevate, bool noWait, bool force, InvocationContext context)
{
var config = LoadConfigAndInitLoggers(pathToConfig, true);
@ -633,6 +632,7 @@ namespace WinSW
{
try
{
var ct = context.GetCancellationToken();
svc.WaitForStatus(ServiceControllerStatus.Stopped, ServiceControllerStatus.StopPending, ct);
}
catch (TimeoutException)
@ -655,7 +655,7 @@ namespace WinSW
}
}
void Restart(string? pathToConfig, bool noElevate, bool force, CancellationToken ct)
void Restart(string? pathToConfig, bool noElevate, bool force, InvocationContext context)
{
var config = LoadConfigAndInitLoggers(pathToConfig, true);
@ -688,6 +688,7 @@ namespace WinSW
try
{
var ct = context.GetCancellationToken();
svc.WaitForStatus(ServiceControllerStatus.Stopped, ServiceControllerStatus.StopPending, ct);
}
catch (TimeoutException)
@ -710,6 +711,7 @@ namespace WinSW
try
{
var ct = context.GetCancellationToken();
svc.WaitForStatus(ServiceControllerStatus.Running, ServiceControllerStatus.StartPending, ct);
}
catch (TimeoutException)
@ -763,7 +765,7 @@ namespace WinSW
_ = HandleApis.CloseHandle(processInfo.ThreadHandle);
}
static int Status(string? pathToConfig)
static void Status(string? pathToConfig, InvocationContext context)
{
var config = LoadConfigAndInitLoggers(pathToConfig, true);
@ -781,7 +783,7 @@ namespace WinSW
_ => "Inactive (stopped)"
});
return svc.Status switch
context.ExitCode = svc.Status switch
{
ServiceControllerStatus.Stopped => 0,
_ => 1
@ -791,7 +793,7 @@ namespace WinSW
when (e.InnerException is Win32Exception inner && inner.NativeErrorCode == Errors.ERROR_SERVICE_DOES_NOT_EXIST)
{
Console.WriteLine("NonExistent");
return Errors.ERROR_SERVICE_DOES_NOT_EXIST;
context.ExitCode = Errors.ERROR_SERVICE_DOES_NOT_EXIST;
}
}

View File

@ -2,11 +2,10 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461;net6.0-windows</TargetFrameworks>
<LangVersion>preview</LangVersion>
<TargetFrameworks>net461;net7.0-windows</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PublishTrimmed>true</PublishTrimmed>
<AssemblyTitle>Windows Service Wrapper</AssemblyTitle>
<Description>Allows arbitrary process to run as a Windows service by wrapping it.</Description>
@ -15,19 +14,27 @@
<Copyright>Copyright (c) 2008-2020 Kohsuke Kawaguchi, Sun Microsystems, Inc., CloudBees, Inc., Oleg Nenashev and other contributors</Copyright>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net6.0-windows' AND '$(RuntimeIdentifier)' != ''">
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
<DebuggerSupport>false</DebuggerSupport>
<NullabilityInfoContextSupport>false</NullabilityInfoContextSupport>
<_AggressiveAttributeTrimming>true</_AggressiveAttributeTrimming>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0-windows' AND '$(RuntimeIdentifier)' != ''">
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' != 'net6.0-windows'">
<PropertyGroup Condition="'$(TargetFramework)' != 'net7.0-windows'">
<ILMergeVersion>3.0.41</ILMergeVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20303.1" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net6.0-windows'">
<ItemGroup Condition="'$(TargetFramework)' != 'net7.0-windows'">
<PackageReference Include="ilmerge" Version="$(ILMergeVersion)" />
<Reference Include="System.ServiceProcess" />
</ItemGroup>
@ -37,11 +44,11 @@
<ProjectReference Include="..\WinSW.Plugins\WinSW.Plugins.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net6.0-windows'">
<ItemGroup Condition="'$(TargetFramework)' != 'net7.0-windows'">
<ProjectReference Include="..\WinSW.Tasks\WinSW.Tasks.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>
<Target Name="PublishCoreExe" AfterTargets="Publish" Condition="'$(TargetFramework)' == 'net6.0-windows'">
<Target Name="PublishCoreExe" AfterTargets="Publish" Condition="'$(TargetFramework)' == 'net7.0-windows'">
<MakeDir Directories="$(ArtifactsPublishDir)" />
<Copy SourceFiles="$(PublishDir)$(TargetName).exe" DestinationFiles="$(ArtifactsPublishDir)WinSW-$(PlatformTarget).exe" />
@ -49,7 +56,7 @@
</Target>
<!-- Merge plugins and other DLLs into the executable -->
<Target Name="Merge" BeforeTargets="AfterBuild" Condition="'$(TargetFramework)' != 'net6.0-windows'">
<Target Name="Merge" BeforeTargets="AfterBuild" Condition="'$(TargetFramework)' != 'net7.0-windows'">
<PropertyGroup>
<InputAssemblies>"$(OutDir)$(TargetFileName)"</InputAssemblies>
@ -77,7 +84,7 @@
</Target>
<UsingTask TaskName="WinSW.Tasks.Trim" AssemblyFile="$(ArtifactsBinDir)WinSW.Tasks\$(Configuration)\net461\WinSW.Tasks.dll" />
<Target Name="Trim" AfterTargets="Merge" Condition="'$(TargetFramework)' != 'net6.0-windows'">
<Target Name="Trim" AfterTargets="Merge" Condition="'$(TargetFramework)' != 'net7.0-windows'">
<Trim Path="$(ArtifactsPublishDir)WinSW-$(TargetFramework).exe" />
</Target>