2013-02-13 7 views
9

는 어떻게 EXCEPTION_POINTERS을받을 수 있나요, 즉 모두 : EExternal 예외 동안EExternal 예외가 발생하는 동안 EXCEPTION_POINTERS를받는 방법은 무엇입니까?

  • PEXCEPTION_RECORD
  • PCONTEXT

데이터? 윈도우에서 예외가 발생하면

는 배경

는 , 그것은 PEXCEPTION_POINTERS 통과

; 예외 정보에 대한 포인터 : 수행

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

방법 EExternal 예외 중 : 델파이이 나에게 EExternal 예외가 발생하면

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

, 그것은 절반 만 정보 만 PEXCEPTION_RECORD을 포함 나는 둘 다 얻습니까?

내가 델파이에서 MiniDumpWriteDump 기능을 사용하여 미니 덤프를 작성하는 것을 시도하고있다

사용 예.

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 매개 변수를 사용하여 전달 : 나는 예외가 발생하면 Windows에서 제공되는 EXCEPTION_POINTERS에서 얻을 수있는 방법이 필요 제외하고

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

이 좋다.

TExceptionPointers 구조는 두 멤버가 포함

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

내가 델파이의 EExternal 예외가 모든 "윈도우" 예외의 기본임을 알고는 PExceptionRecord 필요 포함

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

하지만 관련이 없습니다. ContextRecord.

PEXCEPTION_RECORD은 충분하지 않습니까?

나는 EXCEPTION_POINTERS 떠나 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; 

그런 기능이

가 ReadProcessMemory 또는 WriteProcessMemory의 요청

일부만이

완료되었습니다 0x8007021B 오류와 함께 실패를

SetUnhandledExceptionFilter?

SetUnhandledExceptionFilter을 사용하고 필요한 포인터를 가져 오지 않습니까? 그와

SetUnhandledExceptionFilter(@DebugHelpExceptionFilter); 

function DebugHelpExceptionFilter(const ExceptionInfo: TExceptionPointers): Longint; stdcall; 
begin 
    GenerateDump(@ExceptionInfo); 
    Result := 1; //1 = EXCEPTION_EXECUTE_HANDLER 
end; 

문제는 필터링되지 예외 핸들러만을 제외하고는 필터링되지 않은 경우에 차기이다. 이 델파이이고, 나는 예외 처리 때문에 때문에 때문에 :

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 중에 어떻게 받습니까?

참고 : 모두 배경을에서 무시할 수 있습니다. 나를 더 똑똑하게 보이도록 불필요하게 필러로 설계되었습니다.

선제 snarky 헤퍼 넌 코멘트 : 당신은

보너스 독서

+0

후 선제 헤퍼 넌 코멘트 : 염두에두고

, 여기가 델파이 2007에 대해 수행 할 수있는 방법의 예 나는 그것이 이후 버전에서 어떤 쉽게 의심. 그리고 x64에 대해 걱정할 필요가 없습니다. –

+0

FWIW madExcept를 사용하면 컨텍스트 레코드 –

+4

에 쉽게 액세스 할 수 있습니다. madExcept는 다소 사악한 방법으로 컨텍스트를 유지합니다. ExceptObjProc를 후크합니다. 꽤 까다로웠다. 후킹 프로세스는 파스칼 함수를 호출하기 전에 일부 레지스터를 읽어야하기 때문에. 아마도 ME를 템플릿으로 사용하는 방법을 알아낼 수 있습니다. 그러나 그것은 오래 걸릴 것입니다. 개인적으로, 나는 저를 사용하고 있습니다. 일단 당신이 나에게 당신은 더 이상 미니 덤프가 필요 없다고 생각합니다. ME 진단 프로그램은 사용하기가 더 쉬울 것입니다. –

답변

7

(필터링되지 않은 델파이 핸들러를 설정) 만 예외를 추출 포인터를 사용하고 시스템의 내부에서 그렇게하면 솔루션은 사용중인 델파이 버전과 다소 차이가납니다.

Delphi 5를 설치 한 이후로 시간이 지났지 만 Delphi 2007과 Delphi 5와 Delphi 2007의 개념은 거의 변하지 않았습니다.

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. 
+2

+1 잘 했어. 나는 ExceptObjProc를 얼마나 쉽게 잡아 당길 것인가를 알지 못했다. –

+0

우수! 그리고 Win32 나 RTL'GetExceptionContext'가 있었으면합니다. –

+2

@IanBoyd 예외 상황이 발생한 지점에서 컨텍스트를 캡처해야합니다. OS가 그 역할을 수행합니다. 그리고 그것을 당신에게 전달합니다. 그런 다음 RTL은이를 무시합니다. 그래서이 후킹 방식이 유일하게 실행 가능한 솔루션이라고 생각합니다. 내가 어렵다고 생각한 이유는 ME가 그 이상을한다는 것입니다. 'ExceptObjProc' 메카니즘을 후크하여 사용자가 여전히 그 메카니즘을 사용할 수 있도록합니다. 오히려 멋진. –