2013-01-03 12 views
6

背景:私はstdoutstderrをキャプチャし、プログラムの値を返すことができる必要があるプログラムに取り組んでいますキャプチャ出力


。理想的には、これらの文字列をオブジェクトの内部に格納して、プロセスの詳細を保持するストリングでキャプチャしたいと考えています。私は現在、いくつかの(私の意見では)古典的なCファイルハンドルマジックを使用してファイルに出力を保存することによって動作するいくつかのコードを持っています。結果を出力したいときはいつでも、そのファイルを開き、内容を印刷します。

ときどき(プロセスが起動しているときに)実行ファイルの次回の実行は、ファイルを書き込み用に開くことができないため停止します。

問題文:


私はより安全に、より現代的な方法で別の文字列にウィンドウで作成されたプロセスのstdoutstderrからの出力を保存する方法を探しています。こうすることで、作成した各プロセスの結果を出力するような気がするたびに、その内容を印刷することができます。

私の醜いコード:


メインchunk-

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
  • を使用してい

編集:


追加の注記:コードを実行する関数を呼び出した直後に処理が完了するのを待っているので、その時点でstdoutstderrという結果が表示されます。

+0

関連:呼び出しますが、POSIXのシステムコールに類似のコードの振る舞いを確認しますバックグラウンドスレッドとPeekNamedPipeを()を持つ[?stderrの標準入力、標準出力にexeファイル/プロセスを起動して](http://stackoverflow.com/questions/5485923/launch-an-exe-process-stdin-stdout-and-stderr/39648986)を参照してください。また、非常に便利な[tiny-process-library](https://github.com/eidheim/tiny-process-library)もご覧ください。 – Delgan

答えて

4

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

MSDN: Creating a Child Process with Redirected Input and Output

+0

これはトリックを行うかもしれないようです。 一度それを実装しようとすると、私はそれを調べてあなたに戻ってきます。 +1 –

17

渦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; 
} 
+0

このコード例は高く評価されていますが、コメントに誤りがあると思います。コメントはstdinとstdoutがリダイレクトされると主張していますが、コードはstderrとstdoutがリダイレクトされることを意味しています。コメントはMSDNの記事よりも明確になっているので、これを修正することは素晴らしいことです。 –

+0

ニースキャッチ。私は変更を加えました。 –

+0

コード例をありがとうが、なぜPROCESS_INFORMATIONがReadFromPipe()に渡されたのか教えていただけますか? – randomuser15995183

1

ショーンブレークスリーコードがある良いマイクロソフトのサンプルコードのリワークが、それは大規模な標準出力に問題のビットがありましたstderrインターリーブされたストリームは順不同です。また、いくつかのハンドルがリークしています(これはサンプルコードでは問題ありません)。

#include <windows.h> 
#include <stdio.h> 
#include <malloc.h> 

#ifdef __cplusplus 
#define BEGIN_C extern "C" { 
#define END_C } // extern "C" 
#define null nullptr 
#else 
#define BEGIN_C 
#define END_C 
#define null ((void*)0) 
#endif 

BEGIN_C 

int system_np(const char* command, int timeout_milliseconds, 
       char* stdout_data, int stdout_data_size, 
       char* stderr_data, int stderr_data_size, int* exit_code); 

typedef struct system_np_s { 
    HANDLE child_stdout_read; 
    HANDLE child_stderr_read; 
    HANDLE reader; 
    PROCESS_INFORMATION pi; 
    const char* command; 
    char* stdout_data; 
    int stdout_data_size; 
    char* stderr_data; 
    int stderr_data_size; 
    int* exit_code; 
    int timeout; // timeout in milliseconds or -1 for INIFINTE 
} system_np_t; 

static char stdout_data[16 * 1024 * 1024]; 
static char stderr_data[16 * 1024 * 1024]; 

