9

どうすればEXCEPTION_POINTERS、つまり両方を取得できますか?

  • PEXCEPTION_RECORD
  • PCONTEXT

EExternal例外中のデータ?

バックグラウンド

Windowsが例外をスローすると、PEXCEPTION_POINTERS;が渡されます。例外情報へのポインタ:

typedef struct _EXCEPTION_POINTERS {
   PEXCEPTION_RECORD ExceptionRecord;
   PCONTEXT          ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

DelphiEExternalが例外をスローすると、その情報の半分しか含まれていませんPEXCEPTION_RECORD

EExternal = class(Exception)
public
  ExceptionRecord: PExceptionRecord;
end;

例外の間に、どのようにしEExternalて両方を取得しますか?

使用例

MiniDumpWriteDumpDelphiの関数を使用してミニダンプを作成しようとしています。

この関数には、いくつかのオプションのパラメーターがあります。

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;

基本的なレベルでは、3つのオプションのパラメーターを省略できます。

MiniDumpWriteDump(
    GetCurrentProcess(), 
    GetCurrentProcessId(),
    hFileHandle,
    nil,  //PMinidumpExceptionInformation
    nil,
    nil);

そしてそれは成功します。欠点は、ミニダンプに例外情報がないことです。その情報は(オプションで)4番目のminiExceptionInfoパラメーターを使用して渡されます。

TMinidumpExceptionInformation = record
    ThreadId: DWORD;
    ExceptionPointers: PExceptionPointers;
    ClientPointers: BOOL;
end;
PMinidumpExceptionInformation = ^TMinidumpExceptionInformation;

これは良いEXCEPTION_POINTERSことですが、例外が発生したときにWindowsによって提供されるを取得する方法が必要です。

TExceptionPointers構造には2つのメンバーが含まれます。

EXCEPTION_POINTERS = record
   ExceptionRecord : PExceptionRecord;
   ContextRecord : PContext;
end;

EExternalDelphiの例外がすべての「Windows」例外のベースであり、必要なものが含まれていることを私は知っていますPExceptionRecord

EExternal = class(Exception)
public
  ExceptionRecord: PExceptionRecord;
end;

ただし、関連付けられたは含まれていませんContextRecord

PEXCEPTION_RECORD十分ではありませんか?

に渡そうとするEXCEPTION_POINTERSと、 nilMiniDumpWriteDumpのままになります。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のコメント:Delphi5の使用をやめるべきです。

ボーナスリーディング

4

1 に答える 1

7

Delphi RTLは、コンテキストポインターを直接公開せず、例外ポインターを抽出するだけであり、Systemの内部で公開するため、ソリューションは、使用しているDelphiのバージョンにいくらか固有のものになります。

Delphi 5をインストールしてからしばらく経ちますが、Delphi 2007を使用しており、Delphi5とDelphi2007の概念は、これまでほとんど変わっていないと思います。

これを念頭に置いて、Delphi2007で実行する方法の例を次に示します。

program Sample;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils;


var
  SaveGetExceptionObject : function(P: PExceptionRecord):Exception;

// we show just the content of the general purpose registers in this example
procedure DumpContext(Context: PContext);
begin
  writeln('eip:', IntToHex(Context.Eip, 8));
  writeln('eax:', IntToHex(Context.Eax, 8));
  writeln('ebx:', IntToHex(Context.Ebx, 8));
  writeln('ecx:', IntToHex(Context.Ecx, 8));
  writeln('edx:', IntToHex(Context.Edx, 8));
  writeln('esi:', IntToHex(Context.Esi, 8));
  writeln('edi:', IntToHex(Context.Edi, 8));
  writeln('ebp:', IntToHex(Context.Ebp, 8));
  writeln('esp:', IntToHex(Context.Esp, 8));
end;

// Below, we redirect the ExceptObjProc ptr to point to here
// When control reaches here we locate the context ptr on
// stack, call the dump procedure, and then call the original ptr
function HookGetExceptionObject(P: PExceptionRecord):Exception;
var
  Context: PContext;
begin
  asm
    // This +44 value is likely to differ on a Delphi 5 setup, but probably
    // not by a lot. To figure out what value you should use, set a
    // break-point here, then look in the stack in the CPU window for the
    // P argument value on stack, and the Context pointer should be 8 bytes
    // (2 entries) above that on stack.
    // Note also that the 44 is sensitive to compiler switches, calling
    // conventions, and so on.
    mov eax, [esp+44]
    mov Context, eax
  end;
  DumpContext(Context);
  Result := SaveGetExceptionObject(P);
end;

var
  dvd, dvs, res: double; // used to force a div-by-zero error
begin
  dvd := 1; dvs := 0;
  SaveGetExceptionObject := ExceptObjProc;
  ExceptObjProc := @HookGetExceptionObject;
  try
    asm
      // this is just for register context verification
      // - don't do this in production
      mov esi, $BADF00D5;
    end;
    // cause a crash
    res := dvd / dvs;
    writeln(res);
  except
    on E:Exception do begin
      Writeln(E.Classname, ': ', E.Message);
      Readln;
    end;
  end;
end.
于 2013-02-13T21:39:36.863 に答える