How to make DebugView work under .NET 4?
Asked Answered
T

4

29

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.

Titanium answered 13/12, 2010 at 13:28 Comment(5)
Indeed true. However, you will find the trace messages in Visual Studio's Output window.Viminal
The issue was reported to MS: connect.microsoft.com/VisualStudio/feedback/details/457063/… and their answer was that this is "By Design". If a workaround exists i'll love to know it. . .Hypochondriac
I am aware I can use Visual Studio's output. But it's not nearly as useful as DebugView. No filtering, the damn thing just keeps scrolling... I am amazed, as great tool as DebugView is, that no workaround seems to be available.Titanium
@Adrian Faciu, @kenny: See my answer for a possible workaround. Not quite that straight-forward, but when there is no other option than DebugView it should be okay. Otherwise I would suggest a combination of a TextWriterTraceListener and a good log viewer.Viminal
If I restart DebugView and use admin privileges, I can see Trace.WriteLine output in DebugView v4.78 on Win7 64bit.Hassock
V
24

.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");
    }
}
Viminal answered 14/12, 2010 at 13:15 Comment(1)
This is cool. Wish I could upvote it twice. I made a small optimization that skips all this if you're not running in the debugger and running DbgView. Just wrap the initialization code in something like: if (Debugger.IsAttached && Process.GetProcessesByName("Dbgview").Any())Duiker
T
17

Depending on your needs, there is a simpler workaround: just start your app without the debugger by using Ctrl-F5.

I had hoped to use DebugView to capture debug statements from a hosted Silverlight app that doesn't work in the debugger. Although this doesn't work as it did before .NET 4, launching my host without debugging does let the debugger statements through and they show up in DebugView.

Tittup answered 11/1, 2011 at 15:45 Comment(0)
F
6

This fixed the problem for me:

Trace.AutoFlush = true;
Fungiform answered 6/12, 2011 at 2:51 Comment(0)
T
2

I ran into this problem when I downgraded some projects from .NET 4.5 to .NET 4 - suddenly all my Debug View data disappeared (and I was directly PInvoking to ::OutputDebugString). Anyway, upgrade to late latest available version of Debug View (4.81) solved the problem.

Tampon answered 20/6, 2013 at 14:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.