Question

I need to intercept the console output stream(s) in order to capture it for a log but still pass things through to the original stream so the application works properly. This obviously means storing the original Console.Out TextWriter before changing it with Console.SetOut(new MyTextWriterClass(originalOut)).

I assume the individual operations to get the Out property and to call the SetOut() method are implemented by Console in a thread-safe manner. But I'd like to make sure that some other thread (eg. running client application code that I don't control and can't expect to change, and so I can't rely on my own custom locking scheme) can't accidentally change it in between my get and set and end up getting overwritten by my change to it (breaking their application's behavior!). Since the other code may simply call SetOut(), my code should ideally get the same lock used internally by Console (assuming there is one).

Unfortunately, Console is a (static) class, not an instance, so you can't just lock (Console). And looking in the class documentation there does not seem to be any mention of locking. This is not the normally-expected usage of these Console methods, but there should be some safe way of doing this as an atomic operation.

Failing a standard locking scheme, is there some other way to ensure this? For such a short critical section (and done only once), even momentarily blocking all other threads might be acceptable, if that's the only way to do it. We're using C# and .NET2.0.

If not even that is possible (without disrupting the client application), then we'll just have to rely on it being very unlikely that the client application would redirect its console output and happen to do it in between our get and set operations. I'd just like to cover all the bases, just in case.

Edit: Now that we have a concrete answer with example code, I've reworded the question title to more generally reflect the use cases where the answer(s) can help, to be more clear. Also, added a tag for "atomic".

Was it helpful?

Solution

If you look at the implementation for SetOut it looks thread safe to me:

[HostProtection(SecurityAction.LinkDemand, UI=true)]
public static void SetOut(TextWriter newOut)
{
    if (newOut == null)
    {
        throw new ArgumentNullException("newOut");
    }
    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
    _wasOutRedirected = true;
    newOut = TextWriter.Synchronized(newOut);
    lock (InternalSyncObject)
    {
        _out = newOut;
    }
}

Edit

The cloest thing to a solution I could come up with is using reflection to get their InternalSyncObject and then lock on it.

A word of caution, this is an extremly bad idea and should only be used when no other option exists. You could cause the framework to behave unexpectadly and crash the process.

You will also need to pay attention to any service packs and major releases making sure the internal variable is still used. Since its internal there is no promise that it will be there in the next release. Write your code defensivley and try and degrade the user experience nicely should you not the object with reflection.

Good luck:-)

OTHER TIPS

Okay, now I've had some time to actually work up an implementation of the reflection approach Josh suggested. The basic code (in my ConsoleIntercepter class, which inherits from TextWriter) is:

private static object GetConsoleLockObject()
{
    object lockObject;
    try
    {
        const BindingFlags bindingFlags = BindingFlags.GetProperty |
            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
        // It's currently private, but we'd be happy if it were public, too.
        Type consoleType = typeof(Console);

        lockObject = consoleType.InvokeMember("InternalSyncObject", bindingFlags,
                                              null, null, null);
    }
    catch
    {
        lockObject = null; // Return null on any failure.
    }
    return lockObject;
}
public static void RegisterConsoleIntercepter()
{
    object lockObject = GetConsoleLockObject();
    if (lockObject != null)
    {
        // Great!  We can make sure any other changes happen before we read
        // or after we've written, making this an atomic replacement operation.
        lock (lockObject)
        {
            DoIntercepterRegistration();
        }
    }
    else
    {
        // Couldn't get the lock object, but we still need to work, so
        // just do it without an outer lock, and keep your fingers crossed.
        DoIntercepterRegistration();
    }
}

Then the DoIntercepterRegistration() would be something like:

private static void DoIntercepterRegistration()
{
    Console.SetOut(new ConsoleIntercepter(Console.Out));
    Console.SetError(new ConsoleIntercepter(Console.Error));
}

That's just for the lock-protected atomic replacement of Out and Error; obviously the actual interception, handling, and passing on to the previous TextWriters requires additional code, but that's not part of this question.

Note that (in the source code provided for .NET2.0) the Console class uses InternalSyncObject to protect initialization of Out and Error and to protect SetOut() and SetError(), but it does not use the lock around reads of Out and Error once they have been previously initialized. This is sufficient for my particular case, but could have atomicitiy violations if you did something more complex (and crazy); I can't think of any useful scenarios with that problem, but deliberately pathological ones can be imagined.

If you can do this early in Main() you'll have a much better chance of avoiding any race conditions, especially if you can do it before something creates a worker thread. Or in the static constructor of some class.

If Main() is marked with the [STAThread] attribute it will be running in a single threaded apartment so that should help too.

In general, I would use the security model to keep client code from redirecting the Console while you're not looking. I'm sure that requires Full Trust, so if client code is running with Partial trust, it won't be able to call SetOut().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top