Вопрос по c, winapi, windows – Как я могу прочитать вывод дочернего процесса?

6

Error: User Rate Limit ExceededError: User Rate Limit ExceededError: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

#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()
Re: @ Харри Джонстон - он зависает в первый раз через цикл, который вызывает ReadFile (...) nolandda
вы пробовали вместо этого popen / _popen? user411313
Зависает ли он при первом вызове ReadFile или последующем? Harry Johnston

Ваш Ответ

2   ответа
9

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;
}
Для дальнейшего использования самый безопасный подход - явно указать, какие дескрипторы наследовать, используяPROC_THREAD_ATTRIBUTE_HANDLE_LIST, К сожалению, в документации не ясно, за что вы должны пройтиbInheritHandles если вы предоставляете явный список дескрипторов; экспериментирование может потребоваться.
Вы на месте. Аргумент bInheritHandles для CreateProcess (...) был именно им. Извините за другой флот. В основном это была глупая отладка и минимизация вопросов. В остальном просто непростительная неряшливость. nolandda
Как мне это сделать в проекте UNICODE?
1

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

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

Похожие вопросы