How do i get the EXCEPTION_POINTERS
, i.e. both:
PEXCEPTION_RECORD
andPCONTEXT
data during an EExternal
exception?
Background
When Windows throws an exception, it passes a PEXCEPTION_POINTERS
; a pointer to the exception information:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
When Delphi throws me an EExternal
exception, it only contains half that information, the PEXCEPTION_RECORD
only:
EExternal = class(Exception)
public
ExceptionRecord: PExceptionRecord;
end;
How, during an EExternal
exception, do i get both?
Example Usage
i am trying to write a Minidump using MiniDumpWriteDump
function from Delphi.
The function has a few optional parameters:
function MiniDumpWriteDump(
hProcess: THandle; //A handle to the process for which the information is to be generated.
ProcessID: DWORD; //The identifier of the process for which the information is to be generated.
hFile: THandle; //A handle to the file in which the information is to be written.
DumpType: MINIDUMP_TYPE; //The type of information to be generated.
{in, optional}ExceptionParam: PMinidumpExceptionInformation; //A pointer to a MINIDUMP_EXCEPTION_INFORMATION structure describing the client exception that caused the minidump to be generated.
{in, optional}UserStreamParam: PMinidumpUserStreamInformation;
{in, optional}CallbackParam: PMinidumpCallbackInformation): Boolean;
At a basic level i can omit the three optional parameters:
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFileHandle,
nil, //PMinidumpExceptionInformation
nil,
nil);
and it succeeds. The downside is that the minidump is missing the exception information. That information is (optionally) passed using the 4th miniExceptionInfo parameter:
TMinidumpExceptionInformation = record
ThreadId: DWORD;
ExceptionPointers: PExceptionPointers;
ClientPointers: BOOL;
end;
PMinidumpExceptionInformation = ^TMinidumpExceptionInformation;
This is good, except i need a way to get at the EXCEPTION_POINTERS
that is supplied by Windows when an exception happens.
The TExceptionPointers
structure contains two members:
EXCEPTION_POINTERS = record
ExceptionRecord : PExceptionRecord;
ContextRecord : PContext;
end;
i know that Delphi's EExternal
exception is the base of all "Windows" exceptions, and it contains the needed PExceptionRecord
:
EExternal = class(Exception)
public
ExceptionRecord: PExceptionRecord;
end;
But it doesn't contain the associated ContextRecord
.
Isn't PEXCEPTION_RECORD
good enough?
If i try to pass the EXCEPTION_POINTERS
to MiniDumpWriteDump
, leaving ContextRecord
nil:
procedure TDataModule1.ApplicationEvents1Exception(Sender: TObject; E: Exception);
var
ei: TExceptionPointers;
begin
if (E is EExternal) then
begin
ei.ExceptionRecord := EExternal(E).ExceptionRecord;
ei.ContextRecord := nil;
GenerateDump(@ei);
end;
...
end;
function GenerateDump(exceptionInfo: PExceptionPointers): Boolean;
var
miniEI: TMinidumpExceptionInformation;
begin
...
miniEI.ThreadID := GetCurrentThreadID();
miniEI.ExceptionPointers := exceptionInfo;
miniEI.ClientPointers := True;
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFileHandle,
@miniEI, //PMinidumpExceptionInformation
nil,
nil);
end;
Then the function fails with error 0x8007021B
Only part of a ReadProcessMemory or WriteProcessMemory request was completed
What about SetUnhandledExceptionFilter
?
Why don't you just use
SetUnhandledExceptionFilter
and get the pointer you need?
SetUnhandledExceptionFilter(@DebugHelpExceptionFilter);
function DebugHelpExceptionFilter(const ExceptionInfo: TExceptionPointers): Longint; stdcall;
begin
GenerateDump(@ExceptionInfo);
Result := 1; //1 = EXCEPTION_EXECUTE_HANDLER
end;
Problem with that is that the unfiltered exception handler only kicks in if the exception is unfiltered. Because this is Delphi, and because because i handle the exception:
procedure DataModule1.ApplicationEvents1Exception(Sender: TObject; E: Exception);
var
ei: TExceptionPointers;
begin
if (E is EExternal) then
begin
//If it's EXCEPTION_IN_PAGE_ERROR then we have to terminate *now*
if EExternal(E).ExceptionRecord.ExceptionCode = EXCEPTION_IN_PAGE_ERROR then
begin
ExitProcess(1);
Exit;
end;
//Write minidump
...
end;
{$IFDEF SaveExceptionsToDatabase}
SaveExceptionToDatabase(Sender, E);
{$ENDIF}
{$IFDEF ShowExceptionForm}
ShowExceptionForm(Sender, E);
{$ENDIF}
end;
The application doesn't, nor do i want it to, terminate with a WER fault.
How do i get the EXCEPTION_POINTERS
during an EExternal
?
Note: You can ignore everything from Background on. It's unnecessarily filler designed to make me look smarter.
Pre-emptive snarky Heffernan comment: You should stop using Delphi 5.
Bonus Reading
- MSDN: Crash Dump Analysis (Windows)
(detailed example of how to call
MiniDumpWriteDump
) - CodeProject: Post-Mortem Debugging Your Application with Minidumps and Visual Studio .NET (General talk about the concepts, virtues, and how to generate and use minidumps)
- Stackoverflow: How to create minidump for my process when it crashes? (initial introduction to the world of mini dumps)
- Stackoverflow: Can one prevent Microsoft Error Reporting for a single app? (setting up the unfiltered handler in Delphi)