unit ExceptLog;// Delphi Exception Logger// http://www.jirihajek.net/delphi/ExceptLog.htm// (c) Jiri Hajek 2003-2005// E-mail: jh@jirihajek.net// Created for MediaMonkey (http://www.mediamonkey.com), a free mp3 jukebox player and music library organizer// Purpose: When you include this unit in your project, all exceptions that happens at run-time// are automatically logged together with exception message, information about source of the// exception and call stack content together with source files and line numbers information.// Environment:// This unit was tested under Delphi 7, it would probably work under other Delphi versions// as well, but it might need some modifications in some magic constants in the code below.// Usage:// 1. Include this unit in your project// 2. You'll also need an excellent leak detector for Delphi, MemCheck, from here:// http://v.mahon.free.fr/pro/freeware/memcheck/// (Exception Logger uses its methods to analyze callstack)// 3. Modify OutputFile constant below so that logs are written to a file you want.// 4. Recommended: Set your compiler options for debugging, this is described here:// http://v.mahon.free.fr/pro/freeware/memcheck/project_options.htm// It's a good idea to turn on "Project|Options|Linker|Include TD32 debug info"// because then you get complete reports about source files, procedure names and line numbers.// On the other hand, it makes your compiled exe file much bigger.// 5. Compile and run! You can distribute such compiled file even to your users and they can// directly report any problem to you. Any exception that happens is written to the OutputFile// as specified below. It is also reported using OutputDebugString() method and so it can// be trapped for example by Debug View application from System Internals.// License:// You can use this unit for any purpose, even commercial, as long as you leave this header here.// Author cannot be blamed for any problem with this unit. If you don't agree, don't use it.interfaceimplementationuses MemCheck, SysUtils, Windows;const OutputFile = 'C:/Except.log';type PExceptionRecord = ^TExceptionRecord; TExceptionRecord = record ExceptionCode : LongWord; ExceptionFlags : LongWord; OuterException : PExceptionRecord; ExceptionAddress : Pointer; NumberParameters : Longint; case {IsOsException:} Boolean of True: (ExceptionInformation : array [0..14] of Longint); False: (ExceptAddr: Pointer; ExceptObject: Pointer); end;var oldRTLUnwindProc: procedure; stdcall; writeToFile : boolean = false;procedure MyRtlUnwind; stdcall;var PER : PExceptionRecord; procedure DoIt; var // This is done in a sub-routine because string variable is used and we want it finalized s : string; E: Exception; CS: TCallStack; t : TextFile; begin s:='--------------------------------------------------------'#13#10; s:=s+'New exception:'#13#10; if PER^.ExceptionFlags and 1=1 then // This seems to be an indication of internal Delphi exception, begin // thus we can access 'Exception' class try E := Exception( PER^.ExceptObject); if (E is Exception) then s:=s+'Delphi exception, type '+E.ClassName+', message: '+E.Message+#13#10; except end; end; FillCallStack(CS, 5); // 5 last entries seem to be unusable s:=s+ 'Exception code: '+inttostr( PER^.ExceptionCode)+#13#10+ 'Exception flags: '+inttostr( PER^.ExceptionFlags)+#13#10+ 'Number of parameters: '+inttostr( PER^.NumberParameters)+#13#10+ TextualDebugInfoForAddress(Cardinal(PER^.ExceptionAddress))+#13#10+ CallStackTextualRepresentation(CS, '')+#13#10; OutputDebugString( PChar( s)); if writeToFile then begin try Assign( t, OutputFile); Append( t); Writeln( t, s); Close( t); except end; end; end;begin asm mov eax, dword ptr [EBP+8+13*4] // magic numbers - works for Delphi 7 mov PER, eax end; DoIt; asm mov esp, ebp pop ebp jmp oldRTLUnwindProc end;end;procedure InitExceptionLogging;var f : file;begin try Assign( f, OutputFile); Rewrite( f); Close( f); writeToFile := true; except writeToFile := false; end; oldRTLUnwindProc := RTLUnwindProc; RTLUnwindProc := @MyRtlUnwind;end;initialization InitExceptionLogging;end.