1. 程式人生 > >關閉”xx程式已停止工作”提示視窗

關閉”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 顯示當前執行的所有程序