著實嚇了一跳! 畢竟很常開 Console Application 類型的專案是沒錯啦,不過通常只用來寫些 sample code。
不太會在正式的應用上再寫成 Console Application 了,這種問題感覺就是"那A加你甘丹",還真是連想都沒想過。
因為在放暑假的關係 XD,既然有空檔,那就來研究看看好了,要解決這個問題,初步的構想是從 AppDomain 著手。
透過
- DomainUnload
- ProcessExit
- UnhandledException
好吧,既然有方向那就動工先寫個雛型囉,先來段程式碼:
class Program
{
static void Main(string[] args)
{
var domain = AppDomain.CurrentDomain;
domain.DomainUnload += (s, e) =>
{
Console.WriteLine("AppDomain Unload");
};
domain.ProcessExit += (s, e) =>
{
Console.WriteLine("Process Exit");
};
domain.UnhandledException += (s, e) =>
{
Console.WriteLine("Unhandled Exception");
};
Console.WriteLine("按下 [E] 引發 exception 結束程式, 其他任意鍵結束程式");
if (Console.ReadKey().KeyChar == 'E')
{
throw new Exception();
}
}
}
這 code 是為了先釐清 AppDomain 事件測試而寫的,執行這個程式的話會有幾個發現:
- 因未捕捉到的 Exception 而結束的話,會引發 AppDomain 中的 UnhandledException
- 正常的程式結束,會引發 AppDomain 中的 ProcessExit
- 經由 Ctrl+C, Ctrl+Break 以及視窗關閉等等操作結束的話,AppDomain 的任何一個事件都抓不到
可是朋友的問題,就是希望能抓到視窗被使用者關閉,而非應用程式自行結束(包含發生 Exception)。
這下囧了,如同第 3 點所發現的,沒有一個事件會捕捉到視窗被關閉
喔,不! 事情當然有轉機的,回到家想一想 google 了一下,發現應由 WinAPI 著手了,最後找到了 SetConsoleCtrlHandler 這個 API,搭配一個自訂的 handler 的話可以達成這位朋友要的效果。
那麼整合使用 SetConsoleCtrlHandler 後的 code 如下:
class Program
{
static void Main(string[] args)
{
SetConsoleCtrlHandler(t =>
{
// 這邊簡單的顯示導致視窗關閉的來源是什麼
Console.WriteLine(t.ToString());
// 在這裡處理視窗關閉前想要執行的程式碼
// 返回 false 將事件交回原處理函式執行正常關閉
return false;
},
true);
Console.WriteLine("按下任意鍵結束程式");
Console.ReadKey();
Console.WriteLine("程式正常結束");
}
#region WinAPI 相關
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
public delegate bool ConsoleCtrlDelegate(CtrlTypes ctrlType);
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
#endregion
}
呼叫 SetConsoleCtrlHandler 時必須傳入一委派,以處裡視窗控制事件,結束時必須返回一布林值,SetConsoleCtrlHandler 回傳值與其餘參數可參考 MSDN 文件,不詳加說明。
SetConsoleCtrlHandler 已經可以準確地抓到 Console Application 的中斷事件,如:Ctrl+C、Ctrl+Break、按下關閉視窗按鈕、以工作管理員結束視窗工作、登出、關機等,實際上測試工作良好,但是須注意無法處理 pskill 這樣的工具所造成的程序中斷喔。
參考資料:
沒有留言:
張貼留言