Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   thiscall calling convention (https://www.delphipraxis.net/186229-thiscall-calling-convention.html)

Sunec 15. Aug 2015 18:44

Delphi-Version: 5

thiscall calling convention
 
Ich muss momentan nicht statische C++ Methoden aufrufen.

Dafür gibt es die thiscall calling convention.

Leider kennt Delphi diese nicht :cry:

Also muss man manuell nachhelfen und die Parameter auf den Stack pushen.
Delphi-Quellcode:
asm
    mov ecx, meinKlassenPointer
end;
Das klappt soweit und die Funktionen liefern den richtigen Wert (endlich).

Gibt es da einen besseren Weg das ganze zu realisieren?

Momentan rufe ich vor jedem Funktionsaufruf diesen asm block mit dem jeweiligen Klassenpointer auf. Kann ich das "automatisieren"?

EWeiss 15. Aug 2015 21:28

AW: thiscall calling convention
 
Mal ne Frage kannst du hier nicht mit Vtbl arbeiten anstelle von ASM?
Bin mir aber jetzt nicht sicher ob ich dich richtig verstanden habe.


gruss

Sunec 16. Aug 2015 03:50

AW: thiscall calling convention
 
Du meinst die Methoden als
Delphi-Quellcode:
virtual
zu kennzeichnen?

Hab ich leider auch schon vergeblich versucht. Die Funktion liefert ohne den asm "Schnipsel" den falschen Rückgabewert.

EWeiss 16. Aug 2015 10:46

AW: thiscall calling convention
 
Zitat:

Du meinst die Methoden als virtual zu kennzeichnen?
Nein Ich meine TDispatchableVtbl.

gruss

Sunec 16. Aug 2015 12:43

AW: thiscall calling convention
 
Zitat:

Zitat von EWeiss (Beitrag 1312283)
Zitat:

Du meinst die Methoden als virtual zu kennzeichnen?
Nein Ich meine TDispatchableVtbl.

gruss

Da ich davon noch nie gehört habe, hab ich mal das G von Alphabet befrage:
http://foren.activevb.de/cgi-bin/for...msg=404151&id=

Allerdings keine Ahnung wie mir das weiterhelfen soll :lol:

EWeiss 16. Aug 2015 13:17

AW: thiscall calling convention
 
Zitat:

Zitat von Sunec (Beitrag 1312305)
Zitat:

Zitat von EWeiss (Beitrag 1312283)
Zitat:

Du meinst die Methoden als virtual zu kennzeichnen?
Nein Ich meine TDispatchableVtbl.

gruss

Da ich davon noch nie gehört habe, hab ich mal das G von Alphabet befrage:
http://foren.activevb.de/cgi-bin/for...msg=404151&id=

Allerdings keine Ahnung wie mir das weiterhelfen soll :lol:

Du hast aber schon gelesen ?
stdcall; // Microsoft's thiscall (ecx = this)

und das du den Pointer deiner nicht statischen C++ Methode
hier geliefert bekommst?
AReturn : Pointer; // Pointer to buffer of return value

Von daher dachte ich das es dir vielleicht helfen könnte (andere Methode halt)
Wenn nicht gut dann habe ich wohl was falsch verstanden.
Kein Beinbruch meinerseits.

gruss

SMO 16. Aug 2015 14:04

AW: thiscall calling convention
 
Ich hatte auch mal damit zu tun und habe es folgendermaßen gelöst:

Delphi-Quellcode:
type
  PCppClass = ^TCppClass;
  PCppClassVtbl = ^TCppClassVtbl;
  TCppClassVtbl = packed record
    // These are actually Microsoft "thiscall" convention functions that pass a C++ object reference
    // in the ecx register. Delphi doesn't support this calling convention, but in its default
    // "register" calling convention, it passes the first three parameters in eax, edx, ecx, and
    // the rest on the stack. So, by prepending two integer dummies, we can abuse this to fake
    // a thiscall. However, thiscall passes parameters on the stack from right to left, whereas
    // Delphi register uses left to right. Therefore, the order of the parameters after "This"
    // has to be reversed!
    method0: function (Dummy0, Dummy1: Integer; This: PCppClass; hWin: HWND): Boolean;
    method1: function (Dummy0, Dummy1: Integer; This: PCppClass; Arg2, Arg1: Integer): Boolean;
    method2: function (Dummy0, Dummy1: Integer; This: PCppClass): Boolean;
    method3: function (Dummy0, Dummy1: Integer; This: PCppClass; Arg3: Pointer; Arg2, Arg1: Integer): Boolean;
  end;
  TCppClass = packed record
    Vtbl: PCppClassVtbl;
  end;
Erklärung:
Ich habe eine externe in C++ geschriebene DLL, die in einer Funktion eine C++ Klasse zurückliefert und zwar in Form eines Pointers (PCppClass).
Der Pointer zeigt auf eine Datenstruktur, die ihrerseits an erster Stelle einen Pointer auf die Virtual Method Table (Vtbl) hat.
Die Vtbl ist quasi ein Array mit Prozedur/Funktionszeigern (definiert in TCppClassVtbl), die alle die thiscall Aufrufkonvention haben.

Wie im englischen Kommentar beschrieben ist der Trick folgender:
Delphis normale Aufrufkonvention ist "register", d.h. die ersten 3 Parameter werden wenn möglich in den Registern eax, edx, ecx übergeben, in dieser Reihenfolge. Erst ab dem 4. Parameter wird der Stack benutzt. Dagegen benutzt "thiscall" nur ecx für die Klassen/Objektreferenz ("this" is C++, wie "Self" in Delphi) und den Stack für den Rest.
Wenn man also einen thiscall-Aufruf als register-Aufruf definiert, mit zwei initialen Dummy-Parametern, um eax und edx zu füllen, dann kann man auf diese Weise einen thiscall emulieren.
Ein weiterer Unterschied dabei ist, dass "thiscall" die Parameter von rechts nach links auf den Stack legt (wie bei "stdcall"), "register" allerdings von links nach rechts. Das muss man kompensieren, indem man in jeder Methode der Vtbl die Parameter nach "This" einfach in umgekehrter Reihenfolge deklariert im Vergleich zum C++ Original.

Delphi-Quellcode:
// Aufruf ungefähr so:
var
  P: PCppClass;

P := GetClass();
P^.Vtbl^.method0(0, 0, P, Form1.Handle); // die "^" kann man sich in neueren Delphi-Versionen auch sparen

EWeiss 16. Aug 2015 14:07

AW: thiscall calling convention
 
Ist doch quasi das gleiche wie bei mir.
So wie ich es vorgeschlagen habe.
Von dir jedoch um einiges besser beschrieben :thumb:

gruss

SMO 16. Aug 2015 14:10

AW: thiscall calling convention
 
Zitat:

Zitat von EWeiss (Beitrag 1312336)
Ist doch quasi das gleiche wie bei mir.

Ist es das? Sehe ich nicht so (stdcall vs. register), aber vielleicht stehe ich gerade auf dem Schlauch.

Zitat:

Zitat von EWeiss (Beitrag 1312336)
Von dir jedoch um einiges besser beschrieben :thumb:

Danke. :)

EWeiss 16. Aug 2015 14:17

AW: thiscall calling convention
 
Zitat:

Ist es das? Sehe ich nicht so, aber vielleicht stehe ich gerade auf dem Schlauch.
Du hast nur die Dispatch Funktion "link" den er gepostet hat gesehen.

Vielleicht hilft dir das auf die Sprünge.

Delphi-Quellcode:
function ApiService_Dispatch(AMessage: Integer; AReturn: Pointer;
  AParams: PPointerArray; AParamCount: Integer): Integer; stdcall;
Delphi-Quellcode:
  ApiServiceVtbl: TDispatchableVtbl = (Dispatch: ApiService_Dispatch);
  ApiService: TDispatchable = (Vtbl: Addr(ApiServiceVtbl));
Delphi-Quellcode:
Result := LRESULT(Addr(ApiService));


Aber egal ;)

gruss

SMO 16. Aug 2015 14:40

AW: thiscall calling convention
 
Ich sehe nicht, wie und wo bei diesem Vorgehen das "this" in ecx landet, da stdcall benutzt wird.

Es gibt übrigens durchaus C-Methoden, die das "this" nie benutzen und daher von Delphi aus per stdcall aufgerufen werden können. Aber im allgemeinen Fall geht das nicht und man muss irgendwie dafür sorgen, dass ecx den richtigen Inhalt bekommt.

EWeiss 16. Aug 2015 14:42

AW: thiscall calling convention
 
Zitat:

Zitat von SMO (Beitrag 1312346)
Ich sehe nicht, wie und wo bei diesem vorgehen das "this" in ecx landet, da stdcall benutzt wird.

