標籤:
本文
一.概述
一些重要的程式,必須讓它一直跑著;而且還要時時關心它的狀態——不能讓它出現死結現象。當然,如果一個主程式會出現死結,肯定是設計或者編程上的失誤。我們首要做的事是,把這個Bug揪出來。但如果時間緊迫,這個Bug又“飄忽不定”,那麼,我們還是先寫一個軟體“看門狗”,暫時應一下急吧。
“看門狗”的需求描述:“看門狗”的運行不出現介面視窗,具有一定的隱蔽性;定時判斷目標進程是否運行在當前系統中,如果沒有則啟動目標進程;判斷目標進程是否“沒有響應”,如果是則終止目標進程;如果目標進程“沒有響應”的次數超過一定的數量,則將電腦系統重啟。
二.預備知識
首先要介紹兩個主要的函數,能夠判斷目標進程是否“沒有響應”。在User32.dll中(沒有文檔公開),Win2k/NT下的IsHungAppWindow和Win9X下的IsHungThread;前者是以一個視窗控制代碼作為參數,後者是以線程ID作為參數。我們可以通過VC開發工具的Depends查到這兩個函數。
要使用這兩個函數,我們必須先動態匯入,如下:
if (m_hUser32 == NULL)
{
m_hUser32 = GetModuleHandle("USER32.DLL");
}
if (m_hUser32)
{
m_IsHungNT = (HUNG_FUNNT) GetProcAddress(m_hUser32, "IsHungAppWindow");
m_IsHung9X = (HUNG_FUN9X) GetProcAddress(m_hUser32, "IsHungThread");
}
另外,還有如下知識點:
1. 如何讓視窗隱藏(當然通過Windows工作管理員還是可以看到的)
在架構視窗類別的PreCreateWindow中修改視窗風格,如下:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.dwExStyle |= WS_EX_TOOLWINDOW; // Make invisible in taskbar
cs.style = WS_POPUP; // Hide the main window
return TRUE;
}
2. 如何讓“看門狗”只運行一個進程
使用互斥量。在CWatchDogApp::InitInstance()中,執行如下代碼:
bool CWatchDogApp::IsUniqueCopyInProc()
{
m_Mutex = CreateMutex(NULL, TRUE, "System Watch Dog");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
return false;
}
return true;
}
該函數如果返回false,說明已經有一個WatchDog進程在運行了,當前進程就沒有必要再執行下去了。在InitInstance如下處理:
if (!IsUniqueCopyInProc())
return FALSE;
3. 如何判斷當前作業系統類型
bool CWatchDogApp::IsWinNT()
{
OSVERSIONINFO OSVersionInfo;
OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&OSVersionInfo);
if (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
return true;
}
return false;
}
4. 如何自動重啟電腦
在Win9x和Win2k/NT下,重啟電腦的處理略有不同:
if (theApp.IsWinNT())
{
// 在Win NT/2000下賦予關閉系統的許可權
static HANDLE hToken;
static TOKEN_PRIVILEGES tp;
static LUID luid;
::OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken ) ;
::LookupPrivilegeValue( NULL, SE_SHUTDOWN_NAME, &luid );
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
::AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL );
return ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
}
else
{
return ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
}
5. 如何啟動、結束其他進程
啟動進程用CreateProcess,終止進程用TerminateProcess。參考代碼如下:
bool CWatchDogView::RunTheSysProc()
{
char szPath[MAX_PATH];
GetModuleFileName(NULL, szPath, MAX_PATH);
CString strPath = szPath;
strPath = strPath.Left(strPath.ReverseFind(‘//‘)) + "//HungDemo.exe";
STARTUPINFO StartInfo;
PROCESS_INFORMATION procStruct;
memset(&StartInfo,0,sizeof(STARTUPINFO));
StartInfo.cb = sizeof(STARTUPINFO);
if (!::CreateProcess(
(LPCTSTR) strPath,
NULL,
NULL,
NULL,
FALSE,
NORMAL_PRIOR99vY_CLASS,
NULL,
NULL,
&StartInfo,
&procStruct))
return false;
return true;
}
需要提醒的是,TerminateProcess是在萬不得已的情況下使用的,它不會進入進程使用的DLL的進入點通知“脫離”(Detaching)狀態。有時候,這樣做是很危險的(DLL內部的全域資料可能受影響較大)。
6. 如何讓Win2k/NT自動登入
修改註冊表。在HKEY_LOCAL_MACHINE目錄下的Software/Microsoft/Windows NT/ CurrentVersion/WinLogon下的AutoAdminLogon(字串型)設定成1,並在DefaultUserName設定預設登入使用者,DefaultPassword設定預設使用者的密碼。
7. 如何讓Win2k/NT登入成功後直接執行你的程式(而不是預設的檔案瀏覽器)
修改註冊表。在註冊表HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/ CurrentVersion/Winlogon/Shell的值從原先的explorer.exe修改為自己程式的絕對路徑。
三.功能示範(Win2k/NT下)
友情提醒:開始示範之前,請先將你目前的工作儲存。運行“看門狗”WatchDog;同時使用Ctrl+Alt+Del開啟“Windows工作管理員”。稍候片刻,可以看到目標程式HungDemo會被啟動(這個程式類比了“沒有響應”)。然後,WatchDog發現這個程式“沒有響應”,則把它殺掉,然後重新啟動一個新的HungDemo進程。如此的處理重複六次以後,系統會自動重啟。
http://blog.csdn.net/jiangxinyu/article/details/5217909
軟體看門狗--別讓你地程式無響應(使用未公開API函數IsHungAppWindow,知識點較全)