關閉”xx程式已停止工作”提示視窗
近日在工作中,接手一個專案,程式執行起來後偶發性間隔幾個小時或幾天就會出現如下(圖1, 圖2)的”xx程式已停止工作”的提示視窗,這時需要使用者手動點選”關閉程式”按鈕,程序才會退出。
圖1
圖2
當然最好的解決辦法就是找出程式中導致”程式錯誤”的原因,但由於對接手的專案不是很熟悉,再加上時間緊迫,難以在短時間找到問題原因,於是給此程式新增一個”守護程式”(即: 檢測到程序退出後就自動重啟)。
但程式崩潰時,彈出的”xx程式已停止工作”導致程式程序無法退出,“守護程式”自然也起不到相應的作用。
在網上查找了一番,終於找到兩種解決方法:
第一種方法
執行登錄檔編輯器,依次定位到HKEY_CURRENT_USER\Software\Microsoft\Windows\WindowsError Reporting,在右側視窗中找到並雙擊開啟DontshowUI,然後在彈出的視窗中將預設值“0”修改為“1”。
那麼,當程式崩潰時,就不會再出現”xx程式已停止工作”的提示框,崩潰程式程序會自動退出。
這種修改系統登錄檔的方法是最方便和直接的,但會對所有程式生效,如果特別注重系統的安全性,只想讓指定的程式在崩潰時不出現”xx程式已停止工作”,請參考”第二種方法”。
圖3
第二種方法
檢視Windows工作管理員(圖4)發現,程式崩潰時之所以出現”xx程式已停止”工作,是因為觸發了”Windows的錯誤報告”機制,在我的系統(Windows 10 64位)工作管理員程序列表中會出現一個名稱為”Windows問題報告”的程序,點選此程序左側的”下拉箭頭”,會出現一個視窗列表,此視窗列表就代表了當前所有彈出”xx程式已停止工作”的視窗(圖5),而視窗標題就是我們崩潰程式的程序名。
圖4
圖5
看到此,不知道你是否已經有了啟發。
解決思路如下:
在”守護程式”中定期檢測Windows系統程序列表中是否出現”WerFault
.exe”程序(“Windows問題報告”的程序名), 如果出現, 則查詢”WerFault.exe”程序下的視窗名稱是否存在”要守護程式的程序名”, 如果存在,則表示“要守護的程式崩潰並出現已停止工作”的提示框, 那麼則向“WerFault.exe”程序下的“視窗”傳送 WM_Close 訊息,關閉此“提示視窗”,如此, “要守護的程式程序就會完全退出”, 守護程式就可以重新啟動此程式了。
其實就是用程式模擬“使用者手動關閉‘已停止工作’視窗。
#include <windows.h>
#include <tlhelp32.h> //宣告快照函式檔案
#include "stdio.h"
#include <cstring>
// 根據程序ID, 返回指定程序下"第一個"視窗的視窗控制代碼
// 注: 此程式還不夠完善, 因為指定程序下可能有多個視窗
HWND GetWindowHandleByPID(DWORD dwProcessID)
{
HWND h = GetTopWindow(0);
while (h)
{
DWORD pid = 0;
DWORD dwTheardId = GetWindowThreadProcessId(h, &pid);
if (dwTheardId != 0)
{
if (pid == dwProcessID /*your process id*/)
{
// here h is the handle to the window
return h;
}
}
h = GetNextWindow(h, GW_HWNDNEXT);
}
return NULL;
}
int main(int argc, char *argv[])
{
PROCESSENTRY32 pe32;
//在使用這個結構之前,先設定它的大小
pe32.dwSize = sizeof(pe32);
//給系統內的所有程序拍一個快照
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//Includes the process list in the snapshot
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot 呼叫失敗! n");
return -1;
}
//遍歷程序快照,輪流顯示每個程序資訊
BOOL bMore = ::Process32First(hProcessSnap, &pe32);
while (bMore)
{
/*printf(" 程序名稱為:%s\n", pe32.szExeFile);
printf(" 程序ID為:%u \n\n", pe32.th32ProcessID);*/
if (_stricmp(pe32.szExeFile, "werfault.exe") == 0)
{
printf(" 程序名稱為:%s\n", pe32.szExeFile);
printf(" 程序ID為:%u \n\n", pe32.th32ProcessID);
HWND hwnd = GetWindowHandleByPID(pe32.th32ProcessID);
if (hwnd)
{
char szText[256] = { 0 };
GetWindowText(hwnd, szText, 256);
// 自己崩潰程式的"程序名"
if (_stricmp(szText, "myProcessName.exe") == 0)
{
printf("Text: %s\n\n", szText);
// 關閉"xx程式已停止"提示視窗
SendMessage(hwnd, WM_CLOSE, NULL, NULL);
}
}
}
//遍歷下一個
bMore = ::Process32Next(hProcessSnap, &pe32);
}
//清除snapshot物件
::CloseHandle(hProcessSnap);
return 0;
}
補充
此程式還不夠完善,因為對於下面方法:
HWNDGetWindowHandleByPID(DWORD dwProcessID);
其根據程序ID,返回指定程序下"第一個"視窗的視窗控制代碼,但一個程序下可能會有多個視窗(如圖6)。
圖6
但對“WerFault.exe”程序,我在測試中發現(測試系統:Windows10 64位),當有多個程式出現”已停止工作“提示視窗時(圖7),每個程式會各自對應一個”WerFault.exe”程序(圖8)。即: 每個WerFault.exe程序下只會出現一個“已停止工作”視窗標題。
當然其他Windows系統我沒有測試是否也是這樣,以後有時間再進一步完善此程式。
圖7
圖8
參考文章:
修改DontshowUI預設值彈出視窗關閉小祕密
VC++ 通過程序名或程序ID獲取程序控制代碼
VC 顯示當前執行的所有程序