Autor Beitrag
Allesquarks
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: Do 08.02.07 15:27 
virtuelle Funktionen kann man aus asm ja ungefähr so aufrufen.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
asm
mov eax,myobject;
mov eax,[eax];
call dword ptr [eax+VMTOFFSET(meineMethode)];
end;


Wie geht deas denn nun mit Interfaces? geht das auch über die VMT oder diesmal über DMTIndex oder gar nicht?
Im disassemblat sieht es ja so aus wie ich finde. Wichtig wäre auch noch, wie ich hier (unter assembler) zwischen verschiedenen Interfaces unterscheide.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Do 08.02.07 23:09 
Kannst Du bitte mal kurz folgenden Code compilieren und dann das Disamblat von Procedure Run geben?

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
type 
    IA = Interface 
        Procedure A;
    end;
    IB = Interface 
        Procedure B;
    end;

    TTestClass = class(TObject, IA, IB)
        Procedure A;
        Procedure B;
    end;

var
    X: TTestClass;

Procedure Run;
var
    XA: IA;
    XB: IB;
Begin
    XA := X;
    XA.A;

    XB := X;
    XB.B;
end;


Bitte im Disamly die Source-Zeilen mit anfügen.

P.S.: Hab leider grad kein Delphi installiert, daher die Frage nach dem Disambly. IMHO sollte es aber über CALL EAX.IA.A und CALL EAX.IB.B gehen ...

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Allesquarks Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: Fr 09.02.07 18:10 
Ich hab mal mein eigenes Testprogram genommen das ist aber fast identisch
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
 type myinterface1 = interface
   function test:integer;
 end;

 type myinterface2 = interface
   function test:integer;
 end;

 type obj1 = class(TInterfacedobject,myinterface1,myinterface2)
   function myinterface1.test=test1;
   function myinterface2.test=test2;

   function test1:integer;
   function test2:integer;
 end;

 type obj2 = class(TInterfacedobject,myinterface1,myinterface2)
   function myinterface1.test=test1;
   function myinterface2.test=test2;

   function test1:integer;
   function test2:integer;
 end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function obj1.test1:integer;
begin
  result:=1;
end;

function obj1.test2:integer;
begin
  result:=2;
end;

function obj2.test1:integer;
begin
  result:=3;
end;

function obj2.test2:integer;
begin
  result:=4;
end;

procedure TForm1.Button1Click(Sender: TObject);
var a,b,c,d:integer;var1,var2:obj1;var3,var4:myinterface2;
begin
 var1:=obj1.create;
 var2:=obj1.create;
 var3:=obj1.create;
 var4:=obj2.create;

 a:=myinterface1(var1).test;
 //a:=var1.test1;
 b:=myinterface2(var1).test;
 c:=var3.test;
 d:=var4.test;

 Edit1.text:=inttostr(a)+
             inttostr(b)+
             inttostr(c)+
             inttostr(d);
end;


Hier der CPU Auszug
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
Unit1.pas.78: a:=myinterface1(var1).test;
00454E77 8BC3             mov eax,ebx
00454E79 85C0             test eax,eax
00454E7B 7403             jz $00454e80
00454E7D 83E8F0           sub eax,<span style="color: red">-$10</span>//ich nehme mal an das ist der Offset vom Interface
00454E80 8B10             mov edx,[eax]
00454E82 FF520C           call dword ptr [edx+<span style="color: red">$0c</span>]//Hier Offset der Methode
00454E85 8BF8             mov edi,eax
Unit1.pas.80: b:=myinterface2(var1).test;
00454E87 8BC3             mov eax,ebx
00454E89 85C0             test eax,eax
00454E8B 7403             jz $00454e90
00454E8D 83E8F4           sub eax,<span style="color: red">-$0c</span>//Offset vom anderen Interface
00454E90 8B10             mov edx,[eax]
00454E92 FF520C           call dword ptr [edx+<span style="color: red">$0c</span>]//Hier Offset der Methode
00454E95 8BD8             mov ebx,eax
Unit1.pas.81: c:=var3.test;
00454E97 8B45FC           mov eax,[ebp-$04]
00454E9A 8B10             mov edx,[eax]
00454E9C FF520C           call dword ptr [edx+$0c]
00454E9F 8945F4           mov [ebp-$0c],eax
Unit1.pas.82: d:=var4.test;
00454EA2 8B45F8           mov eax,[ebp-$08]
00454EA5 8B10             mov edx,[eax]
00454EA7 FF520C           call dword ptr [edx+$0c]
00454EAA 8945F0           mov [ebp-$10],eax

Das Problem ist ja nur wie man an diese Offsets rankommt. Denn jetzt einfach immer +$0C zu schreiben ist ja ganz schön riskant wenn der Compiler sich mal entschließen sollte die Tabelle umzugruppieren dann krachts. Gleiches gilt natürlich für die Offsets wo die Interfaces stehen
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Fr 09.02.07 21:54 
user profile iconAllesquarks hat folgendes geschrieben:
Ich hab mal mein eigenes Testprogram genommen das ist aber fast identisch
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
 type myinterface1 = interface
   function test:integer;
 end;

 type myinterface2 = interface
   function test:integer;
 end;

 type obj1 = class(TInterfacedobject,myinterface1,myinterface2)
   function myinterface1.test=test1;
   function myinterface2.test=test2;

   function test1:integer;
   function test2:integer;
 end;

 type obj2 = class(TInterfacedobject,myinterface1,myinterface2)
   function myinterface1.test=test1;
   function myinterface2.test=test2;

   function test1:integer;
   function test2:integer;
 end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function obj1.test1:integer;
