Skip to content

Instantly share code, notes, and snippets.

@bboyle1234
Last active February 13, 2022 16:55
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bboyle1234/a225218cf4a6825c058c to your computer and use it in GitHub Desktop.
Save bboyle1234/a225218cf4a6825c058c to your computer and use it in GitHub Desktop.
ShutdownEventCatcher provides all c# console application shutdown scenarios in a single handler
// Author: Benjamin Boyle
// Email: bboyle1234@gmail.com
using System;
using System.Runtime.InteropServices;
namespace Utils
{
///<summary>
/// Provides all c# console application shutdown scenarios in a single handler
///</summary>
public static class ShutdownEventCatcher
{
public static event Action<ShutdownEventArgs> Shutdown;
static void RaiseShutdownEvent(ShutdownEventArgs args)
{
if (null != Shutdown)
Shutdown(args);
}
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(Kernel32ShutdownHandler handler, bool add);
private delegate bool Kernel32ShutdownHandler(ShutdownReason reason);
/// <summary>
/// Constructor attaches the shutdown event handlers immediately
/// </summary>
static ShutdownEventCatcher()
{
SetConsoleCtrlHandler(new Kernel32ShutdownHandler(Kernel32_ProcessShuttingDown), true);
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
var args = new ShutdownEventArgs(ShutdownReason.ReachEndOfMain);
RaiseShutdownEvent(args);
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var args = new ShutdownEventArgs(e.ExceptionObject as Exception);
RaiseShutdownEvent(args);
}
static bool Kernel32_ProcessShuttingDown(ShutdownReason sig)
{
ShutdownEventArgs args = new ShutdownEventArgs(sig);
RaiseShutdownEvent(args);
return false;
}
}
public enum ShutdownReason
{
/// <summary>
/// Source is Kernel 32
/// User has pressed ^C
/// </summary>
PressCtrlC = 0,
/// <summary>
/// Source is Kernel 32
/// User has pressed ^Break
/// </summary>
PressCtrlBreak = 1,
/// <summary>
/// Source is Kernel 32
/// User has clicked the big "X" to close the console window or a windows message has been sent to the console
/// </summary>
ConsoleClosing = 2,
/// <summary>
/// Source is Kernel 32
/// Windows is logging off
/// </summary>
WindowsLogOff = 5,
/// <summary>
/// Source is Kernel 32
/// Windows is shutting down
/// </summary>
WindowsShutdown = 6,
/// <summary>
/// Source is Kernel 32
/// Program has finished executing
/// </summary>
ReachEndOfMain = 1000,
/// <summary>
/// Source is AppDomain
/// Unhandled exception in the program
/// </summary>
Exception = 1001
}
public class ShutdownEventArgs
{
public readonly Exception Exception;
public readonly ShutdownReason Reason;
public ShutdownEventArgs(ShutdownReason reason)
{
Reason = reason;
Exception = null;
}
public ShutdownEventArgs(Exception exception)
{
Reason = ShutdownReason.Exception;
Exception = exception;
}
}
}
@roberthahn-ais
Copy link

You should store the delegate in a static member to avoid garbage collection and weird NullReferenceExceptions.

private static readonly Kernel32ShutdownHandler Handler;
...
Handler = Kernel32_ProcessShuttingDown;
SetConsoleCtrlHandler(Handler, true);

Regards,
Robert

@LeMoussel
Copy link

This don't run under Mono. Kernel32.dll is unknow.

Regards

Christian.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment