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.libLPWSTR * 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
);

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


(这里是文档结束)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/791761.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

续二叉搜索树递归玩法

文章目录 一、插入递归二、寻找递归&#xff08;非常简单&#xff0c;走流程就行&#xff09;三、插入递归&#xff08;理解起来比较麻烦&#xff09; 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的…

第六题:标题统计

题目描述 凯凯刚写了一篇美妙的作文&#xff0c;请问这篇作文的标题中有多少个字符&#xff1f; 注意&#xff1a;标题中可能包含大、小写英文字母、数字字符、空格和换行符。统计标题字符数时&#xff0c;空格和换行符不计算在内。 输入描述 输入文件只有一行&#xff0c;…

Java中IO、BIO、NIO、AIO分别是什么?

在Java中&#xff0c;IO、BIO、NIO、和AIO分别指不同的输入/输出处理模型。这些模型在处理数据流和网络通信时有各自的特点和使用场景。它们之间的区别&#xff1a; BIO (Blocking IO) - 同步阻塞IO 想象你在餐厅点餐。BIO就像是一对一的服务方式&#xff0c;即一个服务员为一…

ROS 2边学边练(11)-- colcon的使用

从此篇开始我们即将进入client library系列&#xff0c;主要包含包的创建、主题、服务、参数、消息等功能的自定义实现&#xff0c;开始真正进入ROS的大门咯。 前言 从ROS 1到ROS 2&#xff0c;对应的构建工具集由 catkin_make -> catkin_make_isolated ->catkin_tools …

Redis监控方案以及相关黄金指标提升稳定性和可靠性

Redis监控方案以及相关黄金指标提升稳定性和可靠性 1. 需要了解的词2. 「基准性能」相关指标2.1 Latency2.2 最大响应延迟2.3 平均响应延迟2.4 OPS(instantaneous_ops_per_sec)2.5 Hit Rate 3. 「内存」相关指标3.1 内存使用量(used_memory)3.2 内存碎片率(mem_fragmentation_r…

day18-二叉树part05

513.找树左下角的值 class Solution {public int findBottomLeftValue(TreeNode root) {int res 0;if(root null){return res;}Queue<TreeNode> que new LinkedList<>();que.add(root);while(!que.isEmpty()){int size que.size();for(int i 0;i < size;…

文件操作(详解)

该片博客有点长大家可以通过目录选择性阅读 这是个人主页 敲上瘾-CSDN博客 目录 1. 为什么使⽤⽂件&#xff1f; 2. 什么是⽂件&#xff1f; 2.1 程序⽂件 2.2 数据⽂件 2.3 ⽂件名 3. ⼆进制⽂件和⽂本⽂件&#xff1f; 4. ⽂件的打开和关闭 4.1 流和标准流 4.1.1 流…

【c/c++】深入探秘:C++内存管理的机制

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们详细讲解c中的动态内存管理 目录 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.c内存管理方式3.1new/delete对内…

【how2j练习题】JS部分阶段练习

练习题 1 <!-- 练习&#xff1a;自定义一个函数&#xff0c;对数组进行排序&#xff0c;要求排序后没有重复数据 --> <script>function p(s){document.write(s);document.write("<br>");}function uniquel(arr){ var hash[];for(var i 0;i<arr.…

Java源值1.5已过时,将在未来所有发行版中删除

1、背景 确认java项目没问题&#xff0c;但是启动的时候&#xff0c;却报错&#xff1a;java: -source 1.5 中不支持 diamond 运算符 2、解决 2.1 2.2 2.3 2.4 2.5

为什么索引的底层结构是B+树

B树 1.数据库与数据交互的单位是page,而B树的每个节点都是一个page,访问一个节点&#xff0c;就相当于进行了一次I/O操作。所以访问的节点越少&#xff0c;查找效率越大。而B树是矮胖的&#xff0c;查找深度也不会太大。 2.B树中的节点是有序存储的&#xff0c;对于范围查询、排…

拓数派向量计算引擎PieCloudVector助力东吴证券AIGC应用升级

1.项目背景 随着人工智能技术的不断创新和应用&#xff0c;我们可以看到人工智能在各个领域的应用越来越广泛。深度学习技术在图像识别、语音识别、自然语言处理等领域表现出色。机器学习算法的改进将解决更多实际问题&#xff0c;如增强学习、迁移学习和联合学习等&#xff0…

蓝桥杯 - 受伤的皇后

解题思路&#xff1a; 递归 回溯&#xff08;n皇后问题的变种&#xff09; 在 N 皇后问题的解决方案中&#xff0c;我们是从棋盘的顶部向底部逐行放置皇后的&#xff0c;这意味着在任何给定时间&#xff0c;所有未来的行&#xff08;即当前行之下的所有行&#xff09;都还没…

UNITY实战进阶-BatchRendererGroup+Jobs+Burst+RVO2+GPUAnimation 实现万人团战

研究思路&#xff1a;GPUAnimation把动画放入GPU中处理&#xff0c;BatchRendererGroup进行动态批量渲染处理&#xff0c;JobsBurst进行多线程处理逻辑&#xff08;移动、攻击等&#xff09;&#xff0c;RVO2采用Jobs的寻路导航。 准备工作&#xff1a; Editor > Project S…

算法学习——LeetCode力扣动态规划篇9(1035. 不相交的线、53. 最大子数组和、392. 判断子序列、115. 不同的子序列)

算法学习——LeetCode力扣动态规划篇9 1035. 不相交的线 1035. 不相交的线 - 力扣&#xff08;LeetCode&#xff09; 描述 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#x…

matlab使用教程(33)—求解时滞微分方程(1)

1.时滞微分方程(DDE)的分类 时滞微分方程 (DDE) 是当前时间的解与过去时间的解相关的常微分方程。该时滞可以固定不变、与时间相关、与状态相关或与导数相关。要开始积分&#xff0c;通常必须提供历史解&#xff0c;以便求解器可以获取初始积分点之前的时间的解。 1.1常时滞 D…

【MATLAB源码-第176期】基于matlab的16QAM调制解调系统频偏估计及补偿算法仿真,对比补偿前后的星座图误码率。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 在通信系统中&#xff0c;频率偏移是一种常见的问题&#xff0c;它会导致接收到的信号频率与发送信号的频率不完全匹配&#xff0c;进而影响通信质量。在调制技术中&#xff0c;QPSK&#xff08;Quadrature Phase Shift Keyin…

LeetCode每日一题之专题一:双指针 ——复写零

复写零OJ链接&#xff1a;1089. 复写零 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 解法&#xff08;原地复写-双指针&#xff09;&#xff1a; 算法思路&#xff1a; 如果「从前向后」进⾏原地复写操作的话&#xff0c;由于 0 的出现会复写两次&#xff0c;导致…

java 数据结构 Map和Set

目录 搜索树 操作-查找 操作-插入 操作-删除&#xff08;难点&#xff09; Map Map 的常用方法 Set 哈希表 哈希函数 哈希冲突 冲突-避免-负载因子调节&#xff08;重点掌握&#xff09; 冲突-解决 冲突-解决-开散列/哈希桶(重点掌握) 实现HashBuck类 put方法 …

linux系统中命令的使用方式和技巧(三)

1.添加、删除组账号&#xff1a;groupadd groupdel groupadd 新建组账号groupdel 删除组账号cat /etc/group 查看用户组信息cat /etc/passwd 查看用户信息 sudo groupadd 新建组账号 2.修改用户所在组&#xff1a;usermod 使用方法&#xff1a;usermod -g 用户组 用户名 sud…