AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Abfangen von Betriebssystemexceptions
Thema durchsuchen
Ansicht
Themen-Optionen

Abfangen von Betriebssystemexceptions

Ein Thema von Lemmy1 · begonnen am 3. Nov 2005 · letzter Beitrag vom 5. Nov 2005
Antwort Antwort
Benutzerbild von Lemmy1
Lemmy1

Registriert seit: 28. Nov 2004
Ort: Ismaning
184 Beiträge
 
Delphi 2006 Professional
 
#1

Abfangen von Betriebssystemexceptions

  Alt 3. Nov 2005, 19:21
Hi zusammen,

ein Delphifrage wollte ich schon seit Jahren gerne einmal geklärt haben. In einem C++ (Microsoft C++ 6 und 7) Programm habe ich einmal folgendes getan:

Erstmal ein einfacher Exceptionhandler:
Code:
try {
  // code
  } __except(ProcessException(GetExceptionInformation())){
  }
}
Innerhalb von ProcessException war nun folgendes möglich:
Code:
int ProcessException(LPEXCEPTION_POINTERS lpEP)
  switch (lpEP->ExceptionRecord->ExceptionCode){
  case EXCEPTION_ACCESS_VIOLATION:
    // snip...
    // Unter lpEP->ExceptionRecord->ExceptionInformation sind diverse Infos verfügbar: Alle Register, den benutzen Speicher etc
    // snip...
    // Und damit gehts weiter
    return EXCEPTION_CONTINUE_EXECUTION;
  }
}
Sieht auf den ersten Blick ziemlich dumm aus, hat aber eine sehr konkrete Anwendung. Mit VirtualAlloc kann man sich vorher erstmal einen Bereich und dann einzelne Memoryareas sperren. Damit ist es möglich, writes auf bestimmte Memoryadressen zu erkennen, etwas zu tun und anschließend den Code weiter auszuführen als ich nichts gewesen wäre.

Meinen Nintendo64 Emulator musste ich seiner Zeit komplett von Delphi nach C++ umschreiben, weil ich aus Delphi diese Infos nicht rausbekommen habe. Durch diese Technik konnte ich die Performance teils um bis zu 50% (je nach game) steigern. Oder kennt da jemand einen Weg mit Asm/WinApi?

Grüße und Danke im Vorraus
Daniel
www.nemu.com - The N64 Emulator
  Mit Zitat antworten Zitat
Benutzerbild von Lemmy1
Lemmy1

Registriert seit: 28. Nov 2004
Ort: Ismaning
184 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Exceptionhandling wie in C++ möglich?

  Alt 3. Nov 2005, 19:59
Hab gerade selber etwas in der Delphipraxis zu einem ganz anderen Thema etwas gefunden. Bin mal so frei und kopier das einfach so raus:

Delphi-Quellcode:
function IsObject(AObject: Pointer): Boolean;
asm
      OR EAX,EAX // AObject == nil ??
      JNZ @@1
      RET

@@1: XOR EDX,EDX // install Exception Frame, SEH
      PUSH OFFSET @@3
      PUSH DWord Ptr FS:[EDX]
      MOV FS:[EDX],ESP
     
      MOV EAX,[EAX] // EAX := AObject^.ClassType
      OR EAX,EAX // ClassType == nil ??
      JZ @@2

      CMP EAX,[EAX].vmtSelfPtr // EAX = ClassType.vmtSelfPtr
      SETZ AL

@@2: POP DWord Ptr FS:[EDX]
      POP EDX
      RET

// Exception Handler, wird aufgerufen wenn zwischen @@1 und @@2 eine AV auftritt,
// zum Debugger muß auf @@3 ein Breakpoint gesetzt werden,
// Dieser SEH ist NICHT sichtbar für Delphi's Debugger !!

@@3: MOV EAX,[ESP + 00Ch] // context
      MOV DWord Ptr [EAX + 0B0h],0 // context.eax = 0
      MOV DWord Ptr [EAX + 0B8h],OFFSET @@2 // context.eip = @@2
      SUB EAX,EAX // 0 = ExceptionContinueExecution
end;
Also mit ASM scheints ja möglich zu sein. Werde mal versuchen, ob man sowas komfortabel und sauber kapseln kann. Wenn ja, lad ich den entsprechenden Code hoch.

Grüße
Daniel
Daniel
www.nemu.com - The N64 Emulator
  Mit Zitat antworten Zitat
Benutzerbild von Lemmy1
Lemmy1

Registriert seit: 28. Nov 2004
Ort: Ismaning
184 Beiträge
 
Delphi 2006 Professional
 
#3

Re: Exceptionhandling wie in C++ möglich?

  Alt 3. Nov 2005, 23:54
Na also es hat geklappt. Bin jetzt soweit, dass ich im Falle einer Exception den ContextRecord via Standard-Pascal routinen modifizieren kann. Ein Beispielprojekt habe ich angehängt. Da ich hoffe, dass das noch weitere Leute brauchen können, hier die Erklärung (Assemblerkenntnisse sind hier leider notwendig):

Über
Delphi-Quellcode:
procedure CallProcedure(TargetProcedure: TParameterlessProcedure;
  ExceptionHandler : TExceptionHandlerProc);
kann eine procedure aufgerufen werden.
Es gibt zwei Parameter:
- TargetProcedure ist der aufzurufende Code
- ExceptionHandler ist der Exceptionhandler

Beispiel für einen Aufruf:
Delphi-Quellcode:
var
  Dummy : Cardinal;

procedure Test; register;
begin
  asm
    // do something that crashs
    mov eax, 0
    mov eax, [eax]
  end;
