Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Auf Fehlerrückgabe prüfen (https://www.delphipraxis.net/96486-auf-fehlerrueckgabe-pruefen.html)

DelphiManiac 25. Jul 2007 09:28


Auf Fehlerrückgabe prüfen
 
Hallo,

ich habe ein Designproblem bei meiner Gestaltung meines Quelltextes:

Ich habe ein API Klasse zu Steuerung eines Gerätes entworfen, die kann u.a. folgendes:

Delphi-Quellcode:
TGeraet.GetUhrzeit(var uhrzeit:TUhrzeit):integer;
TGeraet.GetParamter(var paramter:Word):integer;
TGeraet.GetSeriennummer(var sn:string):integer;
die Methoden geben einen Fehlercode zurück:

0 -> kein Fehler
120 -> Timeout
130 -> Checksum Error...

Nun finde ich es irgendwie blöd jedes mal wenn ich eine Methode aufrufe
dieses zu machen

Delphi-Quellcode:
if (meinGeraet.GetSeriennummer(sn) <>0) then
begin
  // Fehlerbehandlung
end;
...
if (meinGeraet.GetUhrzeit(uz) <>0) then
begin
  // Fehlerbehandlung
end;
da ich sehr viele Kommunikationsanfragen habe und das ganze meinen Quelltext aufquellt :-(

Danke vielleicht wisst ihr ja eine vielleicht auch OOP-Design-Lösung

Gruß
DelphiManiac

SirThornberry 25. Jul 2007 09:47

Re: Auf Fehlerrückgabe prüfen
 
dann gib anstelle des Integers einen Bool zurück und den Feherlode als optionalen Parameter. Oder den Fehlercode im Bool (ein Bool kann schließlich nicht nur 1 und 0 sein) wobei der Bool dann genau das umgekehrte Ergebnis ist.

mit dem optionalen Fehlercode würde es so aussiehen
Delphi-Quellcode:
function TGeraet.GetUhrzeit(var uhrzeit:TUhrzeit; AResultCode: PInteger=nil):Boolean;
begin
  [...]
  if Assigned(AResultCode) then
    ResultCode^ := bisherigerRueckgabewert;
  result := bisherigerRueckgabewert = 0;
end;

sirius 25. Jul 2007 09:50

Re: Auf Fehlerrückgabe prüfen
 
Vielleicht hilft dir die Aufrufkonvention SAFECALL.

Dezipaitor 25. Jul 2007 09:59

Re: Auf Fehlerrückgabe prüfen
 
Du kannst ja auch Exceptions verwenden.

DelphiManiac 25. Jul 2007 12:27

Re: Auf Fehlerrückgabe prüfen
 
@sirius: Was mach den genau die Aufrufkonvention SAFECALL?

@Dezipaitor: wie genau würdest du sowas mit Exceptions lösen? würdest du eine Exception werfen?

Thx

sirius 25. Jul 2007 12:48

Re: Auf Fehlerrückgabe prüfen
 
Wenn du Exception werfen kannst, ist das doch das einfachste. Falls das nicht geht, dann arbeite mit SafeCall (ist ja auch das, was man u.U. bei Interfaces macht)

Ich dachte du hattest Exception schon ausgeschlossen, aber wenn nicht, dann nimm lieber diesen Weg.

Exceptions:
Delphi-Quellcode:
interface

type UhrzeitException=class(Exception);
//... und noch andere Exceptions


implementation


function TGeraet.GetUhrzeit:TUhrzeit;
begin
  //...
  if Fehler then raise UhrzeitException.create('wenn du magst, dann hier noch ein Text');
end;
Beim Aufrufen:
Delphi-Quellcode:

try
  xyz:=meingeraet.getuhrzeit;
  //...andere Abfragen

except
 on UhrzeitException do Uhrzeitfehler
 on ... do ...
 //...
 else raise;
end;

SirThornberry 25. Jul 2007 12:53

Re: Auf Fehlerrückgabe prüfen
 
@sirius: ich glaube der Fragesteller ist wie ich davon ausgegangen das du die CallingConvention SaveCall meintest. Und das würde ja keinen wirklichen Sinn machen?! Denn das würde ja nur bestimmen wie die Parameterübergabe erfolgt (in welcher Reihenfolge, ob über Register etc.).

DelphiManiac 25. Jul 2007 13:04

Re: Auf Fehlerrückgabe prüfen
 
@SirThornberry:

Ja genau, die Parameterübergabe ist hier ja schon geregelt, und da gibt es auch keine Vorschriften.
Mir ging es nur darum einen auftretenden Fehler zu melden und diesen abzuhandeln.

Und vorallendingen wollte ich nicht immer den Rückgabewert prüfen müssen bei jedem Funktionsaufruf...

Muss zugeben, ich nutze Exceptions viel zu selten, ist es denn hier generell sinvoll eine Exception zu werfen,

wie verhält sich denn das Programm, wenn ich in meiner Funktion erkenne, dass ein Fehler vorlag, und dann eine
Exception auslöse?

Gruß

sirius 25. Jul 2007 13:16

Re: Auf Fehlerrückgabe prüfen
 
@Sir: Das ist auch richtig. SAFECALL hat aber neben der Angabe über Parameterübergabe etc. auch noch einen weiteren Hintergrund. Eine SafeCall-Funktion hat immer zwei Rückgabewerte (eine Prozedur dementsprechend immer einen), zum einen das "echte Ergebnis", zum anderen einen Fehlercode.
Hintergrund ist der, dass man in der OOP ja keine Klassen kreuz und quer durch verschiedene Adressbereiche bzw. Speichermanager etc. schieben kann. Konkret (wobei ... jetzt bin ich mir nicht 100% sicher): Du kannst keine Klasse in einer Library erstellen und in deiner Main-Exe freigeben (ohne Sharemem etc.). Bei Exceptions (in OOP) würde genau das passieren. Beim Werfen der Exception wird die Klasse erstellt und hinterm nächsten except gekillt. -->CRASH

Dagegen gibts SafeCall: Du machst die Exceptionbehandlung innerhalb der Bibliothek und gibst neben dem eigentlichen Ergebnis auch einen Fehlercode mit zurück. Der Compiler prüft nach dem Aufruf einer SafeCall-Funktion automatisch den Fehlercode und ruft gegebenenfalls die Funktion in der Variablen SafeCallErrorProc auf (die normalerweise nil ist). Ausserdem wirft er erneut eine SafeCallException.
Das ist das besondere an SafeCall und entspricht so ziemlich dem was DelpiManiac als Funktionsköpfe hatte.
Also eine Funktion
Delphi-Quellcode:
function xyz(a,b,c,...:integer; var result:integer):TErrorCode; stdcall;
ist dasselbe wie
Delphi-Quellcode:
function xyz(a,b,c,...:integer):integer; safecall;
vom Header her (mit TErrorCode=integer)

So, und jetzt wirds dirty (vielleicht gehts auch einfacher, aber Delphi übergibt sonst immer denselben Fehlercode):
Delphi-Quellcode:
//Die Funktion in einer Bibliothek
function test(var erg:integer):cardinal;stdcall;
//hier allerdings lieber stdcall, weil wir wollen ja den ErrorCode selber setzen
const Error=$80000000; //Vorzeichenbit muss gesetzt sein für Fehler
begin
  //der eigentliche Funktionsinhalt
  erg:=-5;


 
  //wenn Fehler dann
  result:=120+error; //120 unser Fehlercode
  //sonst
  result:=0; //hauptsache nicht negativ
end;
Und im Hauptprogramm:
Delphi-Quellcode:
var ExceptionType:integer;

procedure SafecallError;
asm
  //die ersten beiden Zeilen nutzen wenn keine Exception geworfen werden soll
  //pop ecx
  //mov [esp],edx

  //Ergebnis in Exceptiontype schreiben
  and eax,$7FFFFFFF
  mov ExceptionType,eax
end;

procedure TForm1.Button1Click(Sender: TObject);
var mytest:function:integer;safecall;
begin
 
  mytest:=@test;
  try
    edit1.Text:=inttostr(mytest);
  except
    on ESafeCallException do
      edit1.text:=inttostr(Exceptiontype);
    else raise;
  end;
end;
Soweit, so gut. Und irgendwo vorher muss noch
Delphi-Quellcode:
safecallerrorProc:=@SafeCallError;
gesetzt werden.

War hier nur so ne Idee...die auch erstmal funktioniert :mrgreen:

Edit:
Wenn man in SafeCallError alle auskomentierten Zeilen so lässt, geht es auch ohne ASM:
Delphi-Quellcode:
procedure safecallerror(ErrorCode:integer;NonExceptionAddr:pointer);
begin
  ExceptionType:=Errorcode and $7FFFFFFF;
end;

sirius 25. Jul 2007 13:23

Re: Auf Fehlerrückgabe prüfen
 
Zitat:

Zitat von DelphiManiac
wie verhält sich denn das Programm, wenn ich in meiner Funktion erkenne, dass ein Fehler vorlag, und dann eine
Exception auslöse?

Bei "raise" wird die Exception ausgelöst und jetzt springt das Programm bis zum nächsten Except (auch in übergeordneten Funktionen) und macht dort ganz normal weiter. Wenn es so ein except nicht gibt, kommt die Fehlermeldung mit dem kreativen Text, den du eingegeben hast, auf dem Bildschirm.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:25 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