Как я могу прочитать вывод дочернего процесса?

Я написал функцию, которая пытается прочитать вывод командной строки дочернего процесса через канал. Это должно быть простое подмножество MSDN Создание дочернего процесса с перенаправленным вводом и выводом статья, но я явно ошибаюсь.

Вызов ReadFile (...) ниже блокирует навсегда, независимо от того, помещаю ли я его до или после вызова WaitForSingleObject (...), который должен сигнализировать об окончании дочернего процесса.

Я прочитал все ответы, которые предлагают «Использовать асинхронный файл ReadFile», и я открыт для этого предложения, если кто-то может дать мне некоторое представление о том, как это делается на конвейере. Хотя я не понимаю, почему для этого случая нужен асинхронный ввод-выво

#include "stdafx.h"
#include <string>
#include <windows.h>

unsigned int launch( const std::string & cmdline );

int _tmain(int argc, _TCHAR* argv[])
{
    launch( std::string("C:/windows/system32/help.exe") );  
    return 0;
}

void print_error( unsigned int err )
{
    char* msg = NULL;
    FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        err,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPSTR)&msg,
        0, NULL );
    std::cout << "------ Begin Error Msg ------" << std::endl;
    std::cout << msg << std::endl;
    std::cout << "------  End Error Msg  ------" << std::endl;

    LocalFree( msg );
}

unsigned int launch( const std::string & cmdline )
{
    TCHAR cl[_MAX_PATH*sizeof(TCHAR)]; 
    memset( cl, 0, sizeof(cl) );
    cmdline.copy( cl, (_MAX_PATH*sizeof(TCHAR)) - 1);    

    HANDLE stdoutReadHandle = NULL;
    HANDLE stdoutWriteHandle = NULL;

    SECURITY_ATTRIBUTES saAttr; 
    memset( &saAttr, 0, sizeof(saAttr) ); 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 

    // Create a pipe for the child process's STDOUT. 
    if ( ! CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000) )      
        throw std::runtime_error( "StdoutRd CreatePipe" ); 
    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if ( ! SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0) )
        throw std::runtime_error( "Stdout SetHandleInformation" ); 

    STARTUPINFO startupInfo; 
    memset( &startupInfo, 0, sizeof(startupInfo) ); 
    startupInfo.cb = sizeof(startupInfo);
    startupInfo.hStdError = stdoutWriteHandle;
    startupInfo.hStdOutput = stdoutWriteHandle;
    startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    startupInfo.dwFlags |= STARTF_USESTDHANDLES;

    char* rawEnvVars = GetEnvironmentStringsA();

    //__asm _emit 0xcc;

    PROCESS_INFORMATION processInfo;
    memset( &processInfo, 0, sizeof(processInfo) );

    std::cout << "Start [" << cmdline << "]" << std::endl;
    if ( CreateProcessA( 0, &cl[0], 0, 0, false, 
        CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, 
        rawEnvVars, 0, &startupInfo, &processInfo ) )
    {       
        //CloseHandle( stdoutWriteHandle );
        DWORD wordsRead;
        char tBuf[257] = {'\0'};
        bool success = true;
        std::string outBuf("");
        unsigned int t;
        while(success) {
            //__asm _emit 0xcc;
            std::cout << "Just before ReadFile(...)" << std::endl;
            success = ReadFile( stdoutReadHandle, tBuf, 256, &wordsRead, NULL);
            (t=GetLastError())?print_error(t):t=t;
            std::cout << "Just after ReadFile(...) | read " << wordsRead<< std::endl;
            std::cout << ".";
            if( success == false ) break;
            outBuf += tBuf;
            tBuf[0] = '\0';
        }
        std::cout << "output = [" << outBuf << "]" << std::endl;


        if ( WaitForSingleObject( processInfo.hProcess, INFINITE ) == WAIT_OBJECT_0 )
        {
            unsigned int exitcode = 0;
            GetExitCodeProcess( processInfo.hProcess, (LPDWORD)&exitcode );                
            std::cout << "exitcode = [" << exitcode << "]" << std::endl;

            //__asm _emit 0xcc;
            CloseHandle( processInfo.hProcess );
            CloseHandle( processInfo.hThread );

            return exitcode;
        }
    }
    else
    {
        DWORD procErr = GetLastError();
        std::cout << "FAILED TO CREATE PROCESS!" << std::endl;
        print_error( procErr );
    }
    return -1;
} // end launch()

Ответы на вопрос(2)

FALSE дляbInheritHandles аргументCreateProcess, Новый процесс не может использовать канал, если он не наследует дескриптор к нему. Для того чтобы дескриптор был унаследован,bInheritHandles аргумент должен бытьTRUE and у дескриптора должно быть включено наследование.

