2016-12-05 16 views
1

パイプを使用して、コマンドライン実行可能ファイルからリダイレクトされたstdout出力を取得しています。残念ながら、プロセスが完了するまでは出力がありません。実行可能ファイルは実行中の進行状況を出力し、これは私が解析したいものです。Windows:CreateProcessを使用してリダイレクトされたStdoutのバッファリングを停止する方法

BOOL RunCmd(char *pCmd, 
      char *pWorkingDir, 
      int nWaitSecs, 
      BOOL fRegImport, 
      DWORD *pdwExitCode) 
{ 
    BOOL    fSuccess = TRUE; 
    STARTUPINFO   si; 
    PROCESS_INFORMATION pi; 
    SECURITY_ATTRIBUTES sFileSecurity; 
    ZeroMemory(&sFileSecurity, sizeof(sFileSecurity)); 
    sFileSecurity.nLength  = sizeof(sFileSecurity); 
    sFileSecurity.bInheritHandle = TRUE; 

    HANDLE hReadPipe = NULL; 
    HANDLE hWritePipe = NULL; 

    fSuccess = CreatePipe(&hReadPipe, &hWritePipe, &sFileSecurity, 0); 
    SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0); 

    ZeroMemory(&si, sizeof(si)); 
    ZeroMemory(&pi, sizeof(pi)); 
    si.cb   = sizeof(si); 
    si.dwFlags  = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; 
    si.hStdOutput = hWritePipe; 
    si.hStdError = hWritePipe; 
    si.wShowWindow = SW_HIDE; 

    int rc; 

    // Start the child process. 
    rc = CreateProcess(NULL, // No module name (use command line). 
         pCmd, // Command line. 
         NULL,    // Process handle not inheritable. 
         NULL,    // Thread handle not inheritable. 
         TRUE, 
         CREATE_NO_WINDOW, 
         NULL,    // Use parent's environment block. 
         pWorkingDir,  // Working folder 
         &si,    // Pointer to STARTUPINFO structure. 
         &pi);   // Pointer to PROCESS_INFORMATION structure. 


    if(! rc) 
    return FALSE; 

    // Wait until child process exits. 
    DWORD dwWaitResult; 
    DWORD dwTimeStart = ::GetTickCount(); 
    DWORD dwTimeNow; 

    #define BUFSIZE 4096 

    DWORD dwRead = 0; 
    DWORD dwAvail; 
    CHAR chBuf[ BUFSIZE ]; 
    BOOL bSuccess = TRUE; 

    for(;;) 
    { 
    dwTimeNow = ::GetTickCount(); 
    dwWaitResult = ::WaitForSingleObject(pi.hProcess, ONE_SECOND); 
    dwRead  = 0; 

    for(dwAvail = 0; PeekNamedPipe(hReadPipe, 0, 0, 0, &dwAvail, 0) && dwAvail; dwAvail = 0) 
    { 
     dwRead = 0; 
     ReadFile(hReadPipe, chBuf, min(BUFSIZE, dwAvail), &dwRead, NULL); 

     if(dwRead > 0) 
     { 
     FILE *op = fopen("c:\\REDIR.OUT", "a"); 

     if(op) 
     { 
      fwrite(chBuf, 1, dwRead, op); 
      fclose(op); 
     } 
     } 
    } 

    if(dwWaitResult == WAIT_OBJECT_0) 
    { 
     DWORD dwExitCode; 
     GetExitCodeProcess(pi.hProcess, &dwExitCode); 

     if(pdwExitCode) 
     (*pdwExitCode) = dwExitCode; 

     break; 
    } 

    if(dwWaitResult == WAIT_TIMEOUT) 
    { 
     if(dwTimeNow - dwTimeStart < (DWORD)(ONE_SECOND * nWaitSecs)) 
     continue; 
     else 
     { 
     fSuccess = FALSE; 
     break; 
     } 
    } 

    fSuccess = FALSE; 
    break; 
    } 

    CloseHandle(pi.hProcess); 
    CloseHandle(pi.hThread); 
    CloseHandle(hReadPipe); 
    CloseHandle(hWritePipe); 

    return fSuccess; 
} 

PeekNamedPipe()コールは、毎秒と呼ばれ、プロセスが完了するまでdwAvail毎回ゼロです。

出力をすぐに取得するにはどうすればよいですか?コンソールからプロセスを実行するとき、進捗状況が表示されます。プロセスは、 "\ r"を出力に使用して、同じ行の先頭にパーセンテージを表示します。

+4

バッファリングは、OSではなく、他のアプリにあります。バッファリングを無効にするには、起動するプロセスを変更する必要があります。 –

+0

@RaymondChenありがとう - それは私の最大の恐怖でした。私たちはそのための情報源を持っていません。それを考えると、Visual Studioの「外部ツール」として実行したときの出力をバッファリングしているのを見てきました。あなたは、CMD.exeが進行状況の更新をすべて表示できるならば、ラッパープログラムは同じことをすることができると思います。 – SparkyNZ

+0

@RaymondChenええ、次のリンクで問題を説明しています:https://support.microsoft.com/en-nz/kb/190351 – SparkyNZ

答えて

0

注:私の答えは、MSVCを使用してコンパイルされた実行可能ファイルのみを扱います。

バッファリングポリシーは、Microsoft Cランタイム(CRT)ライブラリ内でコード化されています。詳細hereを学ぶことができます。この記事では、コンソールハンドルを使用し、コンソールバッファを操作してバッファリングされていない出力を受け取ることを提案します。

しかし、STARTUPINFO構造のlpReserved2cbReserved2フィールドを使用して、親プロセスから直接いくつかの内部のフラグでファイルハンドルを継承するために、Microsoft Cランタイム内部の文書化されていない機能があります。 Microsoft Visual Studioで提供されているcrtソースコードで詳細を見つけることができます。または、GitHubでposfhndのようなものを検索してください。

この非公開の機能を利用して、パイプハンドルを提供し、FOPEN | FDEVフラグを子プロセスに指定して、子プロセスを騙して、そのパイプハンドルをFILE_TYPE_CHARハンドルと同じ方法で扱うことができます。

私はこの方法を実証するために働いていますPython3 script

関連する問題