@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
function xyz(a,b,c,...:integer; var result:integer):TErrorCode; stdcall;
ist dasselbe wie
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
safecallerrorProc:=@SafeCallError;
gesetzt werden.
War hier nur so ne Idee...die auch erstmal funktioniert
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;
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.