end;

function ExceptionHandlerProc(
  var ExceptionInfo : Windows.TExceptionRecord;
  EstablisherFrame : Pointer;
  var ContextRecord : Windows.TContext;
  DispatchedContext : Pointer) : TExceptionContinue; cdecl;
begin
  ContextRecord.Eax := Cardinal(@Dummy);
  Result := ecContinueExecution;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    CallProcedure(Test, ExceptionHandlerProc);
    Caption := 'yes';
  except
    Caption := 'no';
  end;
end;
Was passiert hierbei? Beim Click auf Button1 wird der Funktion CallProcedure mitgeteilt, dass Sie "Test" ausführen soll. Im Falle eines Fehlers soll sie zu ExceptionHandlerProc springen. "Test" ist nun absichtlich so geschrieben, dass sie crasht. Aus diesem Grunde springt ExceptionHandlerProc ein. Sie behebt den Fehler (korrigiert das Register EAX) und sagt dem System, dass es an der gleichen Stelle weitermachen soll.

Man könnte in so einem Handler auch diverse andere Sachen tun:
- Loggen ohne den Programmfluss zu zerstören (allerdings sind die Daten im Handler dazu recht dürftig)
- EIP erhöhen, und damit die Assembler-Funktion überspringen
- Statt ecContinueExecution könnte man auch ecContinueSearch zurückgeben. In diesem Fall kann der Delphi-Exceptionhandler verwendet werden, um den Fehler zu verarbeiten.

Es gibt an dem Code noch zwei verbesserungswürdige Stellen:
  • Statt einer parameterlosen Procedure wäre es schöner, komplexere procedures aufzurufen. Mir fehlt da aber etwas die Idee, wie so etwas elegant geht, ohne 100 Überladungen zu bauen.
  • Wäre schön, wenn man TExceptionHandlerProc vereinfachen könnte. Die aktuelle Version wird direkt so vom Betriebssystem aufgerufen, womit ich keinen Einfluss auf die Signatur habe. Schöner wäre es also, innerhalb der Unit erst eine private Funktion aufzurufen, die das ganze etwas kapselt. Das würde durch Verwendung von Unit-Variablen funktionieren (man muss sich ja irgendwie den echten Handler merken), womit die Threadfähigkeit etwas im Eimer wäre. Alternativ könnte man es mit Codegenerierung zur Laufzeit versuchen, was allerdings immer mit Mühe verbunden ist.

Falls jemanden interessiert, was derzeit intern abläuft:
Delphi-Quellcode:
procedure CallProcedure(TargetProcedure: TEmptyProcedure;
  ExceptionHandler : TExceptionHandlerProc);
asm
  // Install Exception Frame
  PUSH ExceptionHandler
  PUSH FS:[0]
  MOV FS:[0], ESP

  // Call procedure
  CALL TargetProcedure

  // Restore exception handler
  MOV EAX, [ESP]
  MOV FS:[0], EAX
  ADD ESP, 8
end;
Da ich das Interface des Exceptionhandlers übersetzt habe, wurde der ASM Code selbst ziemlich klein.

Für alle, die dieses Thema interessiert: Hier gibt es einen sehr ausführlichen Crashcourse, der mir sehr geholfen hat.
Angehängte Dateien
Dateityp: zip exceptionhandling_395.zip (4,8 KB, 5x aufgerufen)
Daniel
www.nemu.com - The N64 Emulator
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.063 Beiträge
 
Delphi 12 Athens
 
#4

Re: Abfangen von Betriebssystemexceptions

  Alt 4. Nov 2005, 01:35
Gibt es einen bestimmten Grund, warum du den Wert für FS:[0] über EAX umleitest?
Wenn nicht, dann könnte es doch so aussehn ^^
Delphi-Quellcode:
procedure CallProcedure...
asm
  ...
  // Restore exception handler
  POP FS:[0]
  ADD ESP, 4 // POP &ExceptionHandler
end;
Und zusätlich müßte man so dann sogar 'n parameterlose Funktion übergeben können.

z.B.
Delphi-Quellcode:
Type TParameterlessFunction = Function; register;

Function CallFunction(TargetFunction: TParameterlessFunction;
  ExceptionHandler : TExceptionHandlerProc): Integer;
{der Rest ist gleich}


Und mal sehn was ich hiermit anfangen kann ^^
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
Benutzerbild von Lemmy1
Lemmy1

Registriert seit: 28. Nov 2004
Ort: Ismaning
184 Beiträge
 
Delphi 2006 Professional
 
#5

Re: Abfangen von Betriebssystemexceptions

  Alt 5. Nov 2005, 01:59
Yup das stimmt wohl.

Ist wohl generell sinnvoller, ein Cardinal hin- und eines zurückzuschicken. In der Win32 Welt bekommt man ja da mit Pointertricks alles reingequetscht

Hab das Beispielprojekt dementsprechend angepasst.

Noch eine Anmerkung, die ich vergessen habe: Innerhalb vom ExceptionHandlerProc sollte man nur dann etwas tun, wenn das zweite Bit in ExceptionFlags NICHT gesetzt ist:
Delphi-Quellcode:
  if (ExceptionInfo.ExceptionFlags and 2 <> 0) then
  begin
    // This is a second time the handler is called
    Exit;
  end;
Grund ist, dass beim nichtbehandeln einer Exception das OS den Handler sonst ein zweites Mal aufruft. Mit obigem Test kann man das erkennen.
Angehängte Dateien
Dateityp: zip exceptionhandling_107.zip (4,9 KB, 6x aufgerufen)
Daniel
www.nemu.com - The N64 Emulator
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:10 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz