程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 軟件看門狗:別讓你的程序沒有響應

軟件看門狗:別讓你的程序沒有響應

編輯:關於C++

一.概述

一些重要的程序,必須讓它一直跑著;而且還要時時關心它的狀態——不能讓它出現死鎖現象。當然,如果一個主程序會出現死鎖,肯定是設計或者編程上的失誤。我們首要做的事是,把這個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_PRIORITY_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進程。如此的處理重復六次以後,系統會自動重啟。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved