Click here to Skip to main content
13,559,022 members
Click here to Skip to main content
Add your own
alternative version


43 bookmarked
Posted 4 Nov 2005
Licenced CPOL

Using the Debugger API to get trace messages from an executing application.

, 4 Nov 2005
Rate this:
Please Sign up or sign in to vote.
Trace.WriteLine outputs messages by OutputDebugString when no listeners are attached. This article shows how you can pick these messages up.


I was tooling around in the documentation pertaining to the TraceListener class and I found that "..where no trace listeners are attached, trace messages are output using the OutputDebugString API..."

This got me thinking - an application that can receive these OutputDebugString messages can allow you to view the trace messages of an application without having to stop and restart it to add a trace listener.

To receive this output you need to attach to the running process as a debugger and then whenever the process needs a trace message your debugger will receive an OUTPUT_DEBUG_STRING_EVENT event and you can get the string from that.

Writing a rudimentary debugger

There are a couple of API calls used in Windows NT for creating a debugger application:

DebugActiveProcess which attaches to the process to start debugging it:

<DllImport("kernel32", CallingConvention:=CallingConvention.Winapi, _
           EntryPoint:="DebugActiveProcess", _
           ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function DebugActiveProcess( <InAttribute()> _
              ByVal ProcessHandle As Int32) As Boolean

End Function

WaitForDebugEvent which pauses the caller until the process being debugged reaches a debug event:

<DllImport("kernel32", CallingConvention:=CallingConvention.Winapi, _
           EntryPoint:="WaitForDebugEvent", _
           ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function WaitForDebugEvent( _
           <OutAttribute(), MarshalAs(UnmanagedType.LPStruct)> _
           ByVal DebugEvent As DebugApi.Structures.DEBUG_EVENT, _
           <InAttribute()> ByVal dwMilliseconds As Int32) As Boolean

End Function

and ContinueDebugEvent which resumes the debugee:

<DllImport("kernel32", CallingConvention:=CallingConvention.Winapi, _
           EntryPoint:="ContinueDebugEvent", _
           ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function ContinueDebugEvent( _
           <InAttribute()> ByVal dwProcessId As Int32, _
           <InAttribute()> ByVal dwThreadId As Int32, _
           <InAttribute(), MarshalAs(UnmanagedType.U4)> _
           ByVal dwContinueStatus As DebugAPIConstatnts.DebugStates) As Boolean

End Function

To debug an application you need to attach to the process and then run a loop that waits for a debug event, handles the debug event and then resumes the debuggee.

Attaching to the process

The .NET Process class has a member Id which you pass to the DebugActiveProcess API to start debugging that process.

Waiting for a debug event

Calling the WaitForDebug event will block until a debug event occurs. When it does occur the details needed to handle it will be held in the structure DEBUG_EVENT passed back.

<StructLayout(LayoutKind.Sequential)> _
Public Class DEBUG_EVENT
    <MarshalAs(UnmanagedType.U4)> Public DebugEventCode _
      As DebugApi.DebugAPIConstatnts.DebugEventTypes
    Public ProcessId As Int32
    Public ThreadId As Int32
    Public lpDebugStringData As UInt32
    Public IsUnicode As Int16
    Public DebugStringLength As Int16
End Class

Handling the OUTPUT_DEBUG_STRING event

When we receive an OUTPUT_DEBUG_STRING event there are three properties that we need to retrieve the actual string: lpDebugStringDate is a pointer to the memory address of the string, IsUnicode is true if the string is in Unicode format and DebugStringLength tells you how long the string is...but there is a slight hitch: the address in lpDebugStringData is a memory address in the debuggee application's address space.

Getting a string from another application's memory

To get at the data in another application's memory we need to use the ReadProcessmemory API call:

<DllImport("kernel32", CallingConvention:=CallingConvention.Winapi, _
           EntryPoint:="ReadProcessMemory", _
           ExactSpelling:=True, SetLastError:=True)> _
Public Shared Function ReadProcessMemory( _
                   <InAttribute()> ByVal hProcess As Int32, _
                   <InAttribute()> ByVal lpBaseAddress As UInt32, _
                   <OutAttribute()> ByVal lpBuffer As IntPtr, _
                   <InAttribute()> ByVal nSize As Int32, _
                   <OutAttribute()> ByRef lpNumberOfBytesRead As UInt32 _
                            ) As Boolean

End Function

For a bit of OO design I have implemented this as a function in the DEBIG_EVENT class:

Public Function GetString(ByVal ProcessHandle As Int32) As String

    Dim sRet As String
    Dim hProcess As Int32

    If DebugStringLength.Equals(0) OrElse lpDebugStringData.Equals(0) Then
        sRet = ""

        hProcess = _
          False, Me.ProcessId)
        If hProcess <> 0 Then

            Dim bytesReturned As UInt32
            Dim lpBuffer As IntPtr
            'Allocate enough space to put the string into
            If Not IsUnicode Then
                lpBuffer = Marshal.AllocHGlobal(DebugStringLength)
                lpBuffer = Marshal.AllocHGlobal(DebugStringLength * 2)
            End If

            If DebugApiDeclarations.ReadProcessMemory(hProcess, _
                       lpDebugStringData, lpBuffer, _
                       DebugStringLength, bytesReturned) Then
                'Make this buffer into a string...
                If IsUnicode Then
                    sRet = Marshal.PtrToStringUni(lpBuffer, DebugStringLength)
                    sRet = Marshal.PtrToStringAnsi(lpBuffer, DebugStringLength)
                End If
                Call DebugApiDeclarations.CloseHandle(hProcess)
                If Not lpBuffer.Equals(IntPtr.Zero) Then
                End If
                Throw New Win32Exception
            End If
            Throw New Win32Exception
        End If

        End If
        Return sRet

End Function

And with that you have a basic debugger that can watch an application and take note of its trace messages...


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Duncan Edwards Jones
Software Developer (Senior)
Ireland Ireland
C# / SQL Server developer
Microsoft MVP (Azure) 2017
Microsoft MVP (Visual Basic) 2006, 2007

You may also be interested in...


Comments and Discussions

QuestionBug Finder Pin
Antonio Petricca7-Jun-13 0:01
memberAntonio Petricca7-Jun-13 0:01 
Question相当不错:) Pin
jamesghq7-Nov-12 2:37
memberjamesghq7-Nov-12 2:37 
GeneralDebugger API OpenProcess Pin
Vitoto3-Jan-06 17:27
memberVitoto3-Jan-06 17:27 
GeneralLOG WriteProcessMemory Pin
Vitoto1-Jan-06 9:53
memberVitoto1-Jan-06 9:53 
GeneralRe: LOG WriteProcessMemory Pin
Duncan Edwards Jones9-Jan-06 5:54
memberDuncan Edwards Jones9-Jan-06 5:54 
GeneralRe: LOG WriteProcessMemory Pin
Vitoto10-Jan-06 5:38
memberVitoto10-Jan-06 5:38 
QuestionDoble Debug ? Pin
Vitoto20-Dec-05 5:59
memberVitoto20-Dec-05 5:59 Find your IT ebooks for free! Pin
shihaibo16-Nov-05 22:22
membershihaibo16-Nov-05 22:22 
GeneralRe: Find your IT ebooks for free! Pin
Duncan Edwards Jones16-Nov-05 23:30
memberDuncan Edwards Jones16-Nov-05 23:30 
Generaldbgview Pin
Dave Cross8-Nov-05 5:47
memberDave Cross8-Nov-05 5:47 
GeneralRe: dbgview Pin
Duncan Edwards Jones8-Nov-05 6:04
memberDuncan Edwards Jones8-Nov-05 6:04 
GeneralRe: dbgview Pin
afisser9-Nov-05 1:43
memberafisser9-Nov-05 1:43 
GeneralGreat stuff Pin
Hal Angseesing8-Nov-05 4:03
memberHal Angseesing8-Nov-05 4:03 
GeneralStandard caveat when doing debugging Pin
Duncan Edwards Jones4-Nov-05 1:59
memberDuncan Edwards Jones4-Nov-05 1:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.180515.1 | Last Updated 4 Nov 2005
Article Copyright 2005 by Duncan Edwards Jones
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid