C++程序自動(dòng)重啟的實(shí)現(xiàn)代碼
一、自動(dòng)重啟的原理
我不知道為什么很多程序員覺得自動(dòng)重啟很low,就像我始終不明白為什么有些人一聽見我說“重新編譯一下”就笑,難道不是重新編譯一下大部分問題就解決了嗎?
自動(dòng)重啟原理很簡單,用一個(gè)進(jìn)程監(jiān)控另一個(gè)進(jìn)程,掛了就再啟動(dòng)一個(gè)。細(xì)節(jié)也不算多,主要是正確判斷進(jìn)程狀態(tài)和啟動(dòng)方式,其實(shí)最大的工作量是程序恢復(fù)時(shí)應(yīng)該如何回到原來的狀態(tài),這意味著程序要隨時(shí)保存狀態(tài)。
只要你能做到用戶無感,你在背后做了什么用戶在意嗎?
二、自動(dòng)重啟的實(shí)現(xiàn)
如果是UNIX,用fork然后監(jiān)控子進(jìn)程,掛了就再fork,一個(gè)循環(huán)就解決問題了。
windows上麻煩一些,監(jiān)控進(jìn)程,控制臺(tái)程序:
#include "stdafx.h"
#include "shellapi.h"
#include <stdio.h>
#include <string>
using namespace std;
bool bDebug = false;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
// TODO: 在此處放置代碼。
if (bDebug)MessageBox(NULL, lpCmdLine, TEXT("啟動(dòng)"), 0);
wchar_t buf[256];
DWORD pid = 0;
{
LPWSTR* szArglist;
int nArgs;
int i;
//從命令行獲取要監(jiān)控的進(jìn)程的PID,參數(shù)-pid后的下一個(gè)參數(shù)
szArglist = CommandLineToArgvW(lpCmdLine, &nArgs);//注意,如果沒有參數(shù),會(huì)返回程序名,如果有參數(shù),則不包括程序名(或許是個(gè)BUG)
if (NULL == szArglist)
{
MessageBox(NULL, lpCmdLine, TEXT("CommandLineToArgvW失敗"), 0);
return 0;
}
else
{
wsprintf(buf, TEXT("參數(shù)個(gè)數(shù)%d"), nArgs);
if (bDebug)MessageBox(NULL, buf, TEXT(""), 0);
for (i = 0; i < nArgs; ++i)
{
if (bDebug)MessageBox(NULL, szArglist[i], TEXT("CommandLineToArgvW"), 0);
if (0 == _tcsicmp(szArglist[i], TEXT("-pid")) && i + 1 < nArgs)
{
pid = _wtol(szArglist[i + 1]);
wsprintf(buf, TEXT("%u"), pid);
if (bDebug)MessageBox(NULL, buf, szArglist[i + 1], 0);
}
}
}
LocalFree(szArglist);
}
wsprintf(buf, TEXT("%u"), pid);
if (bDebug)MessageBox(NULL, buf, TEXT("pid"), 0);
//打開進(jìn)程以供監(jiān)控
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
//等待進(jìn)程結(jié)束
DWORD state = WaitForSingleObject(handle, INFINITE);
if (WAIT_OBJECT_0 == state)
{
DWORD exitCode;
if (!GetExitCodeProcess(handle, &exitCode))//獲得退出碼
{
MessageBox(NULL, TEXT("GetExitCodeProcess 出錯(cuò)"), TEXT("未能獲取程序結(jié)束狀態(tài)"), 0);
}
wsprintf(buf, TEXT("退出碼 %u"), exitCode);
//MessageBox(NULL, buf, TEXT("任務(wù)完成"), 0);
if (0 != exitCode)//正常退出是返回碼(return 返回碼; 或者exit(返回碼),一般約定正常返回0),異常結(jié)束肯定是非0
{//這一段就是以-r參數(shù)重啟程序,兩個(gè)程序必須在同一目錄下
wchar_t _app_pathname[MAX_PATH];
GetModuleFileName(NULL, _app_pathname, MAX_PATH);
wstring app_pathname = _app_pathname;
size_t pos = app_pathname.find_last_of('\\');
if (pos != app_pathname.npos)
{
app_pathname.erase(pos + 1);
app_pathname += TEXT("app.exe");
}
else
{
app_pathname = TEXT("app.exe");
}
wchar_t szCmdLine[256];
wsprintf(szCmdLine, TEXT(" -r"));
PROCESS_INFORMATION info;
STARTUPINFO startup;
GetStartupInfo(&startup);
BOOL bSucc = CreateProcess(app_pathname.c_str(), szCmdLine, NULL, NULL,
FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startup, &info);
if (!bSucc)
{
MessageBox(NULL, TEXT("CreateProcess 出錯(cuò)"), TEXT("恢復(fù)程序失敗"), 0);
}
}
}
else if (WAIT_FAILED == state)
{
MessageBox(NULL, lpCmdLine, TEXT("WaitForSingleObject失敗"), 0);
}
else
{
MessageBox(NULL, lpCmdLine, TEXT("WaitForSingleObject非預(yù)期的返回值"), 0);
}
return 0;
}這個(gè)程序是這樣的,工作的主程序名叫“app.exe”,在適當(dāng)?shù)臅r(shí)候啟動(dòng)了監(jiān)控進(jìn)程(就是這個(gè)代碼,名稱任意,但是必須和app.exe放在一起),并把自己的pid傳遞給監(jiān)控進(jìn)程(命令行參數(shù)-pid 主進(jìn)程),監(jiān)控進(jìn)程啟動(dòng)后從命令行獲取到需要監(jiān)控的pid,監(jiān)視pid狀態(tài),如果是正常結(jié)束,就退出程序,如果是異常結(jié)束,以“-r”參數(shù)啟動(dòng)主進(jìn)程。
主進(jìn)程啟動(dòng)過程是這樣的:啟動(dòng)到某個(gè)階段,檢查命令行,帶有“-r”參數(shù)說明是自動(dòng)恢復(fù),走自動(dòng)恢復(fù)流程,否則走正常流程,啟動(dòng)監(jiān)控進(jìn)程并把自己的pid傳遞過去。
為什么要做一個(gè)獨(dú)立的監(jiān)控程序,不用主進(jìn)程自身呢?因?yàn)槌绦蛱罅?,有很多靜態(tài)初始化的話,不知道起兩個(gè)會(huì)不會(huì)有什么問題。
為什么要通過命令行參數(shù)傳遞進(jìn)程PID呢?主進(jìn)程起子進(jìn)程不是可以獲得子進(jìn)程的PID嗎?因?yàn)楹枚喑绦蛳矚g套殼啊,返回的子進(jìn)程又創(chuàng)建子進(jìn)程干活,自己馬上就退出了。
獲取自身PID的方法:
DWORD pid = GetCurrentProcessId();
要在驗(yàn)證程序異常退出可以return一個(gè)非零值,或者調(diào)用abort()。如果不區(qū)分是否是重啟則不用處理參數(shù),啟動(dòng)監(jiān)控進(jìn)程的代碼和監(jiān)控進(jìn)程啟動(dòng)主進(jìn)程的相似。
三、相關(guān)知識(shí)點(diǎn)
3.1 CommandLineToArg
win32程序處理命令行真是費(fèi)勁死了。
shellapi.h Shell32.dll/Shell32.lib LPWSTR * CommandLineToArgvW( [in] LPCWSTR lpCmdLine, [out] int *pNumArgs );
注意參數(shù)pNumArgs是在函數(shù)內(nèi)部分配的,要在外部釋放。這是C的習(xí)慣性做法。
3.2 LocalFree
釋放本地內(nèi)存對(duì)象。
HLOCAL LocalFree( [in] _Frees_ptr_opt_ HLOCAL hMem );
3.3 OpenProcess
打開進(jìn)程對(duì)象,以便后續(xù)等待進(jìn)程結(jié)束。
HANDLE OpenProcess( [in] DWORD dwDesiredAccess, [in] BOOL bInheritHandle, [in] DWORD dwProcessId );
3.4 WaitForSingleObject
等待對(duì)象,可以是進(jìn)程、線程、控制臺(tái)輸入等類型的句柄,等待的事件包括信號(hào)、超時(shí)等,發(fā)生了某種事件函數(shù)就會(huì)返回。
DWORD WaitForSingleObject( [in] HANDLE hHandle, [in] DWORD dwMilliseconds );
3.5 GetExitCodeProcess
獲得進(jìn)程退出碼。
BOOL GetExitCodeProcess( [in] HANDLE hProcess, [out] LPDWORD lpExitCode );
3.6 GetModuleFileName
一般用來獲取程序的完整路徑名。
DWORD GetModuleFileNameA( [in, optional] HMODULE hModule, [out] LPSTR lpFilename, [in] DWORD nSize );
3.7 GetStartupInfo
獲得程序的啟動(dòng)信息,在這個(gè)代碼里用來傳遞給新進(jìn)程。
void GetStartupInfoW( [out] LPSTARTUPINFOW lpStartupInfo );
3.8 CreateProcess
創(chuàng)建進(jìn)程。
BOOL CreateProcessA( [in, optional] LPCSTR lpApplicationName, [in, out, optional] LPSTR lpCommandLine, [in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes, [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] BOOL bInheritHandles, [in] DWORD dwCreationFlags, [in, optional] LPVOID lpEnvironment, [in, optional] LPCSTR lpCurrentDirectory, [in] LPSTARTUPINFOA lpStartupInfo, [out] LPPROCESS_INFORMATION lpProcessInformation );
參數(shù)雖多,大部分都可以不用。
拓展
C++重啟進(jìn)程
步驟:
1、查找需要重啟進(jìn)程的進(jìn)程id
2、啟動(dòng)需要重啟的進(jìn)程
3、殺死第一步進(jìn)程id的進(jìn)程
代碼:
1、查找需要重啟的進(jìn)程的進(jìn)程id
//通過進(jìn)程名查找進(jìn)程Id
bool FindProcess(std::wstring strProcessName, DWORD& nPid)
{
TCHAR tszProcess[64] = { 0 };
lstrcpy(tszProcess, strProcessName.c_str());
//查找進(jìn)程
STARTUPINFO st;
PROCESS_INFORMATION pi;
PROCESSENTRY32 ps;
HANDLE hSnapshot;
memset(&st, 0, sizeof(STARTUPINFO));
st.cb = sizeof(STARTUPINFO);
memset(&ps, 0, sizeof(PROCESSENTRY32));
ps.dwSize = sizeof(PROCESSENTRY32);
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
// 遍歷進(jìn)程
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return false;
if (!Process32First(hSnapshot, &ps))
return false;
do {
if (lstrcmp(ps.szExeFile, tszProcess) == 0)
{
//找到制定的程序
nPid = ps.th32ProcessID;
CloseHandle(hSnapshot);
return true;
}
} while (Process32Next(hSnapshot, &ps));
CloseHandle(hSnapshot);
return false;
}
2、啟動(dòng)進(jìn)程
//啟動(dòng)進(jìn)程
bool StartPrcess(std::wstring strProcessName)
{
TCHAR tszProcess[64] = { 0 };
lstrcpy(tszProcess, strProcessName.c_str());
//啟動(dòng)程序
SHELLEXECUTEINFO shellInfo;
memset(&shellInfo, 0, sizeof(SHELLEXECUTEINFO));
shellInfo.cbSize = sizeof(SHELLEXECUTEINFO);
shellInfo.fMask = NULL;
shellInfo.hwnd = NULL;
shellInfo.lpVerb = NULL;
shellInfo.lpFile = tszProcess; // 執(zhí)行的程序名(絕對(duì)路徑)
shellInfo.lpParameters = NULL;
shellInfo.lpDirectory = NULL;
shellInfo.nShow = SW_MINIMIZE; //SW_SHOWNORMAL 全屏顯示這個(gè)程序
shellInfo.hInstApp = NULL;
ShellExecuteEx(&shellInfo);
return true;
}
3、殺死進(jìn)程
//殺死進(jìn)程
bool KillProcess(DWORD dwPid)
{
printf("Kill進(jìn)程Pid = %d\n", dwPid);
//關(guān)閉進(jìn)程
HANDLE killHandle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | // Required by Alpha
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx
PROCESS_VM_WRITE, // For WriteProcessMemory);
FALSE, dwPid);
if (killHandle == NULL)
return false;
TerminateProcess(killHandle, 0);
return true;
}
以上就是C++程序自動(dòng)重啟的實(shí)現(xiàn)代碼的詳細(xì)內(nèi)容,更多關(guān)于C++程序自動(dòng)重啟的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)LeetCode(22.生成括號(hào))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(22.生成括號(hào)),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C++基于socket UDP網(wǎng)絡(luò)編程實(shí)現(xiàn)簡單聊天室功能
這篇文章主要為大家詳細(xì)介紹了C++基于socket UDP網(wǎng)絡(luò)編程實(shí)現(xiàn)簡單聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Qt數(shù)據(jù)庫應(yīng)用之實(shí)現(xiàn)通用數(shù)據(jù)庫分頁
數(shù)據(jù)庫分頁展示,在所有的涉及到數(shù)據(jù)庫記錄的項(xiàng)目中都是需要的。本文將利用Qt實(shí)現(xiàn)通用數(shù)據(jù)庫的分頁展示,感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下2022-02-02
一篇文章帶你了解C++ static的作用,全局變量和局部變量的區(qū)別
這篇文章介紹了C++ static的作用,全局變量和局部變量的區(qū)別,需要的朋友可以過來參考下,希望能夠給你帶來幫助2021-09-09

