问题
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