Skip to content

Instantly share code, notes, and snippets.

@jvshahid
Last active February 18, 2024 02:23
Show Gist options
  • Save jvshahid/6fb2f91fa7fb1db23599 to your computer and use it in GitHub Desktop.
Save jvshahid/6fb2f91fa7fb1db23599 to your computer and use it in GitHub Desktop.
C# terminating a child process (as opposed to killing, i.e. sigkill vs. sigint on linux)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TestCtrlEvent
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
[DllImport("Kernel32", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
private delegate bool HandlerRoutine(CtrlTypes CtrlType);
public static void StopProgram(Process proc)
{
//This does not require the console window to be visible.
//Disable Ctrl-C handling for our program
// SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
//Must wait here. If we don't and re-enable Ctrl-C
//handling below too fast, we might terminate ourselves.
proc.WaitForExit();
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
// SetConsoleCtrlHandler(null, false);
}
static void Main(string[] args)
{
// start an application
if (args.Length == 1)
{
child();
}
else
{
parent();
}
}
private static void parent()
{
var location = System.Reflection.Assembly.GetExecutingAssembly().Location;
var process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = location,
Arguments = "child",
UseShellExecute = false,
},
};
process.Start();
Thread.Sleep(1000);
// NOTE: if UseShellExecute isn't false, then the child process
// will get it's own console and the parent will have to Dettach
// from its own console and attach to the child's. This is accomplished
// by the following code. Note that We can't
// attach to multiple consoles and we have to be attached to
// some console otherwise ReadLine() will throw an exception (code starts here):
// FreeConsole();
// AttachConsole((uint)process.Id);
// Set the parent console handler so we don't die when a Ctrl-C event
// is received. NOTE: the following call can be replaced with
// Console.CancelKeyPress See https://msdn.microsoft.com/en-us/library/system.console.cancelkeypress%28v=vs.110%29.aspx
SetConsoleCtrlHandler(ParentConsoleCtrlCheck, true);
while (true)
{
Console.WriteLine("parent: started");
var line = Console.ReadLine();
if (line == "t")
{
StopProgram(process);
}
else if (line == "k")
{
process.Kill();
}
else
{
continue;
}
while (true)
{
// the parent will display this when the child dies
Console.WriteLine("parent: child is dead");
Thread.Sleep(1000);
}
}
}
private static void child()
{
// NOTE: the following call can be replaced with
// Console.CancelKeyPress See https://msdn.microsoft.com/en-us/library/system.console.cancelkeypress%28v=vs.110%29.aspx
SetConsoleCtrlHandler(ChildConsoleCtrlCheck, true);
while (true)
{
Thread.Sleep(1000);
// The child will stop printing this when it dies
Console.WriteLine("child: waiting");
}
}
private static bool ChildConsoleCtrlCheck(CtrlTypes sig)
{
Console.WriteLine("child: Received shutdown event");
// Returning false, will cause the default event handler
// to run and kill the child process
return false;
}
private static bool ParentConsoleCtrlCheck(CtrlTypes sig)
{
Console.WriteLine("parent: Received shutdown event");
// Returning true prevents the default handler from running
// which kills the parent
return true;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment