Monitor OutputDebugString() output.
Posted: Mon Jun 05, 2023 11:56 pm
I was working on a project for another forum member, and he didn't understand dbgview/outputdebugstring() - so I wrote this bit to do the monitoring of the outputdebugstring(); I also added it it to my syslog server as a monitoring option, since it was so simple.
I cheated a bit by using some pre-built modules; but here it is in case anyone else is curious or can use it for their own use.
(I have a data-sensitive project, I had an idea instead of using outputdebugstring("plain text") I'd encrypt the text the text so no one can see it unless they have my debug monitor -- just an idea of how it might be used)
(note: newer module-version below, I left this here so you can do a speed test to see how "doing stuff" in the receive (ie: i/o) slows down the calling application.)
Current version: See this post
While testing, one thing I noticed is outputdebugstring() barely slows down an application (openconsole()/PrintN() isn't much slower) UNLESS you rapid fire data, then it slows down a lot. However, if nothing is monitoring for data from outputdebugstring(), it doesn't slow the app down ...
I cheated a bit by using some pre-built modules; but here it is in case anyone else is curious or can use it for their own use.
(I have a data-sensitive project, I had an idea instead of using outputdebugstring("plain text") I'd encrypt the text the text so no one can see it unless they have my debug monitor -- just an idea of how it might be used)
(note: newer module-version below, I left this here so you can do a speed test to see how "doing stuff" in the receive (ie: i/o) slows down the calling application.)
Current version: See this post
Code: Select all
EnableExplicit
XIncludeFile "IPC_Stuffs.pbi" ;https://www.purebasic.fr/german/viewtopic.php?f=8&t=29238
; (because I was lazy to recreate two of the wheels)
;-------------------------------------------------------------------------------------------------------------------
; v3 - updated to optionally include process name rather than just PID
; note; the more time you spend 'doing stuff' with the data, the more it will slow down the app calling OutputDebugString()
; v2 - updated to better handle if another dbgview is already running.
#moreInfo = #True ; if you need all out speed, set this to #false
CompilerIf #PB_Compiler_Debugger
DebuggerWarning("You should run w/o debugger to improve speed for application calling outputdebugstring()")
CompilerEndIf
CompilerIf #MoreInfo
#SystemProcessInformation = $0005
Structure _UNICODE_STRING Align #PB_Structure_AlignC
usLength.w
usMaximumLength.w
usBuffer.i
EndStructure
Structure _SYSTEM_PROCESS_INFO Align #PB_Structure_AlignC
NextEntryOffset.l
NumberOfThreads.l
Reserved.q[3]
CreateTime.q
UserTime.q
KernelTime.q
ImageName._UNICODE_STRING
BasePriority.l
ProcessId.i
InheritedFromProcessId.i
EndStructure
Procedure.s processFromPID( pid.l )
Protected processName.s,
dwlen,
*systemProcessInformation._SYSTEM_PROCESS_INFO,
*buffer
If NtQuerySystemInformation_( #SystemProcessInformation, 0, 0, @dwlen ) And dwlen
dwlen * SizeOf(Character)
*Buffer = AllocateMemory(dwlen)
If *Buffer
If NtQuerySystemInformation_( #SystemProcessInformation, *Buffer, dwlen, @dwlen ) = #ERROR_SUCCESS
*systemProcessInformation = *Buffer
With *systemProcessInformation
While \NextEntryOffset
If \ImageName\usBuffer
If \ProcessId = pid
processName = PeekS( \ImageName\usBuffer, \ImageName\usLength )
Break
EndIf
EndIf
*systemProcessInformation + \NextEntryOffset
Wend
EndWith
EndIf
FreeMemory( *buffer )
EndIf
EndIf
ProcedureReturn processName
EndProcedure
CompilerEndIf
Procedure.s FormatMessage(Errorcode=-1)
Protected *Buffer, len, result.s
If ErrorCode = -1 : ErrorCode = GetLastError_() : EndIf
len = FormatMessage_( #FORMAT_MESSAGE_ALLOCATE_BUFFER|#FORMAT_MESSAGE_FROM_SYSTEM, 0, Errorcode, 0, @*Buffer, 0, 0)
If len
result = PeekS( *Buffer, len )
LocalFree_( *Buffer )
ProcedureReturn result
Else
ProcedureReturn "Errorcode: 0x" + Hex(Errorcode)
EndIf
EndProcedure
Macro output(a)
PrintN(a) : Debug a
EndMacro
Procedure openOrCreateEvent( *pName )
Protected handle
handle = OpenEvent_( #EVENT_ALL_ACCESS, #False, *pName )
If handle = 0
handle = CreateEvent_( #NUL, #False, #True, *pName )
EndIf
ProcedureReturn handle
EndProcedure
Structure dbwin_buffer
dwProcessID.l
Data.s{4096-SizeOf(long)} ; can't really use this, need ascii, so just for reference
EndStructure
Procedure MonitorDebugOutput( *control.long )
Protected hDBWinMutex,
*hDBMonBuffer.dbwin_buffer,
hEventBufferReady,
hEventDataReady
*hDBMonBuffer = IPC_Mem::Create( "DBWIN_BUFFER", SizeOf(dbwin_buffer) )
If *hDBMonBuffer
hDBWinMutex = IPC_Mutex::Create( "DBWinMutex" )
If hDBWinMutex
hEventBufferReady = openOrCreateEvent( @"DBWIN_BUFFER_READY" )
If hEventBufferReady = 0
;Debug FormatMessage()
*control\l = -1
Else
hEventDataReady = openOrCreateEvent( @"DBWIN_DATA_READY" )
If hEventDataReady = 0
;Debug FormatMessage()
*control\l = -2
Else
Debug "Monitor thread ready"
OpenConsole()
Repeat
If WaitForSingleObject_( hEventDataReady, 100 ) = #WAIT_OBJECT_0
With *hDBMonBuffer
If \dwProcessID
CompilerIf Defined(processFromPID, #PB_Procedure)
output( "[" + RSet( Str(\dwProcessID), 4, "0" ) + "] " + processFromPID(\dwProcessID) + ": " + PeekS(*hDBMonBuffer+SizeOf(long), -1, #PB_Ascii) ) ; nicer output
CompilerElse
output( "PID: " + Str(\dwProcessID) + ": " + PeekS(*hDBMonBuffer+SizeOf(long), -1, #PB_Ascii) ) ; speedier
CompilerEndIf
EndIf
EndWith
SetEvent_( hEventBufferReady )
EndIf
Until *control\l <> 0
CloseConsole()
CloseHandle_( hEventDataReady )
EndIf
CloseHandle_( hEventBufferReady )
Debug "Thread ending"
EndIf
IPC_Mutex::Free( hDBWinMutex )
EndIf
IPC_Mem::Free( *hDBMonBuffer )
EndIf
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
Define threadControl,
mdo
mdo = CreateThread( @MonitorDebugOutput(), @threadControl ) : ThreadPriority( mdo, #THREAD_PRIORITY_TIME_CRITICAL )
Delay( 1 ) ; just to give time for the thread to start
If threadControl = 0 ; if it's not 0, that probably means antoher program is monitoring for outputdebugstring()
OutputDebugString_( "self-test" ) ; but this could be sent from any application.
Delay( 3000 ) ; delay so you can see the console output
threadControl = 1 ; signal thread to exit
WaitThread( mdo )
Else
OutputDebugString_( "Sent to another dbgviewer" )
Debug "Already a dbgview running..."
EndIf
CompilerEndIf