控制台程序足够简洁,但是,经常会点错而误关闭。而且,如果系统关闭,或者用户注销,这时候任务还没完成的话,前面的运算电费就白出了。
有没有办法和WinForm一样,对控制台的退出事件进行控制呢?有的!
引入下面的函数
public delegate bool HandlerRoutine(int dwCtrlType); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool add);
委托HandlerRoutine,就是把函数的指针传递给系统API函数SetConsoleCtrlHandler。这是个典型的回调函数。
然后在Main方法中调用
Program p = new Program(); if (!SetConsoleCtrlHandler(p.HandlerRoutineMethod, true)) { Console.WriteLine("Unable to install event handler!\n"); }
const int CTRL_C_EVENT = 0; const int CTRL_BREAK_EVENT = 1; const int CTRL_CLOSE_EVENT = 2; const int CTRL_LOGOFF_EVENT = 5; const int CTRL_SHUTDOWN_EVENT = 6; public bool HandlerRoutineMethod(int dwCtrlType) { Console.WriteLine(dwCtrlType.ToString()); switch (dwCtrlType) { case CTRL_C_EVENT: return true; case CTRL_BREAK_EVENT: return false; case CTRL_CLOSE_EVENT: Console.WriteLine("确实要退出程序么?如果需要退出,请输入'exit'。"); return true; case CTRL_LOGOFF_EVENT: //用户退出 return false; case CTRL_SHUTDOWN_EVENT: //系统关闭 return false; } return true; }
HandlerRoutineMethod函数,就是系统的真实回调。如果返回的结果为false,则程序关闭,否则,不会关闭。
这里只在CTRL_BREAK_EVENT(按下Ctrl+Break),CTRL_LOGOFF_EVENT用户退出,和系统关闭 CTRL_SHUTDOWN_EVENT,事件时,调用了return false,也就是说,这个时候会关闭。但是在关闭之前我们可以做一些操作。
试想这样的情况,这个任务可能需要运行很长时间,要能在程序关闭的时候有个保存当前进度的方法,那么就可以采用以上操作了。
来看个完整的代码
class Program { static void Main(string[] args) { Program p = new Program(); if (!SetConsoleCtrlHandler(p.HandlerRoutineMethod, true)) { Console.WriteLine("无法注册系统事件!\n"); } while (true) { string s = Console.ReadLine(); if (s == "exit") GenerateConsoleCtrlEvent(p.CTRL_BREAK_EVENT, 0); } } const int CTRL_C_EVENT = 0; const int CTRL_BREAK_EVENT = 1; const int CTRL_CLOSE_EVENT = 2; const int CTRL_LOGOFF_EVENT = 5; const int CTRL_SHUTDOWN_EVENT = 6; public bool HandlerRoutineMethod(int dwCtrlType) { Console.WriteLine(dwCtrlType.ToString()); switch (dwCtrlType) { case CTRL_C_EVENT: return true; case CTRL_BREAK_EVENT: Save(); return false; case CTRL_CLOSE_EVENT: Console.WriteLine("确实要退出程序么?如果需要退出,请输入'exit'。"); return true; case CTRL_LOGOFF_EVENT: //用户退出 Save(); return false; case CTRL_SHUTDOWN_EVENT: //系统关闭 Save(); return false; } return true; } void Save() { //保存当前进度 } public delegate bool HandlerRoutine(int dwCtrlType); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool add); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern bool GenerateConsoleCtrlEvent(int code, int value); }
先 是用SetConsoleCtrlHandler方法,设置了HandlerRoutineMethod为他的回调函数。那么当有事件过来的时候,会先调用这个方法。这个方法return false,则窗体关闭,true,则窗体不关闭。
GenerateConsoleCtrlEvent方法是通知系统事件的。我们这里假设,只有用户输入exit,或者按Ctrl+Break的时候程序退出。对于用户退出和系统关闭,只是保存当前进度。
在while 循环中,如果用户输入exit命令,则通知系统调用回调函数HandlerRoutineMethod,调用的事件是Ctrl+Break。
现在控制台是不是比以前好用了很多呢?:)