-
Notifications
You must be signed in to change notification settings - Fork 33
/
NtUiLib.Exceptions.pas
151 lines (126 loc) · 3.95 KB
/
NtUiLib.Exceptions.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
unit NtUiLib.Exceptions;
{
This module adds support for raising unsuccessful error codes as Delphi
exceptions.
}
interface
uses
NtUtils, System.SysUtils;
type
// An exception type thrown by RaiseOnError method of TNtxStatus
ENtError = class(EOSError)
private
xStatus: TNtxStatus;
public
constructor Create(const Status: TNtxStatus);
property NtxStatus: TNtxStatus read xStatus;
end;
// Make a TNtxStatus containing exception information
function CaptureExceptionToNtxStatus(E: Exception): TNtxStatus;
implementation
uses
Ntapi.ntstatus, NtUiLib.Errors, NtUtils.Ldr, NtUtils.DbgHelp;
{$BOOLEVAL OFF}
{$IFOPT R+}{$DEFINE R+}{$ENDIF}
{$IFOPT Q+}{$DEFINE Q+}{$ENDIF}
{ ENtError }
constructor ENtError.Create;
begin
xStatus := Status;
ErrorCode := Cardinal(Status.Win32Error);
Message := Status.ToString;
end;
// A callback for raising NT exceptions via Status.RaiseOnError;
procedure NtxUiLibExceptionRaiser(const Status: TNtxStatus);
begin
raise ENtError.Create(Status);
end;
{ Capturing }
function CaptureExceptionToNtxStatus;
begin
if E is ENtError then
Result := ENtError(E).NtxStatus
else
begin
Result.Location := E.ClassName;
Result.LastCall.Parameter := E.Message;
if E is EOSError then
Result.Win32Error := EOSError(E).ErrorCode
else if E is EAccessViolation then
Result.Status := STATUS_ACCESS_VIOLATION
else if E is EOutOfMemory then
Result.Status := STATUS_NO_MEMORY
else if (E is EArgumentException) or (E is EArgumentOutOfRangeException) or
(E is EArgumentNilException) then
Result.Status := STATUS_INVALID_PARAMETER
else if E is ENotSupportedException then
Result.Status := STATUS_NOT_SUPPORTED
else if E is ENotImplemented then
Result.Status := STATUS_NOT_IMPLEMENTED
else if (E is EAbort) or (E is EOperationCancelled) then
Result.Status := STATUS_CANCELLED
else if (E is EDirectoryNotFoundException) or (E is EFileNotFoundException)
or (E is EPathNotFoundException) then
Result.Status := STATUS_NOT_FOUND
else if E is EPathTooLongException then
Result.Status := STATUS_NAME_TOO_LONG
else if (E is EDivByZero) or (E is EZeroDivide) then
Result.Status := STATUS_FLOAT_DIVIDE_BY_ZERO
else if E is ERangeError then
Result.Status := STATUS_ARRAY_BOUNDS_EXCEEDED
else if E is EIntOverflow then
Result.Status := STATUS_INTEGER_OVERFLOW
else
Result.Status := STATUS_UNHANDLED_EXCEPTION;
end;
end;
{ Stack Trace Support }
// A callback for capturing the stack trace when an exception occurs
function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer;
var
Trace: TArray<Pointer> absolute Result;
i: Integer;
begin
// Clean-up before assigning
Result := nil;
// Capture the backtrace
Trace := RtlxCaptureStackTrace;
// Trim it by removing exception-handling frames
for i := 0 to High(Trace) do
if Trace[i] = P.ExceptionAddress then
begin
Delete(Trace, 0, i);
Break;
end;
end;
// A callback for representing the stack trace
function GetStackInfoStringProc(Info: Pointer): string;
var
Trace: TArray<Pointer> absolute Info;
Modules: TArray<TLdrxModuleInfo>;
Frames: TArray<String>;
i: Integer;
begin
if not LdrxEnumerateModuleInfo(Modules).IsSuccess then
Modules := nil;
SetLength(Frames, Length(Trace));
for i := 0 to High(Trace) do
Frames[i] := SymxFindBestMatch(Modules, Trace[i]).ToString;
Result := String.Join(#$D#$A, Frames);
end;
procedure CleanUpStackInfoProc(Info: Pointer);
var
Trace: TArray<Pointer> absolute Info;
begin
Finalize(Trace);
end;
initialization
TNtxStatus.NtxExceptionRaiser := NtxUiLibExceptionRaiser;
// Add support for exception stack-tracing
if not Assigned(@Exception.GetExceptionStackInfoProc) then
begin
Exception.GetExceptionStackInfoProc := GetExceptionStackInfoProc;
Exception.GetStackInfoStringProc := GetStackInfoStringProc;
Exception.CleanUpStackInfoProc := CleanUpStackInfoProc;
end;
end.