-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNativeConsole.cs
More file actions
62 lines (51 loc) · 2.38 KB
/
Copy pathNativeConsole.cs
File metadata and controls
62 lines (51 loc) · 2.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
namespace RedisService;
/// <summary>
/// Sends a console Ctrl-C to a child process so it can shut down the way it would on SIGINT (redis persists and exits cleanly).
/// A Windows service has no console of its own, so we temporarily attach to the child's console, tell our own process to ignore the event, broadcast it, then detach.
/// </summary>
[SupportedOSPlatform("windows")]
internal static class NativeConsole
{
private const uint CTRL_C_EVENT = 0;
private delegate bool HandlerRoutine(uint dwCtrlType);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeConsole();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleCtrlHandler(HandlerRoutine? handlerRoutine, [MarshalAs(UnmanagedType.Bool)] bool add);
/// <summary>
/// Attempts to deliver Ctrl-C to the process tree attached to <paramref name="processId"/>'s console.
/// Returns false (so the caller can fall back to a hard kill) when the child has no console to attach to.
/// </summary>
public static bool TrySendCtrlC(int processId, ILogger logger)
{
// Detach from any console we might already own so we can borrow the child's.
FreeConsole();
if (!AttachConsole((uint)processId))
{
logger.LogDebug("AttachConsole({Pid}) failed (win32 error {Error}); graceful stop unavailable.", processId, Marshal.GetLastWin32Error());
return false;
}
try
{
// Ignore the Ctrl-C in our own process; passing group 0 broadcasts to everything on this console, including us.
SetConsoleCtrlHandler(null, add: true);
return GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
}
finally
{
FreeConsole();
SetConsoleCtrlHandler(null, add: false);
}
}
}