Dann lese nochmal.. ;)

Delphi-Quellcode:
type
  PDispatchableVtbl = ^TDispatchableVtbl;
  TDispatchableVtbl = record
    Dispatch: function(
      AMessage  : Integer;       // Interpretation is class-dependend
      AReturn   : Pointer;       // Pointer to buffer of return value
      AParams   : PPointerArray; // Pointer to array of data pointers
      AParamCount: Integer        // Number of parameters in the array
      ): Integer;                 // Almost always TRUE(1) or FALSE(0)
      stdcall;                    // Microsoft's thiscall (ecx = this) <-----
  end;
Delphi-Quellcode:
  PDispatchable = ^TDispatchable;
  TDispatchable = record
    Vtbl: PDispatchableVtbl;
  end;
Delphi-Quellcode:
var
  This: PDispatchable;
Letztendlich wäre die frage ob es ihm was bringt da er quasi nur einen 1 Zeiler verwendet.
Wenn es denn geht warum nicht so belassen.

gruss

SMO 16. Aug 2015 14:49

AW: thiscall calling convention
 
Zitat:

Zitat von EWeiss (Beitrag 1312347)
Zitat:

Zitat von SMO (Beitrag 1312346)
Ich sehe nicht, wie und wo bei diesem vorgehen das "this" in ecx landet, da stdcall benutzt wird.

Dann lese nochmal.. ;)

Ich glaube, du verstehst nicht... das ist ein Kommentar, keine Zuweisung. :P
Bei stdcall landen alle Parameter auf dem Stack, nicht in Registern.

Dieses Codebeispiel ist unvollständig oder funktioniert nicht. Wie gesagt, falls diese "Dispatch" Methode "this" gar nicht braucht, wird es funktionieren, aber das ist nicht der Allgemeinfall.

EWeiss 16. Aug 2015 14:54

AW: thiscall calling convention
 
Zitat:

Dieses Codebeispiel ist unvollständig
Korrekt.. Sorry kann den gesamten Quelltext nicht offenlegen.. Closed source. (Sollte auch nur als Ansporn/Alternative dienen)

Zitat:

oder funktioniert nicht
Es funktioniert und zwar sehr zuverlässig ;)

Zitat:

falls diese "Dispatch" Methode "this" gar nicht braucht, wird es funktionieren, aber das ist nicht der Allgemeinfall.
Mag dahin gestellt sein. :)

gruss

SMO 16. Aug 2015 15:02

AW: thiscall calling convention
 
Zitat:

Zitat von EWeiss (Beitrag 1312349)
Korrekt.. Sorry kann den gesamten Quelltext nicht offenlegen.. Closed source. (Sollte auch nur als Ansporn/Alternative dienen)

Ist ja toll. Nur noch mal zur Erinnerung: Die Kernfrage von Sunec war, ob es einen besseren Weg gibt, die "this" Referenz eines thiscalls in ecx zu bekommen. Dein "Ansporn" hat da in keinster Weise geholfen, eine Lösung zu finden.
Wie es wirklich geht, habe ich gezeigt. (Ist natürlich ein Hack, aber "besser" als einen asm-Block vor jedem Methodenauruf finde ich ihn schon.)

EWeiss 16. Aug 2015 15:07

AW: thiscall calling convention
 
Das erste war ein Beispiel wo kein this verwendet wird.
Nun eins mit This die Basis ist die gleiche.

Und auch das funktioniert.

Delphi-Quellcode:
function LanguageService_Dispatch(AMessage: Integer; AReturn: Pointer;
  AParams: PPointerArray; AParamCount: Integer): Integer; stdcall;
Delphi-Quellcode:
function LanguageService_Dispatch(AMessage: Integer; AReturn: Pointer;
  AParams: PPointerArray; AParamCount: Integer): Integer; stdcall;

begin
  asm
        mov    This, ecx
  end;

  Result := Ord(False);

  if not Assigned(AReturn) then
    Exit;

  ///.... bla, bla
  case AMessage of
    WASERVICEFACTORY_GETINTERFACE:
          begin
            Pointer(AReturn^) := This;
            Result := Ord(True);
          end;
  end;

end;
und so weiter.
Zitat:

Ist natürlich ein Hack, aber "besser" als einen asm-Block vor jedem Methodenauruf finde ich ihn schon.)
Dieser befindet sich innerhalb meiner Funktion.

