C++进程监视器与自动启动程序

写一段Mingw64上编译和Windows 11上运行的GCC的C++程序,获取正在运行的进程窗口的标题,通过字符串来精确匹配,或者用通配符或正则表达式,获得一个进程列表,当这个进程列表中的所有进程都退出的时候,就自动调用一个另外的exe程序或bat批处理脚本,用ini配置文件来记录配置信息,实现异常处理和日志文件写入,日志包含发生错误和运行exe或bat文件的时间戳,运行状态,运行的文件名和出现错误时的函数调用堆栈。

这个程序提供了完整的进程监视和自动执行功能,并包含了详细的日志记录和错误处理机制。

cpp 复制代码
#include <windows.h>
#include <psapi.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <regex>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <dbghelp.h>
#include <shlwapi.h>

#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "psapi.lib")

// 配置结构体
struct Config {
    std::vector<std::string> processPatterns;
    std::string executePath;
    std::string logFile;
    int checkInterval;
    bool useRegex;
};

// 日志函数
void Log(const std::string& message, const std::string& logFile);
void LogError(const std::string& functionName, const std::string& logFile);

// 堆栈跟踪函数
std::string GetStackTrace();

// 配置读取函数
Config ReadConfig(const std::string& configFile);
std::string GetIniString(const std::string& iniFile, const std::string& section, 
                         const std::string& key, const std::string& defaultValue);
int GetIniInt(const std::string& iniFile, const std::string& section, 
              const std::string& key, int defaultValue);
bool GetIniBool(const std::string& iniFile, const std::string& section, 
                const std::string& key, bool defaultValue);

// 进程检查函数
bool IsProcessRunning(const Config& config);
bool MatchProcessName(const std::string& processName, const std::string& pattern, bool useRegex);
std::vector<std::string> GetRunningProcesses();

// 执行程序函数
bool ExecuteProgram(const std::string& programPath, const std::string& logFile);

int main() {
    // 读取配置
    Config config = ReadConfig("config.ini");
    
    if (config.processPatterns.empty()) {
        Log("错误: 没有配置要监视的进程模式", config.logFile);
        return 1;
    }
    
    Log("进程监视器启动", config.logFile);
    Log("监视的进程模式: ", config.logFile);
    for (const auto& pattern : config.processPatterns) {
        Log("  - " + pattern, config.logFile);
    }
    Log("检查间隔: " + std::to_string(config.checkInterval) + "ms", config.logFile);
    Log("使用正则表达式: " + std::string(config.useRegex ? "是" : "否"), config.logFile);
    Log("目标程序: " + config.executePath, config.logFile);
    
    // 主循环
    while (true) {
        try {
            if (!IsProcessRunning(config)) {
                Log("所有监视的进程已退出,正在启动: " + config.executePath, config.logFile);
                
                if (ExecuteProgram(config.executePath, config.logFile)) {
                    Log("成功执行: " + config.executePath, config.logFile);
                } else {
                    Log("执行失败: " + config.executePath, config.logFile);
                }
                
                break;
            }
            
            Sleep(config.checkInterval);
        } catch (const std::exception& e) {
            Log("异常: " + std::string(e.what()), config.logFile);
            LogError("主循环", config.logFile);
        } catch (...) {
            Log("未知异常", config.logFile);
            LogError("主循环", config.logFile);
        }
    }
    
    Log("进程监视器退出", config.logFile);
    return 0;
}

// 读取配置
Config ReadConfig(const std::string& configFile) {
    Config config;
    
    config.processPatterns.clear();
    std::string patterns = GetIniString(configFile, "Monitor", "ProcessPatterns", "");
    if (!patterns.empty()) {
        size_t pos = 0;
        while ((pos = patterns.find(',')) != std::string::npos) {
            config.processPatterns.push_back(patterns.substr(0, pos));
            patterns.erase(0, pos + 1);
        }
        config.processPatterns.push_back(patterns);
    }
    
    config.executePath = GetIniString(configFile, "Monitor", "ExecutePath", "");
    config.logFile = GetIniString(configFile, "Monitor", "LogFile", "monitor.log");
    config.checkInterval = GetIniInt(configFile, "Monitor", "CheckInterval", 5000);
    config.useRegex = GetIniBool(configFile, "Monitor", "UseRegex", false);
    
    return config;
}

