views:

4266

answers:

5

I am wrapping existing C++ code from a BSD project in our own custom wrapper and I want to integrate it to our code with as few changes as possible. This code uses fprintf to print to stderr in order to log / report errors.

I want to redirect this to an alternative place within the same process. On unix I have done this with a socketpair and a thread: one end of the socket is where I send stderr (via a call to dup2) and the other end is monitored in a thread, where I can then process the output.

This does not work on windows though because a socket is not the same as a file handle.

All documents I have found on the web show how to redirect output from a child process, which is not what I want. How can I redirect stderr within the same process getting a callback of some sort when output is written? (and before you say so, I've tried SetStdHandle but cannot find any way to make this work)...

Thanks!

+3  A: 
Greg Hewgill
A: 

Greg:

I need to try this: I should note, I have found the openosfhandle function before but I've not looked at doing it this way: IE, all existing code shows how to use that function to get an OS handle to a socket, and then use the same method I was trying (by calling SetStdHandle using the newly found handle).

One thing though: are you sure this works for stuff that uses the underlying streams? I've found that if you do something as simple as set up a pipe, and call SetStdHandle to set it for stderr, then subsequently call fprintf you will still see it put the ouput in the console. It seems that SetStdHandle does not affect stuff like fprintf etc. Maybe thats why you are doing things this way.

You say I should be able to open a pipe and redirect....would use use dup2 for this? And if so how would you turn a pipe into something suitable for this?

Thanks for the prompt answer though.

jkp
+1  A: 

You have to remember that what MSVCRT calls "OS handles" are not Win32 handles, but another layer of handles added just to confuse you. MSVCRT tries to emulate the Unix handle numbers where stdin=0, stdout=1, stderr=2 and so on. Win32 handles are numbered differently and their values always happen to be a multiple of 4. Opening the pipe and getting all the handles configured properly will require getting your hands messy. Using the MSVCRT source code and a debugger is probably a requirement.

Greg Hewgill
A: 

So, the answer Greg gave was correct :) Here is the code required to redirect stderr as I wanted:

CreatePipe( &m_hReadPipe, &m_hWritePipe, NULL, 0 );
stderr->_file = _open_osfhandle( (long)m_hWritePipe, _O_TEXT);;
setvbuf( stderr, NULL, _IONBF, 0 );

Once you have your unbuffered output redirected to the pipe, you'll need another thread to read it, which can do so calling:

DWORD dwBytesRead = 0;
char cNextChar;
ReadFile( m_hReadPipe, &cNextChar, 1, &dwBytesRead, NULL );

Nice, Thanks a load for that Greg. The only problem I have now is that ReadFile is a blocking call, I really need something non-blocking that I can do the equivelent of "select()" on so that I can tell if there is data to read or not. I know you can do this with a named pipe but I really don't want such a heavy construct for internal use (for one thing how would I name it? what happens if two programs are using this same class etc..). Any pointers appreciated but I know its offtopic.

jkp
A: 

You mention that you don't want to use a named pipe for internal use; it's probably worth poining out that the documentation for CreatePipe() states, "Anonymous pipes are implemented using a named pipe with a unique name. Therefore, you can often pass a handle to an anonymous pipe to a function that requires a handle to a named pipe." So, I suggest that you just write a function that creates a similar pipe with the correct settings for async reading. I tend to use a GUID as a string (generated using CoCreateGUID() and StringFromIID()) to give me a unique name and then create the server and client ends of the named pipe with the correct settings for overlapped I/O (more details on this, and code, here: http://www.lenholgate.com/archives/000762.html).

Once I have that I wire up some code that I have to read a file using overlapped I/O with an I/O Completion Port and, well, then I just get async notifications of the data as it arrives... However, I've got a fair amount of well tested library code in there that makes it all happen...

It's probably possible to set up the named pipe and then just do an overlapped read with an event in your OVERLAPPED structure and check the event to see if data was available... I don't have any code available that does that though.

Len Holgate