MADExcept and Eurekalog are good products (and there is a JVCL tool as well). If you run your app in the IDE, you get the stack there – but for now, you need to acquire a third party package to get it runtime.I don’t disagree with the wish for a basic call stack tool, that works cross platform, but it would affect third party developers.
Whats the problem? You attach handlers to Exception.GetExceptionStackInfoProc, GetStackInfoStringProc and ` and just call a function that grabs the map or td32 info and generates the callstack – if you don’t want to spend any money for a high quality tool like madExcept (can even use it for free for non commercial use!) then use JclDebug.pas
I edited in some URLs above; the actual info is from: Why Delphi (like other developer environments) natively not included full call stack for every exception… [WayBack] (which is because it would kill even more of the Delphi 3rd party market).
And it taught me about this by madshi (of MADExcept fame):
DebugEngine is a collection of utils related to debug stuff (stack trace, CPU registers snaphot, debug info,…). Basically, I started to write a commercial error log plugin for Delphi, then I noticed that my internal framework got bigger and bigger. So I decided to share it with the community in hope it will be useful.
And there is the JCL ExceptDlg.pas which is quite easy to use: just add it anywhere to your project and the global exception handler will show you a stack trace provided you have a .MAP file or .TDS file (which contains TD32 symbol information) in the same directory as your .EXE.
–jeroen
Example code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unit ExceptionHelperUnit; | |
interface | |
uses | |
System.SysUtils; | |
type | |
ExceptionHelper = class helper for Exception | |
public | |
function Describe: string; | |
class procedure RaiseNotImplementedException(const aClass: TClass; const aMethodName: string); | |
class function GetStackTrace: string; | |
end; | |
implementation | |
uses | |
System.RTLConsts, | |
System.SysConst; | |
type | |
EStackTraceException = class(Exception); // EProgrammerNotFound to make it really clear this is only to be used in very limited places ?? | |
{ ExceptionHelper } | |
function ExceptionHelper.Describe: string; | |
var | |
lStackTrace: string; | |
begin | |
Result := inherited ToString(); | |
if Self is EInOutError then | |
if Result = System.RTLConsts.SInvalidFileName then | |
Result := System.SysConst.SInvalidFileName; | |
if Assigned(StackInfo) then | |
lStackTrace := StackTrace | |
else | |
lStackTrace := 'empty'; | |
Result := Format('Exception'#13#10'%s at $%p: %s'#13#10'with StackTrace'#13#10'%s', [ClassName, ExceptAddr, Result, lStackTrace]); | |
end; | |
class function ExceptionHelper.GetStackTrace: string; | |
begin | |
try | |
Result := 'Get StackTrace via Exception.'; | |
raise EStackTraceException.Create(Result) at ReturnAddress; | |
except | |
on E: EStackTraceException do | |
Result := E.StackTrace; | |
end; | |
end; | |
class procedure ExceptionHelper.RaiseNotImplementedException(const aClass: TClass; const aMethodName: string); | |
begin | |
raise ENotImplemented.CreateFmt('Method %s.%s is not implemented.', [aClass.ClassName, aMethodName]); | |
end; | |
end. |