For one of my little projects at work, I’ve been creating a utility which will run as a child process which is just hosted as a console application. It prints a lot of output to console, and it would be kinda useful to be able to capture all the output from the child process, either in a text file, or by reading it in the parent process, or both. I know you can capture child process output in the parent process, or redirect it to a file by passing in a file handle to the CreateProcess API. However, it seems rather a shame to also give up the console window output in the same way…
So here I started wondering – shouldn’t it be possible to keep using Console.WriteLine and have the output appear in multiple places, by using some sort of special splitting or cloning pipe? A little research turned up nothing really explicit on what to do, but a few Windows functions which look they could feature somewhere in a solution:
GetStdHandle, SetStdHandle, CreatePipe, CreateFile.
So where do we get started?
By reading about SetStdHandle, GetStdHandle, and CreateFile with special “CONIN$” and “CONOUT$” files we can see that there’s a few abstraction layers here.
Calling SetStdHandle will let us insert our own file handle underneath the logical ‘standard input/standard output’ layer at the raw file handle layer. But we can only make each concept logically point at a single file handle, so what should we insert in order to have output in two places?
Part 2: Splitting
The current solution I have here is to create an anonymous pipe, and point standard output at it’s ‘input’ end. Alongside this, I have a dedicated thread reading in all data from the pipe, and duplicating the output by writing it to any number of other file handles.
Part 3: Output to console window even while standard output is redirected
The Windows CreateFile API supports this scenario by allowing use of some magic file names to get the actual raw file handle for the console window: “CONOUT$” being the one I am interested in. I had a couple issues getting the p/invoke declarations right. The most aggravating one to debug was caused by assuming that the values of the .net FileAccess enumeration would correspond to the FILEACCESS parameter that CreateFile takes. They don’t.
Critique: I feel a little sad that we need a whole extra dedicated to pumping data around, but oh well - it’s not that bad for what I want to do. The other issue with how I have this set up right now that isn’t necessarily obvious from reading the above code, is that currently all this code is living inside of the process whose output I want to fork - and that seems a little bit wrong, since the whole point was that this process would just think that it was writing to console without knowing anything about the forking process. This is somewhat fixable by using the CreateProcess redirect standard output feature appropriately - assuming that you are happy to be outputting to the parent app’s console window... that doesn’t sound quite right either. Hmm. Well, now that I’ve done all this work, hopefully someone will bring up an obvious and better alternative I’ve overlooked!