int main(int argc, char *argv[]) { 
    int bytes = 1; 
    for (int i = 1; i < argc; i++) { 
     bytes += (int)strlen(argv[i]) + 1; 
    } 
    char* command = (char*)alloca(bytes); 
    command[0] = 0; 
    char* p = command; 
    for (int i = 1; i < argc; i++) { 
     int n = (int)strlen(argv[i]); 
     memcpy(p, argv[i], n); p += n; 
     *p = (i == argc - 1) ? 0x00 : 0x20; 
     p++; 
    } 
    int exit_code = 0; 
    if (command[0] == 0) { 
     command = (char*)"cmd.exe /c \"dir /w /b\""; 
    } 
    int r = system_np(command, 100 * 1000, stdout_data, sizeof(stdout_data), stderr_data, sizeof(stderr_data), &exit_code); 
    if (r != 0) { 
     fprintf(stderr, "system_np failed: %d 0x%08x %s", r, r, strerror(r)); 
     return r; 
    } else { 
     fwrite(stdout_data, strlen(stdout_data), 1, stdout); 
     fwrite(stderr_data, strlen(stderr_data), 1, stderr); 
     return exit_code; 
    } 
} 

static int peek_pipe(HANDLE pipe, char* data, int size) {   
    char buffer[4 * 1024]; 
    DWORD read = 0; 
    DWORD available = 0; 
    bool b = PeekNamedPipe(pipe, null, sizeof(data), null, &available, null); 
    if (!b) { 
     return -1; 
    } else if (available > 0) { 
     int bytes = min(sizeof(buffer), available); 
     b = ReadFile(pipe, buffer, bytes, &read, null); 
     if (!b) { 
      return -1; 
     } 
     if (data != null && size > 0) { 
      int n = min(size - 1, (int)read); 
      memcpy(data, buffer, n); 
      data[n + 1] = 0; // always zero terminated 
      return n; 
     } 
    } 
    return 0; 
} 

static DWORD WINAPI read_from_all_pipes_fully(void* p) { 
    system_np_t* system = (system_np_t*)p; 
    unsigned long long milliseconds = GetTickCount64(); // since boot time 
    char* out = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data : null; 
    char* err = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data : null; 
    int out_bytes = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data_size - 1 : 0; 
    int err_bytes = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data_size - 1 : 0; 
    for (;;) { 
     int read_stdout = peek_pipe(system->child_stdout_read, out, out_bytes); 
     if (read_stdout > 0 && out != null) { out += read_stdout; out_bytes -= read_stdout; } 
     int read_stderr = peek_pipe(system->child_stderr_read, err, err_bytes); 
     if (read_stderr > 0 && err != null) { err += read_stderr; err_bytes -= read_stderr; } 
     if (read_stdout < 0 && read_stderr < 0) { break; } // both pipes are closed 
     unsigned long long time_spent_in_milliseconds = GetTickCount64() - milliseconds; 
     if (system->timeout > 0 && time_spent_in_milliseconds > system->timeout) { break; } 
     if (read_stdout == 0 && read_stderr == 0) { // nothing has been read from both pipes 
      HANDLE handles[2] = {system->child_stdout_read, system->child_stderr_read}; 
      WaitForMultipleObjects(2, handles, false, 1); // wait for at least 1 millisecond (more likely 16) 
     } 
    } 
    if (out != null) { *out = 0; } 
    if (err != null) { *err = 0; } 
    return 0; 
} 

