この記事は公開から3年以上経過しています。
.NET Frameworkのコンソールアプリケーションを×
ボタンなどで閉じる際に後処理を行う方法。
.NETコンソールアプリケーションはFramework(mscoree.dll)によってプロセスメモリ空間にgdi32.dll
とuser32.dll
がロードされているため、CTRL_LOGOFF_EVENT
とCTRL_SHUTDOWN_EVENT
イベントは発生しません(詳細は参考ウェブサイトのMicrosoft Docsを参照)。これらのイベントを処理する必要がある場合は、コンソールアプリケーション上でHidden Windowを作成してWM_QUERYENDSESSION
とWM_ENDSESSION
メッセージを処理する必要があります。
Hidden Windowを使ったWM_QUERYENDSESSION
とWM_ENDSESSION
の処理については、こちらをご参照ください。
問題
.NETのコンソールアプリケーションはCtrl
+c
やCtrl
+Break
の中断イベントをハンドリングすることは可能だが、×
ボタンやAlt
+F4
などでアプリケーションが終了される場合は、WinFormsのようにFormClosed的なイベントハンドリングを行うことができない。
対応
WinAPIのSetConsoleCtrlHandler
でコンソールイベントハンドラを設定し、ハンドラ内で必要な終了処理を行う。
サンプルソースコード
C#
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleAppCS
{
internal class Program
{
private static void Main(string[] args)
{
// コントロールハンドラ設定
ConsoleAPI.SetConsoleCtrlHandler(ControlHandler, true);
// ダミー処理
while (true)
Console.ReadKey();
}
// コントロールハンドラ
private static bool ControlHandler(ConsoleAPI.CtrlTypes ctrlType)
{
switch (ctrlType)
{
case ConsoleAPI.CtrlTypes.CTRL_C_EVENT:
Console.WriteLine("Ctrl+Cが押されました");
// キャンセル
return true;
case ConsoleAPI.CtrlTypes.CTRL_BREAK_EVENT:
Console.WriteLine("Ctrl+Breakが押されました");
// キャンセル
return true;
case ConsoleAPI.CtrlTypes.CTRL_CLOSE_EVENT:
// .NETはuser32.dll/gdi32.dllがロードされるためLOGOFF/SHUTDOWNは利用不可
//case ConsoleAPI.CtrlTypes.CTRL_LOGOFF_EVENT:
//case ConsoleAPI.CtrlTypes.CTRL_SHUTDOWN_EVENT:
// ダミー処理
Console.WriteLine("終了しています...");
Thread.Sleep(1000);
return true;
}
return false;
}
}
// ConsoleAPIクラス
public sealed class ConsoleAPI
{
// https://docs.microsoft.com/en-us/windows/console/handlerroutine
public delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);
// https://docs.microsoft.com/en-us/windows/console/handlerroutine
public enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
// https://docs.microsoft.com/en-us/windows/console/setconsolectrlhandler
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handlerRoutine, bool add);
}
}
VB.NET
Imports System.Runtime.InteropServices
Imports System.Threading
Module ConsoleAppVB
Sub Main()
' コントロールハンドラ設定
ConsoleAPI.SetConsoleCtrlHandler(AddressOf ControlHandler, True)
' ダミー処理
While (True)
Console.ReadKey()
End While
End Sub
' コントロールハンドラ
Private Function ControlHandler(ctrlType As ConsoleAPI.CtrlTypes) As Boolean
Select Case (ctrlType)
Case ConsoleAPI.CtrlTypes.CTRL_C_EVENT
Console.WriteLine("Ctrl+Cが押されました")
' キャンセル
Return True
Case ConsoleAPI.CtrlTypes.CTRL_BREAK_EVENT
Console.WriteLine("Ctrl+Breakが押されました")
' キャンセル
Return True
Case ConsoleAPI.CtrlTypes.CTRL_CLOSE_EVENT
' .NETはuser32.dll/gdi32.dllがロードされるため利用不可
'ConsoleAPI.CtrlTypes.CTRL_LOGOFF_EVENT,
'ConsoleAPI.CtrlTypes.CTRL_SHUTDOWN_EVENT
' ダミー処理
Console.WriteLine("終了しています...")
Thread.Sleep(1000)
Return True
End Select
Return False
End Function
End Module
' ConsoleAPIクラス
Public NotInheritable Class ConsoleAPI
' https://docs.microsoft.com/en-us/windows/console/handlerroutine
Public Delegate Function ConsoleCtrlDelegate(CtrlType As CtrlTypes) As Boolean
' https://docs.microsoft.com/en-us/windows/console/handlerroutine
Public Enum CtrlTypes As UInteger
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6
End Enum
' https://docs.microsoft.com/en-us/windows/console/setconsolectrlhandler
<DllImport("kernel32.dll", SetLastError:=True)>
Public Shared Function SetConsoleCtrlHandler(handlerRoutine As ConsoleCtrlDelegate, add As Boolean) As Boolean
End Function
End Class
参考ウェブサイトなど
-
Microsoft Docs
SetConsoleCtrlHandler function -
PINVOKE.NET
setconsolectrlhandler (kernel32)
以上です。