将Spawned Process的输出捕获到字符串

Sha*_*ley 7 c++ parallel-processing logging

背景:


我的工作需要能够捕捉程序stdout,stderr并返回程序的值.理想情况下,我想在我存储在我的对象中的字符串中捕获这些字符串,该字符串包含进程的详细信息.我目前有一些代码,通过使用一些(在我看来)古老的C文件句柄魔术将输出保存到文件中.任何时候我想输出结果,我打开该文件,然后打印内容.

有时(当我生成的进程继续运行时)我的可执行文件的下一次执行将会中断,因为它无法打开文件进行写入.

问题陈述:


我正在寻找一种方法来将stdout窗口中创建的进程的输出保存为一个字符串,并stderr以更安全,更现代的方式保存到另一个字符串.这样我就可以在每次输出每个创建过程的结果时打印这些内容.

我丑陋的代码:


主要块 -

    int stdoutold = _dup(_fileno(stdout)); //make a copy of stdout
    int stderrold = _dup(_fileno(stdout)); //make a copy of stderr
    FILE *f; 

    if(!fopen_s(&f, "name_of_my_file", "w")){ //make sure I can write to the file
        _dup2(_fileno(f), _fileno(stdout)); //make stdout point to f
        _dup2(_fileno(f), _fileno(stderr)); //make stderr point to f

        fork("command_I_want_to_run", &pi); //run my fake fork (see below)
    }
    else{
        ...//error handling
    }
    _close(_fileno(stdout)); //close tainted stdout
    _close(_fileno(stderr)); //close tainted stderr
    _close(_fileno(f)); //close f
    _dup2(stdoutold, _fileno(stdout)); //fix stdout
    _dup2(stderrold, _fileno(stderr)); //fix stderr
Run Code Online (Sandbox Code Playgroud)

fork-(您可以将其视为CreateProcess,但以防任何人需要查看此处发生的情况)

int fork(std::string s, PROCESS_INFORMATION* pi){
char infoBuf[INFO_BUFFER_SIZE];
int bufCharCount = 
    ExpandEnvironmentStrings(s.c_str(), infoBuf, INFO_BUFFER_SIZE ); 
...
    STARTUPINFO si;
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( pi, sizeof(*pi) );
    LPSTR str = const_cast<char *>(infoBuf);
    if(!CreateProcess(NULL,
        str,
        NULL,
        NULL,
        TRUE,
        0,
        NULL,
        NULL,
        &si,
        pi)
    ){
        int err = GetLastError();
        printf("CreateProcess failed (%d).\n", err);
        CloseHandle((*pi).hProcess);
        CloseHandle((*pi).hThread);
        return err;
    }
return 0;
}
Run Code Online (Sandbox Code Playgroud)

笔记:


  • 我正在使用VS 2010
  • 我想继续使用多个进程,而不是线程,因为我需要运行自己拥有自己进程的自由

编辑:


一个额外注:我也试着等待过程调用运行给出的代码功能之后完成,这样的结果stdout,并stderr提供给我在那个时候.

Sha*_*ley 18

Eddy Luten的回答让我朝着一个很好的方向发展,但是MSDN文档(虽然精心制作)有一些问题.主要是,您需要确保关闭所有不使用的句柄.它还有它希望用户理解的代码.

相反,这是我希望人们能够理解的代码墙:D

#include <string>
#include <iostream>
#include <windows.h> 
#include <stdio.h>
#pragma warning( disable : 4800 ) // stupid warning about bool
#define BUFSIZE 4096
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;

PROCESS_INFORMATION CreateChildProcess(void); 
void ReadFromPipe(PROCESS_INFORMATION); 

int main(int argc, char *argv[]){ 
    SECURITY_ATTRIBUTES sa; 
    printf("\n->Start of parent execution.\n");
    // Set the bInheritHandle flag so pipe handles are inherited. 
    sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
    sa.bInheritHandle = TRUE; 
    sa.lpSecurityDescriptor = NULL; 
    // Create a pipe for the child process's STDERR. 
    if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
        exit(1); 
    }
    // Ensure the read handle to the pipe for STDERR is not inherited.
    if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
        exit(1);
    }
    // Create a pipe for the child process's STDOUT. 
    if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
        exit(1);
    }
    // Ensure the read handle to the pipe for STDOUT is not inherited
    if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
        exit(1); 
    }
    // Create the child process. 
    PROCESS_INFORMATION piProcInfo = CreateChildProcess();

    // Read from pipe that is the standard output for child process. 
    printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
    ReadFromPipe(piProcInfo); 

    printf("\n->End of parent execution.\n");

    // The remaining open handles are cleaned up when this process terminates. 
    // To avoid resource leaks in a larger application, 
    //   close handles explicitly.
    return 0; 
} 

// Create a child process that uses the previously created pipes
//  for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(){
    // Set the text I want to run
    char szCmdline[]="test --log_level=all --report_level=detailed";
    PROCESS_INFORMATION piProcInfo; 
    STARTUPINFO siStartInfo;
    bool bSuccess = FALSE; 

    // Set up members of the PROCESS_INFORMATION structure. 
    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

    // Set up members of the STARTUPINFO structure. 
    // This structure specifies the STDERR and STDOUT handles for redirection.
    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdError = g_hChildStd_ERR_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    // Create the child process. 
    bSuccess = CreateProcess(NULL, 
        szCmdline,     // command line 
        NULL,          // process security attributes 
        NULL,          // primary thread security attributes 
        TRUE,          // handles are inherited 
        0,             // creation flags 
        NULL,          // use parent's environment 
        NULL,          // use parent's current directory 
        &siStartInfo,  // STARTUPINFO pointer 
        &piProcInfo);  // receives PROCESS_INFORMATION
    CloseHandle(g_hChildStd_ERR_Wr);
    CloseHandle(g_hChildStd_OUT_Wr);
    // If an error occurs, exit the application. 
    if ( ! bSuccess ) {
        exit(1);
    }
    return piProcInfo;
}

// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT. 
// Stop when there is no more data. 
void ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
    DWORD dwRead; 
    CHAR chBuf[BUFSIZE];
    bool bSuccess = FALSE;
    std::string out = "", err = "";
    for (;;) { 
        bSuccess=ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) break; 

        std::string s(chBuf, dwRead);
        out += s;
    } 
    dwRead = 0;
    for (;;) { 
        bSuccess=ReadFile( g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) break; 

        std::string s(chBuf, dwRead);
        err += s;

    } 
    std::cout << "stdout:" << out << std::endl;
    std::cout << "stderr:" << err << std::endl;
}
Run Code Online (Sandbox Code Playgroud)