I/O redirection from child process using pipes - winapi

笑着哭i 提交于 2020-01-01 07:01:07

问题


I'm working with an application that offers an api so that scripting it is easier. Basically, when you write valid input, it outputs an answer. I would like to use that output to sends more input, e.g.:

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

What I'd like to do is a program that writes to the target process STDIN, then reads the output from it's STDOUT. To do that, I mostly took the code from there:

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

The only problem is that in order to read from the child process, I first need to close the handle of my parent process used to write to it, which means I can't use the output from the child process to write more stuff to it.

Here is the simplified code taken from 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;
    }
}

The problem comes from the line:

CloseHandle(g_hChildStd_IN_Wr);

Either I leave it there, and I won't be able to write to my child process after I read it once, or I remove it and then ReadFromPipe get stuck in a deadlock.

Any help would be appreciated, thanks!


回答1:


ReadFile(..., BUFSIZE) means "wait until the program writes BUFSIZE bytes, or closes it's end of the pipe". The program is writing just a small number of bytes, and then waiting for more input, which you aren't providing. If you close the write pipe, it knows there's no more input, and quits, at which point your ReadFile knows there's no more input and returns. You need to find a way to read only as many bytes as are in the pipe.

The magic piece here is PeekNamedPipe, which tells you how much data the program has output, and thus how much you can read without blocking. Note that you'll have to keep checking every once in a while to see if the program wrote more bytes since the last time you checked.



来源:https://stackoverflow.com/questions/29684568/i-o-redirection-from-child-process-using-pipes-winapi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!