Skip to content

Instantly share code, notes, and snippets.

@rjmholt
Last active March 13, 2021 20:48
Show Gist options
  • Save rjmholt/fb78c837edb080deb30b7ee9d6bb8b05 to your computer and use it in GitHub Desktop.
Save rjmholt/fb78c837edb080deb30b7ee9d6bb8b05 to your computer and use it in GitHub Desktop.
Close a process gracefully in .NET
using System;
namespace example
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += OnExit;
Console.WriteLine($"PID: {System.Diagnostics.Process.GetCurrentProcess().Id}");
while (true)
{
Console.WriteLine("I'm alive!");
System.Threading.Thread.Sleep(500);
}
}
static void OnExit(object sender, EventArgs args)
{
Console.WriteLine("Exiting gracefully!");
}
}
}
using System;
using System.Runtime.InteropServices;
internal class Program
{
internal static void Main(string[] args)
{
#if UNIX
CloseProcessUnix(args[0]);
#else
unsafe
{
CloseProcessWindows(args[0]);
}
#endif
}
#if UNIX
private const int POSIX_SIGTERM = 15;
private static void CloseProcessUnix(string pidStr)
{
int pid = int.Parse(pidStr);
Console.WriteLine($"Closing process {pid}");
kill(pid, POSIX_SIGTERM);
}
[DllImport("libc", SetLastError = true)]
private static extern int kill(int pid, int signal);
#else
private const uint WM_CLOSE = 0x0010;
private static unsafe void CloseProcessWindows(string pidStr)
{
// Get the pid to close
uint pid = uint.Parse(pidStr);
// Set up the callback argument
int size = Marshal.SizeOf<ProcInfo>();
IntPtr piPtr = Marshal.AllocHGlobal(size);
var procInfo = (ProcInfo*)piPtr;
procInfo->pid = pid;
// Define the callback
WindowCallback callback = SendCloseMsg;
// Make the top-level native call
EnumWindows(callback, piPtr);
// Now free the callback argument
Marshal.FreeHGlobal(piPtr);
}
private unsafe static bool SendCloseMsg(IntPtr hwnd, IntPtr lParam)
{
if (lParam == IntPtr.Zero)
{
// Handle error here
return false;
}
var procInfo = (ProcInfo*)lParam;
GetWindowThreadProcessId(hwnd, out uint pid);
if (pid == procInfo->pid)
{
Console.WriteLine($"Closing process {pid}");
PostMessageW(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
return true;
}
private delegate bool WindowCallback(IntPtr hwnd, IntPtr lParam);
private struct ProcInfo
{
public uint pid;
}
[DllImport("user32.dll")]
private static extern bool EnumWindows(WindowCallback lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool PostMessageW(
IntPtr hwnd,
uint msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hwnd, out uint pid);
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment