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. 检查间隔不宜过短,以免对系统性能造成影响
相关推荐
lly2024068 分钟前
组合模式(Composite Pattern)
开发语言
Billlly10 分钟前
ABC 453 个人题解
算法·题解·atcoder
玉树临风ives18 分钟前
atcoder ABC 452 题解
数据结构·算法
游乐码28 分钟前
c#泛型约束
开发语言·c#
Dontla40 分钟前
go语言Windows安装教程(安装go安装Golang安装)(GOPATH、Go Modules)
开发语言·windows·golang
chushiyunen40 分钟前
python rest请求、requests
开发语言·python
feifeigo12342 分钟前
基于马尔可夫随机场模型的SAR图像变化检测源码实现
算法
铁东博客1 小时前
Go实现周易大衍筮法三变取爻
开发语言·后端·golang
baidu_huihui1 小时前
在 CentOS 9 上安装 pip(Python 的包管理工具)
开发语言·python·pip
南 阳1 小时前
Python从入门到精通day63
开发语言·python