begin
  result:=1;
end;

function obj1.test2:integer;
begin
  result:=2;
end;

function obj2.test1:integer;
begin
  result:=3;
end;

function obj2.test2:integer;
begin
  result:=4;
end;

procedure TForm1.Button1Click(Sender: TObject);
var a,b,c,d:integer;var1,var2:obj1;var3,var4:myinterface2;
begin
 var1:=obj1.create;
 var2:=obj1.create;
 var3:=obj1.create;
 var4:=obj2.create;

 a:=myinterface1(var1).test;
 //a:=var1.test1;
 b:=myinterface2(var1).test;
 c:=var3.test;
 d:=var4.test;

 Edit1.text:=inttostr(a)+
             inttostr(b)+
             inttostr(c)+
             inttostr(d);
end;

In deinem Source fehlte ein wichtiges Element: Klasse auf Interface konvertieren ... Hatte ich bei mir bewusst drin ...

user profile iconAllesquarks hat folgendes geschrieben:
Hier der CPU Auszug
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
asm
//Unit1.pas.78: a:=myinterface1(var1).test;
{00454E77 8BC3           } mov eax,ebx               //Instanzenzeiger lesen
{00454E79 85C0           } test eax,eax              //Auf Gültigkeit prüfen
{00454E7B 7403           } jz $00454e80              //Wenn Nil eine Exception werfen ***
{00454E7D 83E8F0         } sub eax,-$10    //ich nehme mal an das ist der Offset vom Interface //Korrekt ;-)
{00454E80 8B10           } mov edx,[eax]             //Interface-Tabelle für Interface auslesen
{00454E82 FF520C         } call dword ptr [edx+$0c]   //Hier Offset der Methode //Jup. VMT-Offset
{00454E85 8BF8           } mov edi,eax               //Ergebnis speichern
//Unit1.pas.80: b:=myinterface2(var1).test;
{00454E87 8BC3           } mov eax,ebx               //Instanzenzeiger lesen
{00454E89 85C0           } test eax,eax              //Auf Gültigkeit prüfen
{00454E8B 7403           } jz $00454e90              //Wenn NIL eine Exception werfen ***
{00454E8D 83E8F4         } sub eax,-$0c    //Offset vom anderen Interface
{00454E90 8B10           } mov edx,[eax]             //Interface-Tabelle für Interface auslesen
{00454E92 FF520C         } call dword ptr [edx+$0c]   //Hier Offset der Methode
{00454E95 8BD8           } mov ebx,eax               //Ergebnis speichern
//Unit1.pas.81: c:=var3.test;
{00454E97 8B45FC         } mov eax,[ebp-$04]         //Instanzenpointer lesen - Delphi ist so bescheuert!!!
{00454E9A 8B10           } mov edx,[eax]             //VMT zugreifen
{00454E9C FF520C         } call dword ptr [edx+$0c]  //Methodeneintrag aus VMT lesen und aufrufen
{00454E9F 8945F4         } mov [ebp-$0c],eax         //Ergebnis speichern
//Unit1.pas.82: d:=var4.test;
{00454EA2 8B45F8         } mov eax,[ebp-$08]         //Instannzenpointer lesen ...
{00454EA5 8B10           } mov edx,[eax]             //VMT zugreifen
{00454EA7 FF520C         } call dword ptr [edx+$0c]  //Methodeneintrag aus VMT lesen und aufrufen
{00454EAA 8945F0         } mov [ebp-$10],eax         //Ergebnis speichern
end;


user profile iconAllesquarks hat folgendes geschrieben:
Das Problem ist ja nur wie man an diese Offsets rankommt. Denn jetzt einfach immer +$0C zu schreiben ist ja ganz schön riskant wenn der Compiler sich mal entschließen sollte die Tabelle umzugruppieren dann krachts. Gleiches gilt natürlich für die Offsets wo die Interfaces stehen


Als kleine Anmerkung: Delphi ist doof. Die drei Sterne stehen für eine Kuriosität dieses compilers: Wenn man Nil übergibt tritt die damit erzeugte Exception IMMER genau an Offset 0 auf, nicht jedoch an irgendeinem anderen Offset.

So wie's aussieht, muss man dafür einiges rumpointern, wenn man's manuell machen möchte.

Soweit ich das dem RTL-Source entnehmen kann, funktioniert das grob so:
- Abfragen der GUID des gewünschten Interfaces.
- Abfragen der Interface-Table (Gibt's an Self+vmtIntfTable, Self-72)
- Suchen des gewünschten Interfaces in der Table (Man kann für diesen Schritt auch Self.QueryInterface nehmen
- Aufruf der gewünschten Methode mit
ausblenden Delphi-Quelltext
1:
2:
3:
asm
    CALL    DWORD PTR [EDX] + VMTOFFSET Interfacetyp.Methodenname
end;

Wobei EDX hier den Pointer auf die Interface-Tabelle beinhaltet (EAX muss dabei gerettet werden, da Self in EAX übergeben werden muss).

HTH soweit...

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.