C++ 程序自动重启(windows 有源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客


        程序长时间运行,内存泄漏,最后崩溃,怎么办?

        程序24小时运行,偶发随机崩溃,怎么办?

        啃代码、内存泄漏检查工具、分析线程交互……没人敢承诺解决问题。

        那怎么办呢?上标准工业级解决方案啊:自动重启!

目录

一、自动重启的原理

二、自动重启的实现

三、相关知识点

3.1 CommandLineToArg

3.2 LocalFree

 3.3 OpenProcess

 3.4 WaitForSingleObject

 3.5 GetExitCodeProcess

3.6 GetModuleFileName

3.7 GetStartupInfo

3.8 CreateProcess


一、自动重启的原理

        我不知道为什么很多程序员觉得自动重启很low,就像我始终不明白为什么有些人一听见我说“重新编译一下”就笑,难道不是重新编译一下大部分问题就解决了吗?

        自动重启原理很简单,用一个进程监控另一个进程,挂了就再启动一个。细节也不算多,主要是正确判断进程状态和启动方式,其实最大的工作量是程序恢复时应该如何回到原来的状态,这意味着程序要随时保存状态。

        只要你能做到用户无感,你在背后做了什么用户在意吗?

二、自动重启的实现

        如果是UNIX,用fork然后监控子进程,挂了就再fork,一个循环就解决问题了。

        windows上麻烦一些,监控进程,控制台程序:

#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("启动"), 0);

	wchar_t buf[256];
	DWORD pid = 0;
	{
		LPWSTR* szArglist;
		int nArgs;
		int i;

        //从命令行获取要监控的进程的PID,参数-pid后的下一个参数
		szArglist = CommandLineToArgvW(lpCmdLine, &nArgs);//注意,如果没有参数,会返回程序名,如果有参数,则不包括程序名(或许是个BUG)
		if (NULL == szArglist)
		{
			MessageBox(NULL, lpCmdLine, TEXT("CommandLineToArgvW失败"), 0);
			return 0;
		}
		else
		{
			wsprintf(buf, TEXT("参数个数%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);

    //打开进程以供监控
	HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
    //等待进程结束
	DWORD state = WaitForSingleObject(handle, INFINITE);
	if (WAIT_OBJECT_0 == state)
	{
		DWORD exitCode;
		if (!GetExitCodeProcess(handle, &exitCode))//获得退出码
		{
			MessageBox(NULL, TEXT("GetExitCodeProcess 出错"), TEXT("未能获取程序结束状态"), 0);
		}
		wsprintf(buf, TEXT("退出码 %u"), exitCode);
		//MessageBox(NULL, buf, TEXT("任务完成"), 0);
		if (0 != exitCode)//正常退出是返回码(return 返回码; 或者exit(返回码),一般约定正常返回0),异常结束肯定是非0
		{//这一段就是以-r参数重启程序,两个程序必须在同一目录下
			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 出错"), TEXT("恢复程序失败"), 0);
			}
		}
	}
	else if (WAIT_FAILED == state)
	{
		MessageBox(NULL, lpCmdLine, TEXT("WaitForSingleObject失败"), 0);
	}
	else
	{
		MessageBox(NULL, lpCmdLine, TEXT("WaitForSingleObject非预期的返回值"), 0);
	}
	return 0;
}

        这个程序是这样的,工作的主程序名叫“app.exe”,在适当的时候启动了监控进程(就是这个代码,名称任意,但是必须和app.exe放在一起),并把自己的pid传递给监控进程(命令行参数-pid 主进程),监控进程启动后从命令行获取到需要监控的pid,监视pid状态,如果是正常结束,就退出程序,如果是异常结束,以“-r”参数启动主进程。

        主进程启动过程是这样的:启动到某个阶段,检查命令行,带有“-r”参数说明是自动恢复,走自动恢复流程,否则走正常流程,启动监控进程并把自己的pid传递过去。

        为什么要做一个独立的监控程序,不用主进程自身呢?因为程序太大了,有很多静态初始化的话,不知道起两个会不会有什么问题。

        为什么要通过命令行参数传递进程PID呢?主进程起子进程不是可以获得子进程的PID吗?因为好多程序喜欢套壳啊,返回的子进程又创建子进程干活,自己马上就退出了。

        获取自身PID的方法:

		DWORD pid = GetCurrentProcessId();

        要在验证程序异常退出可以return一个非零值,或者调用abort()。如果不区分是否是重启则不用处理参数,启动监控进程的代码和监控进程启动主进程的相似。

三、相关知识点

3.1 CommandLineToArg

        win32程序处理命令行真是费劲死了。

shellapi.h
Shell32.dll/Shell32.lib

LPWSTR * CommandLineToArgvW(
  [in]  LPCWSTR lpCmdLine,
  [out] int     *pNumArgs
);

        注意参数pNumArgs是在函数内部分配的,要在外部释放。这是C的习惯性做法。

3.2 LocalFree

        释放本地内存对象。

HLOCAL LocalFree(
  [in] _Frees_ptr_opt_ HLOCAL hMem
);

 3.3 OpenProcess

          打开进程对象,以便后续等待进程结束。

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);

 3.4 WaitForSingleObject

        等待对象,可以是进程、线程、控制台输入等类型的句柄,等待的事件包括信号、超时等,发生了某种事件函数就会返回。

DWORD WaitForSingleObject(
  [in] HANDLE hHandle,
  [in] DWORD  dwMilliseconds
);

 3.5 GetExitCodeProcess

        获得进程退出码。

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

        获得程序的启动信息,在这个代码里用来传递给新进程。

void GetStartupInfoW(
  [out] LPSTARTUPINFOW lpStartupInfo
);

3.8 CreateProcess

        创建进程。

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
);

         参数虽多,大部分都可以不用。


(这里是文档结束)

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows程序源码指的是在Windows操作系统上运行的软件的源代码。Windows程序源码通常使用C或C++编程语言编写。它包含了实现程序功能所需的算法、数据结构和逻辑。源代码可以通过文本编辑器或集成开发环境(IDE)进行编写和编辑。 在Windows程序源码中,开发人员使用Windows应用程序编程接口(API)来访问操作系统功能和资源。例如,程序可以使用API调用来创建窗口、处理用户输入、执行文件操作、网络通信等。通过API,开发人员可以与各种Windows组件进行交互,如图形用户界面(GUI),文件系统,注册表等。 在源代码中,开发人员可以定义程序的各个模块、函数和变量。模块是程序的不同部分,每个模块有特定的功能。函数是可重复使用的代码块,用于执行特定任务。变量用于存储数据和状态,供程序在执行过程中使用。 开发人员还可以使用库文件来简化开发过程。库文件是预先编译的代码,包含常见功能的实现。通过引入库文件,开发人员可以重用现有代码,提高开发效率。 编写Windows程序源码需要遵循编程的最佳实践,如模块化设计、注释和文档化代码等。这些实践有助于代码的可读性和可维护性,并促进团队协作和代码重用。 总之,Windows程序源码开发人员用于创建Windows应用程序的源代码。它包含各种功能和API调用,以实现程序的特定功能。通过编写高质量的源代码,开发人员可以构建强大、高效和可靠的Windows应用程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值