Fix service crashes

pull/651/head
NextTurn 2020-08-10 00:00:00 +08:00 committed by Next Turn
parent e97bc5bc72
commit 1f188cf20b
4 changed files with 43 additions and 12 deletions

View File

@ -193,20 +193,28 @@ namespace WinSW.Util
if (!AttachConsole(process.Id))
{
int error = Marshal.GetLastWin32Error();
Log.Debug("Failed to attach to console. " + error switch
switch (error)
{
Errors.ERROR_ACCESS_DENIED => "WinSW is already attached to a console.", // TODO: test mode
Errors.ERROR_INVALID_HANDLE => "The process does not have a console.",
Errors.ERROR_INVALID_PARAMETER => "The process has exited.",
_ => new Win32Exception(error).Message // unreachable
});
// The process does not have a console.
case Errors.ERROR_INVALID_HANDLE:
return false;
return error == Errors.ERROR_INVALID_PARAMETER ? (bool?)null : false;
// The process has exited.
case Errors.ERROR_INVALID_PARAMETER:
return null;
// The calling process is already attached to a console.
case Errors.ERROR_ACCESS_DENIED:
default:
Log.Warn("Failed to attach to console. " + new Win32Exception(error).Message);
return false;
}
}
_ = SetConsoleCtrlHandler(null, true);
// Don't call GenerateConsoleCtrlEvent immediately after SetConsoleCtrlHandler.
// A delay was observed as of Windows 10, version 2004 and Windows Server 2019.
_ = GenerateConsoleCtrlEvent(CtrlEvents.CTRL_C_EVENT, 0);
_ = SetConsoleCtrlHandler(null, false);
bool succeeded = FreeConsole();
Debug.Assert(succeeded);

View File

@ -38,6 +38,10 @@ namespace WinSW.Tests
Assert.Equal(ServiceControllerStatus.Running, controller.Status);
Assert.True(controller.CanStop);
Assert.EndsWith(
ServiceMessages.StartedSuccessfully + Environment.NewLine,
File.ReadAllText(Path.ChangeExtension(config.FullPath, ".wrapper.log")));
if (Environment.GetEnvironmentVariable("System.DefinitionId") != null)
{
session = new InterProcessCodeCoverageSession(Helper.Name);
@ -48,6 +52,10 @@ namespace WinSW.Tests
_ = Helper.Test(new[] { "stop", config.FullPath }, config);
controller.Refresh();
Assert.Equal(ServiceControllerStatus.Stopped, controller.Status);
Assert.EndsWith(
ServiceMessages.StoppedSuccessfully + Environment.NewLine,
File.ReadAllText(Path.ChangeExtension(config.FullPath, ".wrapper.log")));
}
}
finally

View File

@ -0,0 +1,8 @@
namespace WinSW
{
internal static class ServiceMessages
{
internal const string StartedSuccessfully = "Service started successfully.";
internal const string StoppedSuccessfully = "Service stopped successfully.";
}
}

View File

@ -11,6 +11,7 @@ using WinSW.Extensions;
using WinSW.Logging;
using WinSW.Native;
using WinSW.Util;
using Messages = WinSW.ServiceMessages;
namespace WinSW
{
@ -206,7 +207,7 @@ namespace WinSW
try
{
this.DoStart();
this.LogMinimal("Service started successfully.");
this.LogMinimal(Messages.StartedSuccessfully);
}
catch (Exception e)
{
@ -220,7 +221,7 @@ namespace WinSW
try
{
this.DoStop();
this.LogMinimal("Service stopped successfully.");
this.LogMinimal(Messages.StoppedSuccessfully);
}
catch (Exception e)
{
@ -257,6 +258,8 @@ namespace WinSW
{
bool succeeded = ConsoleApis.FreeConsole();
Debug.Assert(succeeded);
succeeded = ConsoleApis.SetConsoleCtrlHandler(null, true);
Debug.Assert(succeeded);
this.HandleFileCopies();
@ -561,7 +564,9 @@ namespace WinSW
}
}
bool succeeded = ConsoleApis.AllocConsole();
bool succeeded = ConsoleApis.AllocConsole(); // inherited
Debug.Assert(succeeded);
succeeded = ConsoleApis.SetConsoleCtrlHandler(null, false); // inherited
Debug.Assert(succeeded);
Process process;
@ -573,6 +578,8 @@ namespace WinSW
{
succeeded = ConsoleApis.FreeConsole();
Debug.Assert(succeeded);
succeeded = ConsoleApis.SetConsoleCtrlHandler(null, true);
Debug.Assert(succeeded);
}
Log.Info($"Started process {process.Format()}.");