主控台應用程式在伺服器長時間運行,可能因使用者關閉視窗、登出、系統關機等原因被終止。透過 Win32 API SetConsoleCtrlHandler 註冊回呼,可在進程結束前執行清理(寫入日誌、釋放資源、優雅關閉連線)。
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
namespace ConsoleCtrlDemo
{
class Program
{
// 1. 定義回呼委派與常數
private delegate bool CtrlHandler(int ctrlType);
private const int CTRL_C_EVENT = 0;
private const int CTRL_BREAK_EVENT = 1;
private const int CTRL_CLOSE_EVENT = 2; // 使用者點擊視窗關閉鈕
private const int CTRL_LOGOFF_EVENT = 5; // 使用者登出
private const int CTRL_SHUTDOWN_EVENT = 6; // 系統關機
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(CtrlHandler handler, bool add);
static void Main(string[] args)
{
CtrlHandler handler = ConsoleCtrlHandler;
bool ok = SetConsoleCtrlHandler(handler, true);
Console.WriteLine(ok ? "Handler 註冊成功" : "Handler 註冊失敗");
Console.WriteLine("程式執行中... 按 Ctrl+C 或關閉視窗測試");
Console.ReadLine(); // 保持主執行緒存活
}
private static bool ConsoleCtrlHandler(int ctrlType)
{
switch (ctrlType)
{
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
Console.WriteLine($"收到關閉訊號:{ctrlType},開始清理...");
// 模擬清理工作(最多 10 秒,系統會強制終止)
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"清理步驟 {i + 1}/10");
Thread.Sleep(1000);
}
Console.WriteLine("清理完成,程式即將結束");
return true; // 返回 true 表示已處理,系統不再傳遞給下一個 handler
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
Console.WriteLine("收到 Ctrl+C / Ctrl+Break,忽略");
return true; // 攔截,不讓預設處理器終止程式
default:
return false;
}
}
}
}
| 事件 | 觸發時機 | 處理時限 |
|---|---|---|
| CTRL_CLOSE_EVENT | 使用者點擊視窗 [×] | 約 10 秒(Windows 10 可透過註冊表調整) |
| CTRL_LOGOFF_EVENT | 使用者登出 | 同左 |
| CTRL_SHUTDOWN_EVENT | 系統關機/重啟 | 同左 |
true = 已處理,阻止預設行為;false = 交給下一個 handler 或預設處理器。IHostedService),原生支援啟動/停止生命週期,無需 P/Invoke。