// 从INI文件读取字符串
std::string GetIniString(const std::string& iniFile, const std::string& section, 
                         const std::string& key, const std::string& defaultValue) {
    char buffer[256];
    DWORD result = GetPrivateProfileString(section.c_str(), key.c_str(), 
                                          defaultValue.c_str(), buffer, 
                                          sizeof(buffer), iniFile.c_str());
    return std::string(buffer);
}

// 从INI文件读取整数
int GetIniInt(const std::string& iniFile, const std::string& section, 
              const std::string& key, int defaultValue) {
    return GetPrivateProfileInt(section.c_str(), key.c_str(), defaultValue, iniFile.c_str());
}

// 从INI文件读取布尔值
bool GetIniBool(const std::string& iniFile, const std::string& section, 
                const std::string& key, bool defaultValue) {
    int value = GetPrivateProfileInt(section.c_str(), key.c_str(), 
                                    defaultValue ? 1 : 0, iniFile.c_str());
    return value != 0;
}

// 检查是否有进程在运行
bool IsProcessRunning(const Config& config) {
    std::vector<std::string> processes = GetRunningProcesses();
    
    for (const auto& pattern : config.processPatterns) {
        for (const auto& process : processes) {
            if (MatchProcessName(process, pattern, config.useRegex)) {
                return true;
            }
        }
    }
    
    return false;
}

// 匹配进程名
bool MatchProcessName(const std::string& processName, const std::string& pattern, bool useRegex) {
    if (useRegex) {
        try {
            std::regex re(pattern, std::regex::icase);
            return std::regex_search(processName, re);
        } catch (const std::regex_error&) {
            return false;
        }
    } else {
        // 使用通配符匹配
        return PathMatchSpec(processName.c_str(), pattern.c_str());
    }
}

// 获取所有运行中的进程
std::vector<std::string> GetRunningProcesses() {
    std::vector<std::string> processes;
    
    DWORD aProcesses[1024], cbNeeded, cProcesses;
    
    if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) {
        return processes;
    }
    
    cProcesses = cbNeeded / sizeof(DWORD);
    
    for (unsigned int i = 0; i < cProcesses; i++) {
        if (aProcesses[i] == 0) continue;
        
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 
                                      FALSE, aProcesses[i]);
        if (hProcess) {
            char szProcessName[MAX_PATH] = "<未知>";
            
            if (GetModuleBaseName(hProcess, NULL, szProcessName, 
                                 sizeof(szProcessName) / sizeof(char))) {
                processes.push_back(szProcessName);
            }
            
            CloseHandle(hProcess);
        }
    }
    
    return processes;
}

// 执行程序
bool ExecuteProgram(const std::string& programPath, const std::string& logFile) {
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));
    
    // 创建可修改的字符串副本
    char cmdLine[MAX_PATH];
    strncpy(cmdLine, programPath.c_str(), MAX_PATH);
    cmdLine[MAX_PATH - 1] = '\0';
    
    // 启动程序
    if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        LogError("CreateProcess", logFile);
        return false;
    }
    
    // 等待程序完成
    WaitForSingleObject(pi.hProcess, INFINITE);
    
    // 获取退出代码
    DWORD exitCode;
    if (GetExitCodeProcess(pi.hProcess, &exitCode)) {
        Log("程序退出代码: " + std::to_string(exitCode), logFile);
    }
    
    // 关闭句柄
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    
    return exitCode == 0;
}

// 记录日志
void Log(const std::string& message, const std::string& logFile) {
    auto now = std::chrono::system_clock::now();
    auto now_time = std::chrono::system_clock::to_time_t(now);
    auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()) % 1000;
    
    std::ostringstream oss;
    oss << std::put_time(std::localtime(&now_time), "%Y-%m-%d %H:%M:%S")
        << '.' << std::setfill('0') << std::setw(3) << now_ms.count();
    
    std::ofstream file(logFile, std::ios_base::app);
    if (file.is_open()) {
        file << "[" << oss.str() << "] " << message << std::endl;
    }
}

