著實嚇了一跳! 畢竟很常開 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 這樣的工具所造成的程序中斷喔。
參考資料:
沒有留言:
張貼留言