2015-04-16 4 views
2

Я работаю с приложением, которое предлагает api, чтобы упростить создание сценариев. В принципе, когда вы пишете допустимый ввод, он выдает ответ. Я хотел бы использовать этот вывод отправляет больше входного сигнала и т.д .:Перенаправление ввода-вывода из дочернего процесса с использованием труб - winapi

Input: <nodes> 
Output: 1, 56, 23 
Input <56> 
Output: "Apple" 

То, что я хотел бы сделать, это программа, которая записывает в целевой процесс STDIN, а затем читает вывод от его STDOUT. Чтобы сделать это, я в основном взял код оттуда:

Creating a Child Process with Redirected Input and Output (Windows) - MSDN

Единственная проблема заключается в том, что для того, чтобы читать из дочернего процесса, сначала я должен закрыть дескриптор моего родительского процесса, используемого для записи это означает, что я не могу использовать вывод из дочернего процесса, чтобы написать ему больше материала.

Вот упрощенный код взят из MSDN:

#include <Windows.h> 
#include <string> 

using std::string; 

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

#define BUFSIZE 4096 

void WriteToPipe(string msg); 
void ReadFromPipe(void); 

int main() 
{ 
    /* 
    * CREATE PIPES 
    */ 

    SECURITY_ATTRIBUTES saAttr; 

    // Set the bInheritHandle flag so pipe handles are inherited. 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 

    // Create pipes for the child process's STDOUT and STDIN, 
    // ensures both handle are not inherited 
    CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0); 
    SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0); 

    CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0); 
    SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0); 

    /* 
    * CREATE CHILD PROCESS 
    */ 

    TCHAR szCmdline[]=TEXT("target.exe"); 
    STARTUPINFO siStartInfo; 
    PROCESS_INFORMATION piProcInfo; 

    // 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. 
    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; 

    CreateProcess(NULL, szCmdline, NULL, NULL, TRUE, 0, 
        NULL, NULL, &siStartInfo, &piProcInfo); 

    // 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(g_hChildStd_OUT_Wr); 
    CloseHandle(g_hChildStd_IN_Rd); 
    CloseHandle(piProcInfo.hProcess); 
    CloseHandle(piProcInfo.hThread); 

    /* 
    * ACTUAL APPLICATION 
    */ 

    WriteToPipe("<nodes>\n"); 

    // Need to close the handle before reading 
    CloseHandle(g_hChildStd_IN_Wr); // PROBLEM HERE 

    ReadFromPipe(); 

    WriteToPipe("<56>\n"); // I can't, as I have released the handle already 

    system("pause"); 

    return 0; 
} 

void WriteToPipe(string msg) 
{ 
    WriteFile(g_hChildStd_IN_Wr, msg.c_str(), msg.size(), NULL, NULL); 
} 

void ReadFromPipe(void) 
{ 
    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; 
    } 
} 

Проблема возникает из линии:

CloseHandle(g_hChildStd_IN_Wr); 

Либо я оставил его там, и я не буду в состоянии написать мой дочерний процесс после того, как я прочитал его один раз, или я удаляю его, а затем ReadFromPipe застрял в тупике.

Любая помощь будет оценена, спасибо!

+1

«Чтобы прочитать из дочернего процесса, мне сначала нужно закрыть дескриптор моего родительского процесса, который используется для записи:« Нет, нет, где-то есть способ сделать это. Я сделал это один раз, я просто должен запомнить, как –

+0

Если я удалю строку «CloseHandle (g_hChildStd_IN_Wr);», программа застрянет в ReadFromPipe по строке «bSuccess = ReadFile (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, NULL); , Хотя я не совсем уверен, почему это происходит, оно делает ... любое решение? – MyUsername112358

+0

Да, это то, что я только что пробовал! Но по какой-то причине «result = PeekNamedPipe (g_hChildStd_OUT_Rd, chBuf, BUFSIZE, & dwRead, & dwAvailableBytes, & dwBytesLeft);» всегда выполняется, но dwRead, dwAvailableBytes и dwBytesLeft всегда получают значение 0, есть ли байты для чтения или нет. Я буду больше смотреть на него, может быть, я что-то пропустил ... – MyUsername112358

ответ

2

ReadFile(..., BUFSIZE) означает «подождите, пока программа не напишет BUFSIZE байт или не закрывает конец трубы». Программа записывает только небольшое количество байтов, а затем ждет больше ввода, которое вы не предоставляете. Если вы закроете канал записи, он знает, что больше нет ввода, и завершает работу, после чего ваш ReadFile знает, что больше нет ввода и возврата. Вам нужно найти способ читать только столько байтов, сколько в трубе.

Волшебная штука здесь PeekNamedPipe, которая сообщает вам, сколько данных выдает программа, и, следовательно, сколько вы можете читать без блокировки. Обратите внимание, что вам нужно будет периодически проверять, будет ли программа писать больше байтов с момента последнего проверки.

+0

Для записи первое предложение не совсем корректно. Трубы - особый случай; ReadFile будет выходить каждый раз, когда операция записи завершается с другой стороны канала. (Это не очень помогает в этом конкретном сценарии, но в некоторых контекстах это значительно упрощает.) –