mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
232 lines
6.1 KiB
ObjectPascal
232 lines
6.1 KiB
ObjectPascal
unit uExceptions;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils;
|
|
|
|
function ExceptionToString: String;
|
|
procedure WriteExceptionToFile(const aFileName: String; const ExceptionText: String = '');
|
|
procedure WriteExceptionToErrorFile(const ExceptionText: String = ''); inline;
|
|
procedure ShowExceptionDialog(const ExceptionText: String = '');
|
|
procedure ShowException(e: Exception);
|
|
{en
|
|
Log exception to file, show on console and show message dialog.
|
|
Can be called from other threads.
|
|
}
|
|
procedure HandleException(e: Exception; AThread: TThread = nil);
|
|
|
|
implementation
|
|
|
|
uses
|
|
Forms, Controls, Dialogs, LCLProc, LCLStrConsts, syncobjs,
|
|
uDebug, uLng, uGlobs, uDCVersion, DCOSUtils, LazUTF8, DCConvertEncoding;
|
|
|
|
type
|
|
THandleException = class
|
|
private
|
|
FHandleExceptionLock: TCriticalSection;
|
|
FHandleExceptionMessage: String;
|
|
FHandleExceptionBackTrace: String;
|
|
procedure ShowException;
|
|
public
|
|
constructor Create; reintroduce;
|
|
destructor Destroy; override;
|
|
procedure HandleException(e: Exception; AThread: TThread = nil);
|
|
end;
|
|
|
|
var
|
|
HandleExceptionObj: THandleException;
|
|
|
|
function ExceptionToString: String;
|
|
var
|
|
FrameCount: Integer;
|
|
FrameNumber: Integer;
|
|
Frames: PPointer;
|
|
begin
|
|
Result := 'Unhandled exception:';
|
|
|
|
if Assigned(ExceptObject) and (ExceptObject is Exception) then
|
|
begin
|
|
Result := Result + ' ' +
|
|
Exception(ExceptObject).ClassName + ': ' +
|
|
Exception(ExceptObject).Message;
|
|
end;
|
|
|
|
Result := Result + LineEnding +
|
|
' Stack trace:' + LineEnding +
|
|
BackTraceStrFunc(ExceptAddr) + LineEnding;
|
|
|
|
FrameCount := ExceptFrameCount;
|
|
Frames := ExceptFrames;
|
|
for FrameNumber := 0 to FrameCount - 1 do
|
|
Result := Result + BackTraceStrFunc(Frames[FrameNumber]) + LineEnding;
|
|
end;
|
|
|
|
procedure WriteExceptionToFile(const aFileName: String; const ExceptionText: String);
|
|
var
|
|
f: System.Text;
|
|
begin
|
|
if (aFileName <> EmptyStr) and not mbDirectoryExists(aFileName) then
|
|
begin
|
|
AssignFile(f, UTF8ToSys(aFileName));
|
|
{$PUSH}{$I-}
|
|
if not mbFileExists(aFileName) then
|
|
Rewrite(f)
|
|
else if mbFileAccess(aFileName, fmOpenWrite or fmShareDenyNone) then
|
|
Append(f);
|
|
{$POP}
|
|
if (TextRec(f).mode <> fmClosed) and (IOResult = 0) then
|
|
begin
|
|
WriteLn(f, '--------------- ',
|
|
FormatDateTime('dd-mm-yyyy, hh:nn:ss', SysUtils.Now),
|
|
' ---------------');
|
|
WriteLn(f, '| DC v', dcVersion, ' Rev. ', dcRevision,
|
|
' -- ', TargetCPU + '-' + TargetOS + '-' + TargetWS);
|
|
if WSVersion <> EmptyStr then
|
|
Write(f, '| ', OSVersion, ' -- ', WSVersion)
|
|
else
|
|
Write(f, '| ', OSVersion);
|
|
WriteLn(f, ' | PID ', GetProcessID);
|
|
|
|
if ExceptionText = EmptyStr then
|
|
begin
|
|
if Assigned(ExceptObject) and (ExceptObject is Exception) then
|
|
WriteLn(f, 'Unhandled exception: ',
|
|
Exception(ExceptObject).ClassName, ': ',
|
|
Exception(ExceptObject).Message)
|
|
else
|
|
WriteLn(f, 'Unhandled exception');
|
|
WriteLn(f, ' Stack trace:');
|
|
|
|
System.DumpExceptionBackTrace(f);
|
|
end
|
|
else
|
|
WriteLn(f, ExceptionText);
|
|
|
|
// Make one empty line.
|
|
WriteLn(f);
|
|
|
|
CloseFile(f);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure WriteExceptionToErrorFile(const ExceptionText: String = '');
|
|
begin
|
|
WriteExceptionToFile(gErrorFile, ExceptionText);
|
|
end;
|
|
|
|
procedure ShowExceptionDialog(const ExceptionText: String = '');
|
|
// Based on TApplication.ShowException.
|
|
var
|
|
Msg: string;
|
|
MsgResult: Integer;
|
|
begin
|
|
if AppNoExceptionMessages in Application.Flags then exit;
|
|
|
|
if ExceptionText = EmptyStr then
|
|
begin
|
|
if Assigned(ExceptObject) and (ExceptObject is Exception) then
|
|
Msg := Exception(ExceptObject).Message
|
|
else
|
|
Msg := '';
|
|
end
|
|
else
|
|
Msg := ExceptionText;
|
|
|
|
if FindInvalidUTF8Codepoint(PChar(Msg), Length(Msg), False) > 0 then
|
|
Msg := CeSysToUtf8(Msg);
|
|
if (Msg <> '') and (Msg[length(Msg)] = LineEnding) then Delete(Msg, Length(Msg), 1);
|
|
|
|
with Application do
|
|
if (not Terminated) and (Application <> nil) and (AppInitialized in Flags) then
|
|
begin
|
|
DisableIdleHandler;
|
|
try
|
|
MsgResult := MessageDlg(
|
|
Application.Title + ' - ' + rsMtError,
|
|
rsMtError + ':' + LineEnding + Msg + LineEnding + LineEnding +
|
|
Format(rsUnhandledExceptionMessage,
|
|
[LineEnding + gErrorFile + LineEnding + LineEnding,
|
|
StringReplace(rsMbIgnore, '&', '', [rfReplaceAll]),
|
|
StringReplace(rsMbAbort, '&', '', [rfReplaceAll])]),
|
|
mtError, [mbIgnore, mbAbort], 0, mbIgnore);
|
|
|
|
finally
|
|
EnableIdleHandler;
|
|
end;
|
|
if MsgResult = mrAbort then
|
|
begin
|
|
Flags := Flags + [AppNoExceptionMessages];
|
|
Halt;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure ShowException(e: Exception);
|
|
begin
|
|
MessageDlg(Application.Title, rsMsgLogError + LineEnding + e.Message, mtError, [mbOK], 0);
|
|
end;
|
|
|
|
procedure HandleException(e: Exception; AThread: TThread);
|
|
begin
|
|
HandleExceptionObj.HandleException(e, AThread);
|
|
end;
|
|
|
|
constructor THandleException.Create;
|
|
begin
|
|
FHandleExceptionLock := TCriticalSection.Create;
|
|
end;
|
|
|
|
destructor THandleException.Destroy;
|
|
begin
|
|
inherited;
|
|
FreeAndNil(FHandleExceptionLock);
|
|
end;
|
|
|
|
procedure THandleException.HandleException(e: Exception; AThread: TThread);
|
|
var
|
|
BackTrace: String;
|
|
begin
|
|
if MainThreadID = GetCurrentThreadId then
|
|
begin
|
|
BackTrace := ExceptionToString;
|
|
DCDebug(BackTrace);
|
|
WriteExceptionToErrorFile(BackTrace);
|
|
ShowExceptionDialog(e.Message);
|
|
end
|
|
else
|
|
begin
|
|
FHandleExceptionLock.Acquire;
|
|
try
|
|
FHandleExceptionMessage := e.Message;
|
|
FHandleExceptionBackTrace := ExceptionToString;
|
|
|
|
if FHandleExceptionBackTrace <> EmptyStr then
|
|
DCDebug(FHandleExceptionBackTrace);
|
|
|
|
TThread.Synchronize(AThread, @ShowException);
|
|
|
|
finally
|
|
FHandleExceptionLock.Release;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure THandleException.ShowException;
|
|
begin
|
|
WriteExceptionToErrorFile(FHandleExceptionBackTrace);
|
|
ShowExceptionDialog(FHandleExceptionMessage);
|
|
end;
|
|
|
|
initialization
|
|
HandleExceptionObj := THandleException.Create;
|
|
|
|
finalization
|
|
FreeAndNil(HandleExceptionObj);
|
|
|
|
end.
|
|
|