Come faccio ad avere il EXCEPTION_POINTERS
, vale a dire sia:Come ottenere EXCEPTION_POINTERS durante un'eccezione EExternal?
PEXCEPTION_RECORD
ePCONTEXT
dati durante un'eccezione EExternal
?
Sfondo
Quando Windows genera un'eccezione, si passa una PEXCEPTION_POINTERS
; un puntatore alle informazioni di eccezione:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
Quando Delphi mi genera un'eccezione EExternal
, contiene solo la metà che le informazioni, solo il PEXCEPTION_RECORD
:
EExternal = class(Exception)
public
ExceptionRecord: PExceptionRecord;
end;
come, durante un'eccezione EExternal
, fare ho entrambi?
Esempio di utilizzo
sto provando a scrivere un Minidump utilizzando MiniDumpWriteDump
funzione da Delphi.
la funzione ha un paio di parametri opzionali:
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;
a livello di base posso omettere i tre parametri opzionali:
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFileHandle,
nil, //PMinidumpExceptionInformation
nil,
nil);
e riesce. Lo svantaggio è che al minidump mancano le informazioni sulle eccezioni. Tale informazione è (opzionalmente) superato con il 4 ° miniExceptionInfo parametro:
TMinidumpExceptionInformation = record
ThreadId: DWORD;
ExceptionPointers: PExceptionPointers;
ClientPointers: BOOL;
end;
PMinidumpExceptionInformation = ^TMinidumpExceptionInformation;
Questo è un bene, tranne che ho bisogno di un modo per arrivare al EXCEPTION_POINTERS
che viene fornito da Windows quando un'eccezione accade.
La struttura TExceptionPointers
contiene due membri:
EXCEPTION_POINTERS = record
ExceptionRecord : PExceptionRecord;
ContextRecord : PContext;
end;
so che ad eccezione di Delphi EExternal
è la base di tutta la "Windows" eccezioni, e contiene la necessaria PExceptionRecord
:
EExternal = class(Exception)
public
ExceptionRecord: PExceptionRecord;
end;
Ma non contiene gli associati ContextRecord
.
Non è PEXCEPTION_RECORD
abbastanza buono?
se cerco di passare il EXCEPTION_POINTERS
-MiniDumpWriteDump
, lasciando 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;
Poi la funzione non riesce con l'errore 0x8007021B
Solo una parte di una richiesta ReadProcessMemory o WriteProcessMemory è stato completato
Che dire di SetUnhandledExceptionFilter
?
Perché non utilizzare solo lo
SetUnhandledExceptionFilter
e ottenere il puntatore necessario?
SetUnhandledExceptionFilter(@DebugHelpExceptionFilter);
function DebugHelpExceptionFilter(const ExceptionInfo: TExceptionPointers): Longint; stdcall;
begin
GenerateDump(@ExceptionInfo);
Result := 1; //1 = EXCEPTION_EXECUTE_HANDLER
end;
problema con questo è che il non filtrato gestore di eccezioni calci solo se l'eccezione è filtrato. Perché questo è Delphi, sia perché perché ho gestire l'eccezione:
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;
L'applicazione non si, né voglio a, terminare con un errore WER.
Come ottengo il EXCEPTION_POINTERS
durante un EExternal
?
Nota: È possibile ignorare tutto da Sfondo su. È inutilmente riempitivo progettato per farmi sembrare più intelligente.
Pre-emptive snarky Heffernan commento: Si dovrebbe smettere di usare Delphi 5.
Bonus lettura
- MSDN: Crash Dump Analysis (Windows) (esempio dettagliato di come chiamare
MiniDumpWriteDump
) - CodeProject: Post-Mortem Debugging Your Application with Minidumps and Visual Studio .NET (Generale parlare dei concetti, delle virtù e di come generare e utilizzare minidumps)
- Stackoverflow: How to create minidump for my process when it crashes? (introduzione iniziale al mondo delle mini discariche)
- Stackoverflow: Can one prevent Microsoft Error Reporting for a single app? (impostazione del gestore non filtrato a Delfi)
Post-emptive Heffernan commento: Dubito che è più facile nelle versioni successive. E non devi preoccuparti di x64. –
FWIW madExcept offre un facile accesso al record di contesto –
madExcept ottiene il contesto con mezzi piuttosto nefandi. Si aggancia ExceptObjProc. Il che risulta essere piuttosto complicato. Perché il processo di hooking ha bisogno di leggere alcuni registri prima di chiamare una funzione Pascal. Probabilmente potrei capire come farlo usando ME come modello. Ma ci vorrebbero secoli. Personalmente, userò semplicemente ME. Una volta che avremo ME, direi che non hai più bisogno di minidump. La diagnostica ME sarà più facile da usare. –