I'm using CreateProcess
to launch an interactive script interpreter and would like to transparently forward stdin/stdout/stderr from/to the interpreter.
My first attempt was to setup the STARTUPINFO
structure passed to CreateProcess
like
STARTUPINFOA si = { sizeof( si ) };
si.hStdError = ::GetStdHandle( STD_ERROR_HANDLE );
si.hStdOutput = ::GetStdHandle( STD_OUTPUT_HANDLE );
si.hStdInput = ::GetStdHandle( STD_INPUT_HANDLE );
si.dwFlags |= STARTF_USESTDHANDLES;
I.e. I tried to make the script interpreter process use the very same handle for reading/writing as my launcher process uses. That didn't seem to work though (I'm not even sure those standard handles can be inherited).
A second idea, based on the Creating a Child Process with Redirected Input and Output example is to setup three pipes to forward all data written to any of the pipes. Since I don't know how to wait for data to be written to more than one file (WaitForMultipleObjects
cannot synchronize on pipes), I was considering to have three threads, each of which doing a blocking ReadFile
call on a pipe.
I suspect that this might be overkill though so I'm wondering: is there some easier way to do this? I don't need to do any kind of processing of the data passed from/to the script interpreter at all.
As a side note, on Linux I'm using execvp
to just replace the current process with the script interpreter process, but on Windows I need to launch the script interpreter with the main thread in suspended state (so that I can do some bytecode patching) - so even since _execvp seems to be availble on Windows, I apparantly have to use CreateProcess.
In order to wait for I/O on more than one file or pipe, you issue asynchronous I/O requests on each of those files, then wait for completion of said requests. Something along these lines (untested):
HANDLE file1, file2; // initialized somehow
HANDLE events[2];
events[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
events[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
OVERLAPPED overlapped1 = {0};
overlapped1.hEvent = events[0];
OVERLAPPED overlapped2 = {0};
overlapped2.hEvent = events[1];
ReadFile(file1, buffer1, size1, NULL, &overlapped1);
ReadFile(file2, buffer2, size2, NULL, &overlapped2);
WaitForMultipleObjects(2, events, FALSE, INFINITE);
ReadFile
and WaitForMultipleObjects
would need to be called in a loop. You check the return value of WaitForMultipleObjects
to know which operation has completed, use GetOverlappedResult
to discover the outcome of that operation (whether it succeeded, and if so, how many bytes it retrieved), process the data, call ReadFile
for that handle again if you want to read some more from it, then get back to waiting. This is somewhat similar to a loop of non-blocking I/O driven by select
in Linux.
Still more advanced technique is I/O completion ports. This allows one to have a thread pool handling lots of asynchronous I/O. Commonly used in Web servers and such, probably overkill for your case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With