11

バックグラウンド:


stdoutプログラムの、stderrおよび戻り値をキャプチャできるようにする必要があるプログラムに取り組んでいます。理想的には、プロセスの詳細を保持するオブジェクト内に保存する文字列にこれらをキャプチャしたいと考えています。私は現在、(私の意見では) 古風な C ファイル ハンドル マジックを使用して、出力をファイルに保存することで機能するコードをいくつか持っています。結果を出力したいときはいつでも、そのファイルを開いて内容を印刷します。

ときどき (作成したプロセスが実行されたままになっている場合)、書き込み用にファイルを開くことができないため、実行可能ファイルの次の実行が失敗することがあります。

問題文:


stdoutWindows で作成されたプロセスの出力を 1 つの文字列に保存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

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;
}

ノート:


  • 私はVS 2010を使用しています
  • スレッドではなく複数のプロセスを使用し続けたいのは、実行するものが独自のプロセスの自由を持つ必要があるためです

編集:


追加の注意: また、指定されたコードを実行する関数を呼び出した直後にプロセスが終了するのを待つようにしているため、その時点でstdoutとの結果を利用できます。stderr

4

3 に答える 3

21

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;
}
于 2013-01-03T23:55:32.690 に答える
3

プロセスの stdout ストリームの内容をキャプチャするには、パイプを使用する必要があります。これを達成する方法については、MSDN に精巧な例があります。

MSDN: リダイレクトされた入力と出力を持つ子プロセスの作成

于 2013-01-03T22:10:46.503 に答える