// 记录错误
void LogError(const std::string& functionName, const std::string& logFile) {
    DWORD error = GetLastError();
    if (error == 0) {
        return;
    }
    
    LPSTR messageBuffer = nullptr;
    size_t size = FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPSTR)&messageBuffer, 0, NULL);
    
    std::string message(messageBuffer, size);
    LocalFree(messageBuffer);
    
    std::string logMessage = "错误在 " + functionName + ": " + message + "\n堆栈跟踪:\n" + GetStackTrace();
    Log(logMessage, logFile);
}

// 获取堆栈跟踪
std::string GetStackTrace() {
    void* stack[100];
    HANDLE process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    WORD frames = CaptureStackBackTrace(0, 100, stack, NULL);
    
    std::ostringstream oss;
    SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    
    for (int i = 0; i < frames; i++) {
        SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
        oss << i << ": " << symbol->Name << " (0x" << std::hex << symbol->Address << std::dec << ")" << std::endl;
    }
    
    free(symbol);
    return oss.str();
}

编译说明

使用MinGW-w64编译此程序,需要链接以下库:

  • -lshlwapi (用于PathMatchSpec函数)
  • -ldbghelp (用于堆栈跟踪功能)
  • -lpsapi (用于进程枚举)

编译命令示例:

bash 复制代码
g++ -std=c++11 -o process_monitor.exe process_monitor.cpp -lshlwapi -ldbghelp -lpsapi

配置文件示例 (config.ini)

ini 复制代码
[Monitor]
; 要监视的进程名称模式,多个模式用逗号分隔
ProcessPatterns=notepad.exe,chrome.exe,firefox.exe

; 当所有监视进程退出后要执行的程序或批处理
ExecutePath=C:\path\to\your\program.exe

; 日志文件路径
LogFile=monitor.log

; 检查进程状态的间隔(毫秒)
CheckInterval=5000

; 是否使用正则表达式匹配进程名(否则使用通配符)
UseRegex=false

功能说明

  1. 进程监视: 程序会定期检查系统中运行的进程,并根据配置的模式进行匹配
  2. 多种匹配模式: 支持通配符和正则表达式两种匹配方式
  3. 自动执行: 当所有匹配的进程都退出后,自动执行配置的程序或脚本
  4. 日志记录: 详细记录程序运行状态、错误信息和堆栈跟踪
  5. 错误处理: 完善的异常处理和错误报告机制

注意事项

  1. 程序需要以足够权限运行,以便能够枚举系统进程
  2. 堆栈跟踪功能需要调试符号,在发布版本中可能无法提供完整信息
  3. 确保配置的执行路径正确无误
  4. 检查间隔不宜过短,以免对系统性能造成影响
相关推荐
qq_4798754311 分钟前
设置接收超时(SO_RCVTIMEO)
c语言·算法
这儿有一堆花26 分钟前
C++标准库算法:从零基础到精通
c++
张同学的IT技术日记38 分钟前
【奇妙的数据结构世界】用图像和代码对堆栈的使用进行透彻学习 | C++
算法
老赵的博客1 小时前
QT的项目pro qmake编译
开发语言·qt
野生的编程萌新2 小时前
【数据结构】从基础到实战:全面解析归并排序与计数排序
数据结构·算法·排序算法
枯萎穿心攻击2 小时前
从 Unity UGUI 到 Unreal UMG 的交互与高效实践:UI 事件、坐标系适配与性能优化
开发语言·ui·unity·性能优化·ue5·游戏引擎·虚幻引擎
Mercury_Lc2 小时前
【链表 - LeetCode】206. 反转链表【带ACM调试】
算法·链表
WALL-EC2 小时前
Qt工具栏中图标槽函数没有响应的问题分析
开发语言·qt·osgearth
YuTaoShao3 小时前
【LeetCode 热题 100】152. 乘积最大子数组——(解法一)递推
java·算法·leetcode·职场和发展
熙客3 小时前
Java:HashMap的使用
java·开发语言