static int create_child_process(system_np_t* system) { 
    SECURITY_ATTRIBUTES sa = {0}; 
    sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
    sa.bInheritHandle = true; 
    sa.lpSecurityDescriptor = null; 
    HANDLE child_stdout_write = INVALID_HANDLE_VALUE; 
    HANDLE child_stderr_write = INVALID_HANDLE_VALUE; 
    if (!CreatePipe(&system->child_stderr_read, &child_stderr_write, &sa, 0)) { 
     return GetLastError(); 
    } 
    if (!SetHandleInformation(system->child_stderr_read, HANDLE_FLAG_INHERIT, 0)){ 
     return GetLastError(); 
    } 
    if (!CreatePipe(&system->child_stdout_read, &child_stdout_write, &sa, 0)) { 
     return GetLastError(); 
    } 
    if (!SetHandleInformation(system->child_stdout_read, HANDLE_FLAG_INHERIT, 0)){ 
     return GetLastError(); 
    } 
    // Set the text I want to run 
    STARTUPINFO siStartInfo = {0}; 
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdError = child_stderr_write; 
    siStartInfo.hStdOutput = child_stdout_write; 
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; 
    siStartInfo.wShowWindow = SW_HIDE; 
    bool b = CreateProcessA(null, 
     (char*)system->command, 
     null,    // process security attributes 
     null,    // primary thread security attributes 
     true,    // handles are inherited 
     CREATE_NO_WINDOW, // creation flags 
     null,    // use parent's environment 
     null,    // use parent's current directory 
     &siStartInfo,  // STARTUPINFO pointer 
     &system->pi);  // receives PROCESS_INFORMATION 
    int err = GetLastError(); 
    CloseHandle(child_stderr_write); 
    CloseHandle(child_stdout_write); 
    if (!b) { 
     CloseHandle(system->child_stdout_read); system->child_stdout_read = INVALID_HANDLE_VALUE; 
     CloseHandle(system->child_stderr_read); system->child_stderr_read = INVALID_HANDLE_VALUE; 
    } 
    return b ? 0 : err; 
} 

int system_np(const char* command, int timeout_milliseconds, 
       char* stdout_data, int stdout_data_size, 
       char* stderr_data, int stderr_data_size, int* exit_code) { 
    system_np_t system = {0}; 
    if (exit_code != null) { *exit_code = 0; } 
    if (stdout_data != null && stdout_data_size > 0) { stdout_data[0] = 0; } 
    if (stderr_data != null && stderr_data_size > 0) { stderr_data[0] = 0; } 
    system.timeout = timeout_milliseconds > 0 ? timeout_milliseconds : -1; 
    system.command = command; 
    system.stdout_data = stdout_data; 
    system.stderr_data = stderr_data; 
    system.stdout_data_size = stdout_data_size; 
    system.stderr_data_size = stderr_data_size; 
    int r = create_child_process(&system); 
    if (r == 0) { 
     system.reader = CreateThread(null, 0, read_from_all_pipes_fully, &system, 0, null); 
     if (system.reader == null) { // in theory should rarely happen only when system super low on resources 
      r = GetLastError(); 
      TerminateProcess(system.pi.hProcess, ECANCELED); 
     } else { 
      bool thread_done = WaitForSingleObject(system.pi.hThread, timeout_milliseconds) == 0; 
      bool process_done = WaitForSingleObject(system.pi.hProcess, timeout_milliseconds) == 0; 
      if (!thread_done || !process_done) { 
       TerminateProcess(system.pi.hProcess, ETIME); 
      } 
      if (exit_code != null) { 
       GetExitCodeProcess(system.pi.hProcess, (DWORD*)exit_code); 
      }   
      CloseHandle(system.pi.hThread); 
      CloseHandle(system.pi.hProcess); 
      CloseHandle(system.child_stdout_read); system.child_stdout_read = INVALID_HANDLE_VALUE; 
      CloseHandle(system.child_stderr_read); system.child_stderr_read = INVALID_HANDLE_VALUE; 
      WaitForSingleObject(system.reader, INFINITE); // join thread 
      CloseHandle(system.reader); 
     } 
    } 
    if (stdout_data != null && stdout_data_size > 0) { stdout_data[stdout_data_size - 1] = 0; } 
    if (stderr_data != null && stderr_data_size > 0) { stderr_data[stderr_data_size - 1] = 0; } 
    return r; 
} 

END_C 
関連する問題