【发布时间】:2013-02-13 15:34:36
【问题描述】:
我如何获得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 异常期间同时获得两者?
示例用法
我正在尝试使用 Delphi 的 MiniDumpWriteDump 函数编写一个 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);
它成功了。缺点是小型转储缺少异常信息。该信息(可选)使用第 4 个 miniExceptionInfo 参数传递:
TMinidumpExceptionInformation = record
ThreadId: DWORD;
ExceptionPointers: PExceptionPointers;
ClientPointers: BOOL;
end;
PMinidumpExceptionInformation = ^TMinidumpExceptionInformation;
这很好,除非我需要一种方法来获取在发生异常时由 Windows 提供的 EXCEPTION_POINTERS。
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 传递给MiniDumpWriteDump,则留下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;
然后函数失败并出现错误0x8007021B
ReadProcessMemory 或 WriteProcessMemory 请求仅完成了一部分
SetUnhandledExceptionFilter 呢?
您为什么不直接使用
SetUnhandledExceptionFilter并获取您需要的指针?
SetUnhandledExceptionFilter(@DebugHelpExceptionFilter);
function DebugHelpExceptionFilter(const ExceptionInfo: TExceptionPointers): Longint; stdcall;
begin
GenerateDump(@ExceptionInfo);
Result := 1; //1 = EXCEPTION_EXECUTE_HANDLER
end;
问题在于 unfiltered 异常处理程序只有在异常未过滤时才会启动。因为这是 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 错误终止。
如何在EExternal 期间获得EXCEPTION_POINTERS?
注意:您可以忽略从背景开始的所有内容。这是不必要的填充物,旨在让我看起来更聪明。
先发制人的尖刻 Heffernan 评论:你应该停止使用 Delphi 5。
阅读奖励
-
MSDN: Crash Dump Analysis (Windows)
(如何拨打
MiniDumpWriteDump的详细示例) - CodeProject: Post-Mortem Debugging Your Application with Minidumps and Visual Studio .NET (关于概念、优点以及如何生成和使用小型转储的一般性讨论)
- Stackoverflow: How to create minidump for my process when it crashes? (初步介绍迷你转储世界)
- Stackoverflow: Can one prevent Microsoft Error Reporting for a single app? (在 Delphi 中设置未过滤的处理程序)
【问题讨论】:
-
Post-emptive Heffernan 评论:我怀疑在以后的版本中它会更容易。而且您不必担心 x64。
-
FWIW madExcept 让您可以轻松访问上下文记录
-
madExcept 通过相当邪恶的方式获取上下文。它钩住了ExceptionObjProc。事实证明这很棘手。因为挂钩过程需要在调用 Pascal 函数之前读取一些寄存器。我可能会想出如何使用 ME 作为模板来做到这一点。但这需要很长时间。就个人而言,我只会使用我。一旦你有了我,我猜你就不再需要小型转储了。 ME 诊断程序将更易于使用。
标签: delphi exception-handling delphi-5 minidump dbghelp