CreateProcess 并捕获标准输出

rwa*_*ace 3 c windows winapi

使用 CreateProcess 运行另一个程序时,捕获标准输出的推荐方法是什么?也就是说,将第二个程序打印到标准输出的任何内容都放在第一个程序可以分析的数组中?

这两个程序都是直接用 C 编写的 Win32 程序,没有什么花哨的东西。

小智 9

微软的例子认为这两个程序都是由同一开发人员编写的。就我而言,我正在启动一个在 stdout/stderr 上输出的通用程序。我把微软的例子改成了这样。以下函数externalProgram使用命令行运行通用函数arguments

HANDLE m_hChildStd_OUT_Rd = NULL;
HANDLE m_hChildStd_OUT_Wr = NULL;
HANDLE m_hreadDataFromExtProgram = NULL;

HRESULT RunExternalProgram(std::string externalProgram, std::string arguments)
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES saAttr;

    ZeroMemory(&saAttr, sizeof(saAttr));
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    // Create a pipe for the child process's STDOUT. 

    if (!CreatePipe(&m_hChildStd_OUT_Rd, &m_hChildStd_OUT_Wr, &saAttr, 0))
    {
        // log error
        return HRESULT_FROM_WIN32(GetLastError());
    }

    // Ensure the read handle to the pipe for STDOUT is not inherited.

    if (!SetHandleInformation(m_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
    {
        // log error
        return HRESULT_FROM_WIN32(GetLastError());
    }

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    si.hStdError = m_hChildStd_OUT_Wr;
    si.hStdOutput = m_hChildStd_OUT_Wr;
    si.dwFlags |= STARTF_USESTDHANDLES;

    ZeroMemory(&pi, sizeof(pi));

    std::string commandLine = extProgram + " " + arguments;

    // Start the child process. 
    if (!CreateProcessA(NULL,           // No module name (use command line)
        (TCHAR*)commandLine.c_str(),    // Command line
        NULL,                           // Process handle not inheritable
        NULL,                           // Thread handle not inheritable
        TRUE,                           // Set handle inheritance
        0,                              // No creation flags
        NULL,                           // Use parent's environment block
        NULL,                           // Use parent's starting directory 
        &si,                            // Pointer to STARTUPINFO structure
        &pi)                            // Pointer to PROCESS_INFORMATION structure
        )
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }
    else
    {
        m_hreadDataFromExtProgram  = CreateThread(0, 0, readDataFromExtProgram, NULL, 0, NULL);
    }
    return S_OK;
}

Run Code Online (Sandbox Code Playgroud)

如果CreateProcessA成功,将启动一个外部线程来异步捕获启动程序的输出。从外部线程执行的代码如下:

DWORD __stdcall readDataFromExtProgram(void * argh)
{
    DWORD dwRead;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;

    for (;;)
    {
        bSuccess = ReadFile(m_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if (!bSuccess || dwRead == 0) continue;

        // Log chBuf

        if (!bSuccess) break;
    }
    return 0;
}

Run Code Online (Sandbox Code Playgroud)

HANDLE我省略了代码中涉及的所有线程的任何关闭条件。


And*_*ers 8

简短的回答是创建一个匿名管道,相应地设置hStdOut/hStdErr和结构的dwFlag成员STARTUPINFO,并CreateProcess()继承管道写入端的句柄。不要忘记关闭管道的写入句柄,然后您可以循环读取管道的读取句柄,直到它因ERROR_BROKEN_PIPE错误而失败。

MSDN 提供了一个详细的例子:

创建具有重定向输入和输出的子进程

您不是第一个这样做的人,StackOverflow 上应该有大量示例代码和重复问题。