In previous article “Writing Windows Debugger in C#” the whole debugger architecture for Windows OS was discuss. This post describe how to process output debug string event sent by debugged process. On debugger side output debug string event is triggered by OutputDebugString function call, which has 2 versions: ASCII version OutputDebugStringA and Unicode version OutputDebugStringW. In managed code several methods of System.Diagnostics.Debug and System.Diagnostics.Trace classes be used for this triggering.
Below the c# example which used unmanaged and managed calls to produce output debug string event on debugger side:
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace OutputDebugString
{
class OutputDebugString
{
[DllImport("kernel32.dll")]
public static extern void OutputDebugStringA(byte[] byteASCII);
[DllImport("kernel32.dll")]
public static extern void OutputDebugStringW(byte[] byteUnicode);
static void Main(string[] args)
{
  Console.WriteLine("Send message to debugger");
  byte[] byteUnicode = Encoding.Unicode.GetBytes("OutputDebugStringW called");
  OutputDebugStringW(byteUnicode);
  byte[] byteASCII = Encoding.ASCII.GetBytes("OutputDebugStringA called");
  OutputDebugStringA(byteASCII);
  Debug.Write("c# Debug.WriteLine");
  Trace.Write("c# Trace.WriteLine");
}
}
}
If this code is debugged in MS Visual studio output of OutputDebugString, Debug.Write, Trace.Write will be presented in Output windows. “Enable unmanaged code debugging” check box from Properties Debug page should be checked to see output from both managed and unmanaged functions.
How these output messages are processed by debugger:
When code is running under debugger and calling output debug message function the debugger receives an event OUTPUT_DEBUG_STRING_EVENT with appropriate information OUTPUT_DEBUG_STRING_INFO.
The OUTPUT_DEBUG_STRING_INFO structure looks like in C# definition:
public struct OUTPUT_DEBUG_STRING_INFO
{
public IntPtr lpDebugStringData;
public ushort fUnicode;
public ushort nDebugStringLength;
}
Where lpDebugStringData is address where the message string is located in debugged process address space, fUnicode specifies the message string format: 0 – ASCII and 1 – Unicode, nDebugStringLength is the length of message string. Again because the massage string is located in debugged process the debugger shoulc call ReadProcessMemory function.
The debugger code which gets debugging message from debugged code is presented below:
case PInvokes.OUTPUT_DEBUG_STRING_EVENT:
Console.WriteLine("OUTPUT_DEBUG_STRING_EVENT");
PInvokes.OUTPUT_DEBUG_STRING_INFO OutputDebugStringInfo = (PInvokes.OUTPUT_DEBUG_STRING_INFO)Marshal.PtrToStructure(debugInfoPtr, typeof(PInvokes.OUTPUT_DEBUG_STRING_INFO));
UInt64 lpNumberOfBytesRead = 0;
byte[] lpBuffer = new byte[OutputDebugStringInfo.nDebugStringLength];
if(PInvokes.ReadProcessMemory(hProcess, OutputDebugStringInfo.lpDebugStringData, lpBuffer, OutputDebugStringInfo.nDebugStringLength, ref lpNumberOfBytesRead))
{
string debugOutputString = "";
if (OutputDebugStringInfo.fUnicode == 0)
debugOutputString = Encoding.ASCII.GetString(lpBuffer);
else
debugOutputString = Encoding.Unicode.GetString(lpBuffer);
Console.WriteLine("OutputDebugString: " + debugOutputString);
}
break;
The full debugger C# code is here.
The image below shows how debugger output debug messages in command prompt windows: