2012-07-07 68 views
0

我正在尝试使用处于挂起状态的CreateProcess创建进程并从中读取stdout。对于基础我拿走了MSDN代码。 在创建过程之后,我将在过程中设置作业限制(尚未实现),然后我将从单独的线程开始读取STDOUT管道。 线程启动之前,我恢复暂停的进程。 结果我没有从ReadFile调用中得到任何东西,它只是停止并等待数据到达,即使过程完成。 下面是代码无法从挂起的进程中单独读取管道中的数据

#include <windows.h> 
#include <tchar.h> 
#include <stdio.h> 
#include <strsafe.h> 

#define BUFSIZE 4096 

HANDLE g_hChildStd_IN_Rd = NULL; 
HANDLE g_hChildStd_IN_Wr = NULL; 
HANDLE g_hChildStd_OUT_Rd = NULL; 
HANDLE g_hChildStd_OUT_RdDup = NULL; 
HANDLE g_hChildStd_OUT_Wr = NULL; 
HANDLE g_hSavedStd_OUT_Wr = NULL; 

HANDLE g_hInputFile = NULL; 

DWORD WINAPI CreateChildProcess(LPVOID param); 
void WriteToPipe(void); 
DWORD WINAPI ReadFromPipe(LPVOID param); 
void ErrorExit(PTSTR); 
    PROCESS_INFORMATION piProcInfo; 

int _tmain(int argc, TCHAR *argv[]) 
{ 
    SECURITY_ATTRIBUTES saAttr; 

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

    // Set the bInheritHandle flag so pipe handles are inherited. 

    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 

    // Create a pipe for the child process's STDOUT. 

    if (! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) 
     ErrorExit(TEXT("StdoutRd CreatePipe")); 

    // Ensure the read handle to the pipe for STDOUT is not inherited. 

    if (! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) 
     ErrorExit(TEXT("Stdout SetHandleInformation")); 

    // Create a pipe for the child process's STDIN. 

    if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) 
     ErrorExit(TEXT("Stdin CreatePipe")); 

    // Ensure the write handle to the pipe for STDIN is not inherited. 

    if (! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) 
     ErrorExit(TEXT("Stdin SetHandleInformation")); 

    STARTUPINFO siStartInfo; 
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); 
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdError = g_hChildStd_OUT_Wr; 
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; 
    siStartInfo.hStdInput = g_hChildStd_IN_Rd; 
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 
    //CreateThread(&saAttr,0, CreateChildProcess, &siStartInfo, 0, NULL); 
    CreateChildProcess(&siStartInfo); 
    ResumeThread(piProcInfo.hThread); 

    printf("\n->Contents of child process STDOUT:\n\n", argv[1]); 
    //ReadFromPipe(NULL); 
    HANDLE thread = CreateThread(&saAttr,0, ReadFromPipe, &siStartInfo, 0, NULL); 
    WaitForSingleObject(thread, INFINITE); 

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

DWORD WINAPI CreateChildProcess(LPVOID param) 
    // Create a child process that uses the previously created pipes for STDIN and STDOUT. 
{ 
    TCHAR szCmdline[]=TEXT("C:\\GnuWin32\\bin\\ls.exe"); 

    STARTUPINFO *siStartInfo = (STARTUPINFO*)param; 

    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 STDIN and STDOUT handles for redirection. 


    // Create the child process. 

    bSuccess = CreateProcess(NULL, 
     szCmdline,  // command line 
     NULL,   // process security attributes 
     NULL,   // primary thread security attributes 
     TRUE,   // handles are inherited 
     CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM | CREATE_NO_WINDOW,    // creation flags 
     NULL,   // use parent's environment 
     NULL,   // use parent's current directory 
     siStartInfo, // STARTUPINFO pointer 
     &piProcInfo); // receives PROCESS_INFORMATION 
    //If resumethread is here - it works well 

    // If an error occurs, exit the application. 
    if (! bSuccess) 
     ErrorExit(TEXT("CreateProcess")); 
    else 
    { 
     // Close handles to the child process and its primary thread. 
     // Some applications might keep these handles to monitor the status 
     // of the child process, for example. 

     CloseHandle(piProcInfo.hProcess); 
     CloseHandle(piProcInfo.hThread); 
    } 
    return 0; 
} 

void WriteToPipe(void) 

    // Read from a file and write its contents to the pipe for the child's STDIN. 
    // Stop when there is no more data. 
{ 
    DWORD dwRead, dwWritten; 
    CHAR chBuf[BUFSIZE]; 
    BOOL bSuccess = FALSE; 

    for (;;) 
    { 
     bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL); 
     if (! bSuccess || dwRead == 0) break; 

     bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL); 
     if (! bSuccess) break; 
    } 

    // Close the pipe handle so the child process stops reading. 

    if (! CloseHandle(g_hChildStd_IN_Wr)) 
     ErrorExit(TEXT("StdInWr CloseHandle")); 
} 

DWORD WINAPI ReadFromPipe(LPVOID param) 

    // 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. 
{ 
    DWORD dwRead, dwWritten; 
    CHAR chBuf[BUFSIZE]; 
    BOOL bSuccess = FALSE; 
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 

    for (;;) 
    { 
     bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); 
     if(! bSuccess || dwRead == 0) break; 

     bSuccess = WriteFile(hParentStdOut, chBuf, 
      dwRead, &dwWritten, NULL); 
     if (! bSuccess) break; 
    } 
    return 0; 
} 

void ErrorExit(PTSTR lpszFunction) 

    // Format a readable error message, display a message box, 
    // and exit from the application. 
{ 
    LPVOID lpMsgBuf; 
    LPVOID lpDisplayBuf; 
    DWORD dw = GetLastError(); 

    FormatMessage(
     FORMAT_MESSAGE_ALLOCATE_BUFFER | 
     FORMAT_MESSAGE_FROM_SYSTEM | 
     FORMAT_MESSAGE_IGNORE_INSERTS, 
     NULL, 
     dw, 
     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
     (LPTSTR) &lpMsgBuf, 
     0, NULL); 

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
     (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
     LocalSize(lpDisplayBuf)/sizeof(TCHAR), 
     TEXT("%s failed with error %d: %s"), 
     lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf); 
    LocalFree(lpDisplayBuf); 
    ExitProcess(1); 
} 

如果我调用ResumeThread(piProcInfo.hThread);在CreateProcess函数之后 - 它运作良好。但是我仍然需要在独立的函数中创建并调用ResumeThread之后的几个函数。 所以,我想做些什么来让程序在单独的线程中获得暂停进程的输出。

回答

2

总是检查返回值。

您正试图在已关闭的手柄上调用ResumeThread。如果你检查了返回值,你会注意到这个函数没有成功,这会告诉你问题是什么。

删除CloseHandle(piProcInfo.hThread)CreateChildProcess和呼叫ResumeThread将工作。

您应该在创建子进程后关闭g_hChildStd_OUT_Wrg_hChildStd_IN_Rd,以便您可以知道子进程何时退出。