2016-12-05 8 views
1

파이프를 사용하여 명령 줄 실행 파일에서 표준 출력을 리디렉션하도록하고 있습니다. 불행히도 프로세스가 완료 될 때까지 출력이 없습니다. 실행 파일은 실행될 때 진행 상태를 출력하고 이것은 내가 파싱하고 싶은 것입니다. 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을 참조하십시오. 이 기사에서는 콘솔 핸들을 사용하여 콘솔 버퍼를 조작하여 버퍼되지 않은 출력을 수신하도록 제안합니다.

그러나, 파일을 상속 마이크로 소프트 C 런타임 내부 문서화되지 않은 기능은 STARTUPINFO 구조의 lpReserved2cbReserved2 필드를 사용하여 부모 프로세스에서 직접 일부 내부 플래그로 처리있다. 자세한 내용은 Microsoft Visual Studio에서 제공하는 crt 소스 코드에서 찾을 수 있습니다. 또는 GitHub에서 posfhnd과 같은 것을 검색하십시오.

우리는이 문서화되지 않은 기능을 활용하여 파이프 핸들을 제공하고 FOPEN | FDEV 플래그를 자식 프로세스에 지정하여 해당 파이프 핸들을 FILE_TYPE_CHAR 핸들과 동일한 방식으로 처리하도록 속일 수 있습니다.

나는이 방법을 보여주기 위해 Python3 script으로 작업했다.