How to make DebugView work under .NET 4?

So Many Goblins picture So Many Goblins · Dec 13, 2010 · Viewed 15.6k times · Source

SysInternals' DebugView no longer works if used under .NET 4. Some research indicated that the new architecture of the framework did not allow for traces to be captured if a debugger was attached; in my case it's the Visual Studio debugger. Changing target framework from 4 to 3.5 makes it work again.

Anybody knows a way of getting DebugView to work under .NET 4 while having the Visual Studio debugger attached? I tried clearing the Listeners collection of the Trace class, but no luck.

Answer

Dirk Vollmar picture Dirk Vollmar · Dec 14, 2010

.NET trace messages are emitted using the OutputDebugString function in the Windows kernel. This function, as documented in MSDN,

sends a string to the debugger for display.

Obviously, a native debugger will receive this message. This is meant by the remark that this behavior is by design. The reason the messages were passed on to other listeners such as DebugView before .NET 4.0 is that Visual Studio did not debug .NET code as a "native" debugger; DebugView has never worked when a native debugger is attached.

A workaround could be to add a TraceListener that forwards all messages to another process that has no debugger attached. The communication could be realized using any IPC mechanism. The following is a sample using TCP sockets.


Server Application

This would be a simple stand-alone command line program that gets started and stopped automatically by the TraceListener class:

using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.WriteLine("Usage: DebugOutputListener.exe <port>");
            return;
        }
        TcpListener server = null;
        try
        {
            Int32 port = Convert.ToInt32(args[0]);
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");

            server = new TcpListener(localAddr, port);
            server.Start();

            while (true)
            {
                Console.Write("Waiting for a connection... ");

                using (TcpClient client = server.AcceptTcpClient())
                {
                    using (NetworkStream stream = client.GetStream())
                    {

                        byte[] bufferLength = new byte[4];
                        stream.Read(bufferLength, 0, 4);
                        int length = BitConverter.ToInt32(bufferLength, 0);

                        if (length == -1)
                        {
                            // close message received
                            Trace.WriteLine("DebugOutputListener is closing.");
                            return;
                        }

                        byte[] bufferMessage = new byte[length];
                        stream.Read(bufferMessage, 0, length);

                        string msg = Encoding.UTF8.GetString(bufferMessage);
                        Trace.WriteLine(msg);
                    }
                }
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine("SocketException: {0}", e);
        }
        finally
        {
            server.Stop();
        }
    }
}

TraceListener

using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class DebugOutputTraceListener : TraceListener
{
    private IPEndPoint ipEndPoint;
    private bool needsDisposing;

    public DebugOutputTraceListener(string debugOutputListenerPath, int port)
    {
        this.ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 13000);

        // start the process that forwards the trace messages 
        var psi = new ProcessStartInfo()
        {
            FileName = debugOutputListenerPath,
            Arguments = port.ToString(),
            CreateNoWindow = true,
            UseShellExecute = false
        };
        Process.Start(psi);
        needsDisposing = true;
    }

    ~DebugOutputTraceListener()
    {
        Dispose(false);
    }

    public override void Write(string message)
    {
        sendMessage(message);
    }

    public override void WriteLine(string message)
    {
        sendMessage(message + Environment.NewLine);
    }

    private void sendMessage(string message)
    {
        try
        {
            using (TcpClient client = new TcpClient())
            {
                client.Connect(ipEndPoint);
                byte[] bufferMessage = Encoding.UTF8.GetBytes(message);
                byte[] bufferLength = 
                    BitConverter.GetBytes(bufferMessage.Length);

                using (NetworkStream stream = client.GetStream())
                {
                    stream.Write(bufferLength, 0, bufferLength.Length);
                    stream.Write(bufferMessage, 0, bufferMessage.Length);
                }
            }
        }
        catch (SocketException e)
        {
            Trace.WriteLine(e.ToString());
        }
    }

    /// <summary>
    /// Sends -1 to close the TCP listener server.
    /// </summary>
    private void sendCloseMessage()
    {
        try
        {
            using (TcpClient client = new TcpClient())
            {
                client.Connect(ipEndPoint);
                byte[] buffer = BitConverter.GetBytes(-1);

                using (NetworkStream stream = client.GetStream())
                {
                    stream.Write(buffer, 0, buffer.Length);
                }
            }
        }
        catch (SocketException e)
        {
            Trace.WriteLine(e.ToString());
        }
    }

    public override void Close()
    {
        sendCloseMessage();
        needsDisposing = false;
        base.Close();
    }

    protected override void Dispose(bool disposing)
    {
        if (needsDisposing)
        {
            sendCloseMessage();
            needsDisposing = false;
        }
        base.Dispose(disposing);
    }
}

Usage

public class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        // using Debug; start a listener process on port 13000
        Debug.Listeners.Add(
            new DebugOutputTraceListener("DebugOutputListener.exe", 13000));
        Debug.WriteLine("A debug message.");

        // using Trace; start a listener process on port 13001
        Trace.Listeners.Add(
            new DebugOutputTraceListener("DebugOutputListener.exe", 13001));
        Trace.WriteLine("A trace message");
    }
}