我如何获得EXCEPTION_POINTERS
,即两者:
PEXCEPTION_RECORD
和PCONTEXT
EExternal
异常期间的数据?
背景
当 Windows 抛出异常时,它会传递一个PEXCEPTION_POINTERS
; 指向异常信息的指针:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
当Delphi向我抛出EExternal
异常时,它只包含一半的信息,PEXCEPTION_RECORD
唯一的:
EExternal = class(Exception)
public
ExceptionRecord: PExceptionRecord;
end;
在EExternal
异常期间,我如何同时获得两者?
示例用法
我正在尝试使用MiniDumpWriteDump
Delphi 的函数编写一个 Minidump。
该函数有几个可选参数:
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;
在基本层面上,我可以省略三个可选参数:
MiniDumpWriteDump(
GetCurrentProcess(),
GetCurrentProcessId(),
hFileHandle,
nil, //PMinidumpExceptionInformation
nil,
nil);
它成功了。缺点是小型转储缺少异常信息。该信息(可选)使用第四个miniExceptionInfo参数传递:
TMinidumpExceptionInformation = record
ThreadId: DWORD;
ExceptionPointers: PExceptionPointers;
ClientPointers: BOOL;
end;
PMinidumpExceptionInformation = ^TMinidumpExceptionInformation;
这很好,除非我需要一种方法来在EXCEPTION_POINTERS
发生异常时获取 Windows 提供的信息。
该TExceptionPointers
结构包含两个成员:
EXCEPTION_POINTERS = record
ExceptionRecord : PExceptionRecord;
ContextRecord : PContext;
end;
我知道 Delphi 的EExternal
异常是所有“Windows”异常的基础,它包含所需的PExceptionRecord
:
EExternal = class(Exception)
public
ExceptionRecord: PExceptionRecord;
end;
但它不包含关联的ContextRecord
.
还PEXCEPTION_RECORD
不够好吗?
如果我尝试传递EXCEPTION_POINTERS
to MiniDumpWriteDump
,留下ContextRecord
零:
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;
然后函数失败并出现错误0x8007021B
仅完成了 ReadProcessMemory 或 WriteProcessMemory 请求的一部分
怎么样SetUnhandledExceptionFilter
?
你为什么不直接使用
SetUnhandledExceptionFilter
并获得你需要的指针呢?
SetUnhandledExceptionFilter(@DebugHelpExceptionFilter);
function DebugHelpExceptionFilter(const ExceptionInfo: TExceptionPointers): Longint; stdcall;
begin
GenerateDump(@ExceptionInfo);
Result := 1; //1 = EXCEPTION_EXECUTE_HANDLER
end;
问题在于未过滤的异常处理程序仅在异常未过滤时才会启动。因为这是 Delphi,而且因为我处理了异常:
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;
该应用程序不会,我也不希望它因 WER 故障而终止。
我如何EXCEPTION_POINTERS
在一个期间获得EExternal
?
注意:您可以忽略从背景开始的所有内容。它是不必要的填充物,旨在让我看起来更聪明。
先发制人的尖刻 Heffernan 评论:你应该停止使用 Delphi 5。
奖金阅读
- MSDN:崩溃转储分析(Windows)
(如何调用的详细示例
MiniDumpWriteDump
) - CodeProject:使用 Minidump 和 Visual Studio .NET 对应用程序进行事后调试 (一般性讨论概念、优点以及如何生成和使用 minidump)
- Stackoverflow:当我的进程崩溃时如何为它创建小型转储? (初步介绍迷你转储世界)
- Stackoverflow:可以防止单个应用程序的 Microsoft 错误报告吗? (在 Delphi 中设置未过滤的处理程序)