Другие вопросы:

You're specifying CREATE_UNICODE_ENVIRONMENT but passing an ANSI environment block. Note that it is easier to pass NULL for lpEnvironment and let the system copy the environment block for you. You don't need to specify CREATE_UNICODE_ENVIRONMENT in this case, even if you're compiling in Unicode mode.

Similarly, if you're calling CreateProcessA you should be using STARTUPINFOA.

You don't zero-terminate tBuf each time around the loop, so you'll get spurious extra characters in your output buffer.

You need to close stdoutWriteHandle before you enter your read loop, or you won't know when the subprocess exits. (Or you could use asynchronous IO and check for process exit explicitly.)

GetLastError() is undefined if an API function succeeds, so you should only be calling it if ReadFile returns FALSE. (Of course, in this case this is purely cosmetic since you aren't acting on the error code.)

Для справки, вот моя исправленная версия вашего кода. Я превратил его в простой C (извините!), Потому что это то, с чем я знаком. Я скомпилировал и протестировал в режиме Unicode, но я думаю, что он также должен работать без изменений в режиме ANSI.

#define _WIN32_WINNT _WIN32_WINNT_WIN7
#include <windows.h>
#include <stdio.h>

void launch(const char * cmdline_in)
{
    PROCESS_INFORMATION processInfo;
    STARTUPINFOA startupInfo; 
    SECURITY_ATTRIBUTES saAttr; 

    HANDLE stdoutReadHandle = NULL;
    HANDLE stdoutWriteHandle = NULL;

    char cmdline[256];
    char outbuf[32768];
    DWORD bytes_read;
    char tBuf[257];

    DWORD exitcode;

    strcpy_s(cmdline, sizeof(cmdline), cmdline_in);

    memset(&saAttr, 0, sizeof(saAttr));
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 

    // Create a pipe for the child process's STDOUT. 
    if (!CreatePipe(&stdoutReadHandle, &stdoutWriteHandle, &saAttr, 5000))
    {
        printf("CreatePipe: %u\n", GetLastError());
        return;
    }

    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if (!SetHandleInformation(stdoutReadHandle, HANDLE_FLAG_INHERIT, 0))
    {
        printf("SetHandleInformation: %u\n", GetLastError());
        return;
    }

    memset(&startupInfo, 0, sizeof(startupInfo));
    startupInfo.cb = sizeof(startupInfo);
    startupInfo.hStdError = stdoutWriteHandle;
    startupInfo.hStdOutput = stdoutWriteHandle;
    startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    startupInfo.dwFlags |= STARTF_USESTDHANDLES;

    // memset(&processInfo, 0, sizeof(processInfo));  // Not actually necessary

    printf("Starting.\n");

    if (!CreateProcessA(NULL, cmdline, NULL, NULL, TRUE,
        CREATE_NO_WINDOW, NULL, 0, &startupInfo, &processInfo))
    {
        printf("CreateProcessA: %u\n", GetLastError());
        return;
    }

    CloseHandle(stdoutWriteHandle);

    strcpy_s(outbuf, sizeof(outbuf), "");

    for (;;) {
        printf("Just before ReadFile(...)\n");
        if (!ReadFile(stdoutReadHandle, tBuf, 256, &bytes_read, NULL))
        {
            printf("ReadFile: %u\n", GetLastError());
            break;
        }
        printf("Just after ReadFile, read %u byte(s)\n", bytes_read);
        if (bytes_read > 0)
        {
            tBuf[bytes_read] = '\0';
            strcat_s(outbuf, sizeof(outbuf), tBuf);
        }
    }

    printf("Output: %s\n", outbuf);

    if (WaitForSingleObject(processInfo.hProcess, INFINITE) != WAIT_OBJECT_0)
    {
        printf("WaitForSingleObject: %u\n", GetLastError());
        return;
    }

    if (!GetExitCodeProcess(processInfo.hProcess, &exitcode))
    {
        printf("GetExitCodeProcess: %u\n", GetLastError());
        return;
    }

    printf("Exit code: %u\n", exitcode);

    CloseHandle( processInfo.hProcess );
    CloseHandle( processInfo.hThread );

    return;
}

int main(int argc, char** argv)
{
    launch("C:\\windows\\system32\\help.exe");
    return 0;
}

Существует "LPOVERLAPPED lpOverlapped" Параметр для ReadFile (), который вы установили в NULL. Похоже, что единственный путь - разрешить перекрывающийся ввод-вывод в вашем канале, а затем использовать WaitForSingleObject () для & quot; overlapped.hEvent & quot ;.

Другой способ заключается в использованииConnectNamedPipe функционировать и создать структуру OVERLAPPED для канала.

ВАШ ОТВЕТ НА ВОПРОС