給應用程式加裝“看門狗”
相信大多數的程式設計師或使用者,在Windows中見到類似於下面的親切而又溫馨的提示資訊,都不會感到陌生:
“XXX執行了非法操作,將被關閉。要終止程式,請單擊<確定>;要除錯程式,請單擊<取消>。”或者,“是否向Microsoft傳送錯誤報告?<傳送>,<不傳送>。”
如果這個程式執行在無人值守、需要保持連續工作狀態的場合,而其中的bug又一時難以排除,就需要採取應急措施,消除或減少程式出錯造成的影響。本文討論解決這個問題的辦法。
做過一定硬體開發的人都知道,惡劣的工作環境,帶有缺陷的硬體設計,不完善的演算法等內外因素,都可能造成程式“跑飛”,因此專門加裝一個“看門狗”,負責監視程式主體,必要時產生復位中斷,有效地避免裝置當機。
“看門狗”的思想,完全可以拿到高階語言程式設計中來用。基本做法是:設計一個簡單的監視程式做為主程序,將原來的工作程式作為子程序,由主程序啟動子程序並監視子程序的執行狀態。子程序在發生嚴重錯誤時不彈出本文開始時描述的對話方塊,而是悄悄退出。主程序發現子程序退出後,重新啟動子程序。如此反覆。
在具體實現上,下面以VC為例說明:
設定子程序為“靜默模式”
在系統初始化部分(CWinApp或main中的開頭),呼叫API函式SetErrorMode
SetErrorMode(SEM_NOGPFAULTERRORBOX);
保證程式在發生嚴重錯誤時不彈出對話方塊,無需人工干預,自行退出。
啟動子程序
在主程序中,建立子程序並執行。假定子程序的可執行檔案為work.exe,示意性程式碼如下
STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &pi, sizeof(pi) ); ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); // Start the child process if (CreateProcess("work.exe", "", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { // success … … }
CreateProcess有10個引數,看起來挺嚇人,其實並不複雜,很容易理解。最後一個引數會返回子程序的ID和控制代碼等資訊,後面就是對程序ID或控制代碼進行監視。
監視子程序
定時檢查子程序是否在正常執行。有好幾個API都可以用於對指定ID的程序進行監視,象GetProcessVersion,GetProcessTimes,GetProcessIoCounters等,其中GetProcessVersion最簡單,只有一個引數:
DWORD GetProcessVersion( DWORD ProcessId);
當子程序已經退出時,該函式返回0。
更為“專業”的函式是GetExitCodeProcess,它甚至能告訴我們子程序退出的原因:
BOOL GetExitCodeProcess( HANDLE hProcess, // handle to the process LPDWORD lpExitCode // termination status );
更進一步的考慮
為增強系統的可靠性,給工作程式加裝“看門狗”,不失為一種可行的技術方案。但如果有兩套使用者介面,看起來就有點不那麼專業了。可將子程序設計為基於console的應用,不帶使用者介面,所有的資訊都通過主程序視窗輸出。主程序CreateProcess的第6個引數需加入CREATE_NO_WINDOW項,將子程序隱藏起來。這樣從使用者的角度看起來,就象只存在一個應用程式。
另一個問題是,如果使用者關閉主程序,如何同時關閉子程序?用TerminateProcess函式固然能結束子程序,但可能會造成記憶體洩漏等新問題。最好是主程序向子程序發出結束的訊息並進行同步,使子程序能夠從容地退出。
再擴充套件一下,一個主程序可以同時管理多個子程序。典型的例子是利用多塊網絡卡進行抓包、分析、處理的系統,將每一塊網絡卡應用與一個子程序繫結,而主程序負責監視所有的子程序的工作。
上面的討論涉及到程序間通訊(IPC)問題。解決的辦法有很多,象file mapping, mailslot, pipe, DDE, COM, RPC, clipboard, socket, WM_COPYDATA等都能達到目的,可根據個人喜好和具體情況採用。