1

Here's the situation: we have a build server, which services requests over a socket and exposes no UI to the machine it is running on. Based on the requests it receives, it launches build scripts (which are typically MS batch files) and serves the files these produce. For diagnostic purposes, the build scripts are launched with stdout and stderr redirected to a pipe, and this output is saved into a buffer by the build server application and passed back to the client requesting the build. (That way, if the build fails, the person who requested the build can see what the error message was.)

During debug, this build server was built as a console application, mainly for the convenience of being able to print diagnostic messages (client connected, request made, etc). Once everything was working just right, I rebuilt it as a non-console app.

Suddenly, the behavior of the application changed. The output captured by the pipe was reduced to only the output directly generated by the batch file (i.e. commands echoed, in this case, since echo wasn't off). The subprocesses launched by the batch files (such as MSBuild, nmake, and others) opened up console windows and directed their output to the windows instead of to the pipe.

I suspect what's happening is that when the application was a console app, the console application didn't attempt to manage its own input/output and instead inherited from the running console, resulting in the _dup2 on the I/O fds staying live. Thus everything worked fine. But in the situation where the parent build server didn't create a console window, the subprocesses spawned by the batch file felt the need to create their own and handle their own I/O, robbing the build server of the captured output (not to mention being extremely annoying to anyone connected to the server's desktop).

So basically the question is simple: how can a non-console launch applications and prevent this behavior? I know it's possible in principle for most console apps because Emacs shell-mode is an example of a non-console application capturing and redirecting command output on Windows, but I have no idea how to do it.

Currently I am using Win32's POSIX-flavored CRT functions: _pipe, _dup, _dup2, _close, _spawnvp, and _read. I'm aware that these are thin wrappers around the "real" win32 functions but being a UNIX guy by training I figured I'd save myself the trouble of wading through CreateProcess and friends.

Development language needs to be C, because the build server is actually written in Haskell and linking C is simpler. (For any of you Haskellers out there -- I rolled my own spawn/redirect code and linked with the FFI instead of using System.Process because I crucially need both stdout and stderr redirected, and on Windows this is non-trivial to do in pure Haskell).

Of course I can use the build server as a console app and just minimize it on the server, no big deal, it works great if I do that. But I'd rather not ... any ideas?

EDIT: Thought I might mention that I don't do anything with stdin. Should I be creating a stdin fd, maybe? These scripts aren't interactive.

808140
  • 205
  • 2
  • 11

2 Answers2

1

You could call AllocConsole to create a console, but that's not fundamentally different from creating the application as a console app.

I think a better solution would be to open your log file and then call SetStdHandle, passing it STD_OUTPUT_HANDLE and the handle of the file that you just opened. I think that should give you a stdout that the other processes will write to.

Alternatively, you could perhaps find a way to hide that console window completely (remove it from the task bar, etc.)

Additional info:

You could perhaps try getting the console window handle and calling ShowWindow(handle, SW_HIDE). That removes the window from the screen, and yet when you write to stdout it actually writes to that window. You should be able to get the console window handle by calling GetConsoleWindow.

If that doesn't work, you could have your program take a parameter -hide. If that is on the command line, then the program starts a new copy of itself, but in the process start info it says to start the process with a hidden window.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Your pointer to `AllocConsole` was most useful, because it explains the behavior I'm seeing. When you start a console app, it seems it tries to `AllocConsole`, but each console process can have only one associated console, and by default it inherits from its parent. So in the situation where my server is a console app, `AllocConsole` fails, but in the situation where it's not, it doesn't. `AllocConsole` sets up all I/O file descriptors to point to the new console and thus overrides my pipe. [Here is a solution but it seems crazy to me.](http://www.codeproject.com/KB/threads/RTconsole.aspx) – 808140 Sep 16 '11 at 21:00
  • @808140: Yes, that solution does seem crazy. See my alternate method. That might do what you want. – Jim Mischel Sep 16 '11 at 22:08
0

This problem has been addressed several times on SO. Take a look at this question, for example.

Community
  • 1
  • 1
Carey Gregory
  • 6,836
  • 2
  • 26
  • 47
  • I've seen several questions generally addressing the problem of redirection, such as the one you linked to. That is not my problem. I can redirect the output of child console processes just fine, *assuming that the parent process is also a console application.* In particular, the suggested solutions in that thread are all basically C++ wrappers around what I'm already doing with `_pipe` and `_dup2`. – 808140 Sep 16 '11 at 17:02
  • Then I apparently misread your question. I don't think you're going to be able to do what you want without wading into CreateProcess. That's the only way you can programmatically control the std* handles of child processes. – Carey Gregory Sep 16 '11 at 17:09