How can I ensure the get-and-set operation is atomic to redirect Console.Out to output the console log?

I need to intercept the console output stream to capture it for the log, but still pass things to the original stream so that the application runs correctly. This obviously means saving the original Console.OutTextWriter before modifying it with Console.SetOut(new MyTextWriterClass(originalOut)).

I assume that separate operations to get the Out property and call the SetOut () method are implemented in a Consolethread-safe way. But I would like to make sure that some other thread (for example, the code of the executable client application that I do not control and cannot expect to change it, and therefore I cannot rely on my own blocking scheme) cannot accidentally change it between my get and set and ultimately get a rewrite of my change to it (violation of their application behavior!). Since other code might just call SetOut (), my code should ideally get the exact same lock that is used internally Console(if any).

Unfortunately, it Consoleis a (static) class, not an instance, so you cannot just lock (Console). And, looking in the class documentation does not seem to mention locking. This is not the usually expected use of these Console methods, but there should be some safe way to do this as an atomic operation.

Bad standard blocking scheme, is there any other way to provide this? For such a short critical section (and executed only once), even instantly blocking all other threads may be acceptable if this is the only way to do this. We use C # and .NET2.0.

If this is not even possible (without breaking the client application), then we just need to rely on it, it is very unlikely that the client application would redirect the output of its console and this is between our get and set operations. I would just like on all grounds just in case.

Change . Now that we have a specific answer with sample code, I reformulated the title of the question to more broadly reflect the use cases in which the answers can help to be more understandable. In addition, a tag has been added for "atomic".

+3
source share
4 answers

SetOut, :

[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;
    }
}

, , - , InternalSyncObject, .

, , , . .

, , . , . defensivley , .

: -)

+2

, , , . ( ConsoleIntercepter, TextWriter):

private static object GetConsoleLockObject()
{
    object lockObject;
    try
    {
        const BindingFlags bindingFlags = BindingFlags.GetProperty |
            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
        // It 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();
    }
}

DoIntercepterRegistration() :

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

, Out Error; , , TextWriter , .

, ( .NET2.0) Console InternalSyncObject Out Error SetOut() SetError(), Out Error, . , , - ( ); - , .

+1

Main(), - , , - . .

Main() [STAThread], , .

0

In general, I would use a security model so that client code does not redirect the console until you look. I am sure that this requires Full Trust, so if the client code works with partial trust, he will not be able to call SetOut ().

0
source

All Articles