![]() |
Abfangen von Betriebssystemexceptions
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:
Innerhalb von ProcessException war nun folgendes möglich:
try {
// code } __except(ProcessException(GetExceptionInformation())){ } }
Code:
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.
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; } } 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 |
Re: Exceptionhandling wie in C++ möglich?
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:
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.
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; Grüße Daniel |
Re: Exceptionhandling wie in C++ möglich?
Liste der Anhänge anzeigen (Anzahl: 1)
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:
kann eine procedure aufgerufen werden.
procedure CallProcedure(TargetProcedure: TParameterlessProcedure;
ExceptionHandler : TExceptionHandlerProc); Es gibt zwei Parameter: - TargetProcedure ist der aufzurufende Code - ExceptionHandler ist der Exceptionhandler Beispiel für einen Aufruf:
Delphi-Quellcode:
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.
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; 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:
Falls jemanden interessiert, was derzeit intern abläuft:
Delphi-Quellcode:
Da ich das Interface des Exceptionhandlers übersetzt habe, wurde der ASM Code selbst ziemlich klein.
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; Für alle, die dieses Thema interessiert: ![]() |
Re: Abfangen von Betriebssystemexceptions
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:
Und zusätlich müßte man so dann sogar 'n parameterlose Funktion übergeben können.
procedure CallProcedure...
asm ... // Restore exception handler POP FS:[0] ADD ESP, 4 // POP &ExceptionHandler end; 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 ^^ |
Re: Abfangen von Betriebssystemexceptions
Liste der Anhänge anzeigen (Anzahl: 1)
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:
Grund ist, dass beim nichtbehandeln einer Exception das OS den Handler sonst ein zweites Mal aufruft. Mit obigem Test kann man das erkennen.
if (ExceptionInfo.ExceptionFlags and 2 <> 0) then
begin // This is a second time the handler is called Exit; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:49 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz