5

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.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • 2
    possible duplicate of [Making CreateProcess inherit the console of the calling process](http://stackoverflow.com/questions/340356/making-createprocess-inherit-the-console-of-the-calling-process) – Frerich Raabe Oct 29 '13 at 13:21
  • 2
    Re: how to wait for I/O on more than one file. You create one `OVERLAPPED` structure, complete with an event handle, for each file handle, and issue asynchronous I/O requests (e.g. `ReadFile`) passing those `OVERLAPPED` structures in. Then wait on those event handles. – Igor Tandetnik Oct 29 '13 at 13:30
  • Could you elaborate on that in an answer, @Igor? That seems to be the primary difference from [previous](http://stackoverflow.com/questions/5485923/launch-an-exe-process-with-stdin-stdout-and-stderr) [questions](http://stackoverflow.com/questions/340356/making-createprocess-inherit-the-console-of-the-calling-process) here. – Shog9 Dec 17 '13 at 01:38
  • @Shog9: OK, I posted something. Not sure if that's what you had in mind. – Igor Tandetnik Dec 17 '13 at 02:24
  • Thanks @Igor - that'll do it. FWIW, I've always just used (blocked) background threads for this, but there are good reasons to avoid that in some cases - note also that waitformultipleobjectsex can be used to integrate this with UI updates in a GUI app. – Shog9 Dec 17 '13 at 02:28
  • Also related: http://stackoverflow.com/questions/340356/making-createprocess-inherit-the-console-of-the-calling-process – Frerich Raabe Feb 11 '14 at 10:26
  • I do wonder whether passing the standard handles explicitely via `STARTUPINFO` is really necessary. What handles does the child process get if you don't pass any handles (and don't use the `STARTF_USESTDHANDLES` flag)? – Frerich Raabe May 28 '14 at 10:38

2 Answers2

5

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.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
1

Popuplating the STARTUPINFO as shown by the OP works fine if you make sure to not pass the CREATE_NO_WINDOW argument in the dwFlags argument of CreateProcess.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207