Es ging mir bei den letzten Antworten nur darum um dir zu zeigen das deine Methode sich von meiner nur wesentlich unterscheidet.
In Kombination mit und ohne This
Aber ich vermute mal das es ihm nicht viel bringt.

Wie gesagt die Winamp_APIService komplett zu veröffentlichen geht leider nicht
da steckt viel zeit und Hirnschmalz drin. Sorry

gruss

SMO 16. Aug 2015 16:18

AW: thiscall calling convention
 
Du hast also in Delphi etwas implementiert, das von einem Aufrufer (Winamp?) benutzt werden kann, der eigentlich eine C++ Klasse bzw. thiscall erwartet.
Das ist genau das Gegenteil von dem was Sunec möchte (selbst aufrufen, nicht aufgerufen werden). Aber das Prinzip ist natürlich gleich.

Und auch dein neues Beispiel könnte mit meinem Trick "schöner" gemacht werden (kein asm, keine globale "This" Variable):

Delphi-Quellcode:
function LanguageService_Dispatch(DummyEax, DummyEdx: Integer; This: PDispatchable; AParamCount: Integer;
AParams: PPointerArray; AReturn: Pointer; AMessage: Integer): Integer; // register;

Mein Punkt war jedenfalls, dass man Delphis register Aufrufkonvention problemlos für thiscall verwenden kann, unter folgenden Bedingungen:
  1. Man braucht am Anfang der Parameterliste zwei Dummys, um eax und edx zu befüllen (Werte sind egal).
  2. Der 3. Parameter ist "this" und landet wie erforderlich in ecx.
  3. Die restlichen Parameter landen auf dem Stack, aber da register den Stack von links nach rechts befüllt, thiscall aber von rechts nach links, muss die Reihenfolge der restlichen Parameter umgedreht werden!

Aus einem hypothetischen
"function TSomeClass.Foo(Arg1, Arg2, Arg3: Integer): Integer; thiscall;"
wird also
"function Foo(DummyEax, DummyEdx: Integer; This: PSomeClass; Arg3, Arg2, Arg1: Integer): Integer; register;"


Zitat:

Zitat von EWeiss (Beitrag 1312351)
Wie gesagt die Winamp_APIService komplett zu veröffentlichen geht leider nicht
da steckt viel zeit und Hirnschmalz drin. Sorry

Verstehe, ist auch nicht nötig. Sunecs Frage sollte damit beantwortet sein, es sei denn jemand kennt einen besseren Weg, um von Delphi aus eine C-Methode mit thiscall aufzurufen.

EWeiss 16. Aug 2015 16:26

AW: thiscall calling convention
 
Zitat:

Du hast also in Delphi etwas implementiert, das von einem Aufrufer (Winamp?) benutzt werden kann, der eigentlich eine C++ Klasse bzw. thiscall erwartet.
Es geht um eine beliebige Anwendung welche die Visualisierungs Plugins von Winamp verwenden will.
Winamp selbst hat damit nichts zu tun.
Zitat:

Aber das Prinzip ist natürlich gleich.
Dito ;)

Zitat:

Mein Punkt war jedenfalls, dass man auf asm verzichten kann, wenn man mit zwei "Dummy" Parametern leben kann (egal ob man sich Vtbl-Records deklariert oder nicht). Denn dann passt Delphis register Aufrufkonvention problemlos für thiscall.
:thumb:

Zitat:

Sunecs Frage sollte damit beantwortet sein, es sei denn jemand kennt einen besseren Weg, um von Delphi aus eine C-Methode mit thiscall aufzurufen.
Sehe ich genauso.. :wink:

gruss

Sunec 16. Aug 2015 20:30

AW: thiscall calling convention
 
Wow ich hätte nicht gedacht eine so ausführliche und plausible Erklärung zu bekommen :thumb:

Besten Dank!

Zitat:

Zitat von SMO (Beitrag 1312355)
Aus einem hypothetischen
"function TSomeClass.Foo(Arg1, Arg2, Arg3: Integer): Integer; thiscall;"
wird also
"function Foo(DummyEax, DummyEdx: Integer; This: PSomeClass; Arg3, Arg2, Arg1: Integer): Integer; register;"


Das klappt hervorragend und ich konnte nun alle Aufrufe problemlos ohne ASM (und mit umgestellten Parametern) aufrufen!

Spitze! :thumb:


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