项目需要将算法放到独立的进程运行,一来避免了算法不稳定导致主程序崩溃,二来避免了多个算法库的依赖冲突。主程序和子程序之间可用 Socket、共享内存、管道等方式进程交互,复杂的也可以上 RPC 框架。

本来首先想到的是使用 QProcess 来启动子进程,但是启动之后想要结束或者是判断进程状态属实不方便,索性直接全用 Win32 的接口来操作。由于需求简单,只是一个主程序对应多个子进程的形式,所以也不需要设计什么进程调度,只需要启动、关闭和检测进程运行状态。分别查找对应的接口:

首先是启动进程,百度到主要有四种接口,参照:,为了方便创建进程时拿到对应的句柄,没用 CreateProcess,选择了 ShellExecuteEx,大致如下:

//初始化
    SHELLEXECUTEINFOW se_info = { 0 };
    //memset(&se_info, 0x00, sizeof(SHELLEXECUTEINFOW));
    //in.required.此结构体字节大小
    se_info.cbSize = sizeof(SHELLEXECUTEINFOW);
    //in.SEE_MASK_NOCLOSEPROCESS用于指示hProcess成员接收到进程句柄。
    se_info.fMask = SEE_MASK_NOCLOSEPROCESS;
    //in.optional.父窗口的句柄,用于显示系统在执行此功能时可能产生的任何消息框。该值可以为NULL。
    //se_info.hwnd = NULL;
    //in.optional.要执行的动作,open打开指定lpFile文件,runas管理员身份启动应用程序
    se_info.lpVerb = L"open";
    //in.以空值结尾的字符串的地址
    se_info.lpFile = reinterpret_cast<LPCWSTR>(info_path.utf16());
    //in.optional.执行参数
    se_info.lpParameters = reinterpret_cast<LPCWSTR>(info_arg.utf16());
    //in.optional.工作目录,为NULL则使用当前目录
    //se_info.lpDirectory = NULL;
    //in.required.SW_HIDE隐藏该窗口并激活另一个窗口,打开的进程不会显示窗口
    //因为hide不显示窗口,测试/调试时可以用SW_SHOW
    se_info.nShow = info.visible ? SW_SHOW : SW_HIDE;
    //out.如果设置了SEE_MASK_NOCLOSEPROCESS并且ShellExecuteEx调用成功,它将把该成员设置为大于32的值。
    //如果函数失败,则将其设置为SE_ERR_XXX错误值,以指示失败的原因。
    //se_info.DUMMYUNIONNAME;
    //out.valid when SEE_MASK_NOCLOSEPROCESS 新启动的应用程序的句柄,如果未启动则为NULL
    //se_info.hProcess;

    qDebug()<<"execute exe."<<info.path<<info.key<<info.args;
    //ShellExecuteEx创建的进程可以提权
    //CreateProcess继承权限,但可以更好地控制
    if(::ShellExecuteExW(&se_info)){
        if(se_info.hProcess != NULL){
            auto hProcess = se_info.hProcess; //拿到句柄
            qDebug()<<"execute success.";
        }
    }
    qDebug()<<"execute fail."<<(intptr_t)(HINSTANCE)se_info.hInstApp;

启动之后就将启动参数和句柄放到列表中,主进程轮询检测子进程是否运行正常,如果异常则重新启动该进程。子进程也可以轮询检测主进程执行状态,异常则自动退出进程,我是通过启动参数的方式将主进程 pid 传递给的子进程,然后以 OpenProcess 接口来拿到对应的句柄。检测进程是否运行我是用 GetExitCodeProcess,大致如下:

//检查所有进程的状态,并重置计数
    for(auto iter = processTable.begin(); iter != processTable.end(); iter++)
    {
        const ProcessInfo &node = iter.value();
        DWORD exit_code;
        //检测进程是否正常运行,把需要重启的重启(hProcess为进程句柄)
        ::GetExitCodeProcess(node.hProcess,&exit_code);
        //如果进程尚未终止且函数成功,则返回的状态为STILL_ACTIVE
        if(exit_code!=STILL_ACTIVE){
              //如果异常可以选择自动重启进程
        }
    }

最后,主程序也可以主动关闭进程,使用 TerminateProcess

//从列表取对应的进程句柄进程关闭
        if(::TerminateProcess(hProcess,-1)){
            qDebug()<<"terminate success.";
        }

(2021-9-22)补充:每个启动参数最好用双引号括起来,避免解析时遇到空格被拆开了。

我做的一个小 Demo:

github 链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/MultiProcess_Win

qt process管理员 qt进程管理_子进程