Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Pointer auf Methoden-Pointer


Hami04 - Mi 12.05.10 09:59
Titel: Pointer auf Methoden-Pointer
Hallo,
ich habe ein Problem mit Pointern auf Methoden-Pointer.
Ein Methoden-Pointer in Delphi besteht ja aus einem Record mit zwei Pointern: einen auf die Funktion und einen auf das Objekt. Diese Methoden-Pointer möchte ich in eine Struktur packen. Die Struktur nimmt allerdings nur einfache Pointer auf. Ich versuche also, einen Pointer auf den Methoden-Pointer, also auf den Record, zu nehmen und diesen in die Struktur zu packen. Wenn ich den Pointer jetzt dereferenziere bekomme ich einen Zugriffsverletzung.

Hier etwas Beispielcode:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure test
type
   TMethod = procedure of object;
var
   Method: TMethod;
   Method2: TMethod;
   pMethod: ^TMethod;
begin
   Method := MyObject.MyMethod;
   pMethod := @Method;
   Method2 := pMethod^;

   Method; // funktioniert, es wird erfolgreich MyObject.MyMethod aufgerufen
   Method2; // Zugrifssverletzung
end;


Kann sich irgendjemand, das erklären. Ich nämlich leider nicht.

Viele Grüße
Philipp


BenBE - Mi 12.05.10 10:04

Gib mal bitte den gemischten Assembler-Code aus dem CPU-Fenster für diese Routine. Dann kann ich Dir sagen, was Delphi da baut. Ich denk mal, der wird einmal zu wenig Dereferenzieren. Aber mehr mit ASM-Code dazu.


Hami04 - Mi 12.05.10 10:39

Problem ist gelöst. Es muss ein @@ genommen werden.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure test
type
   TMethod = procedure of object;
var
   Method: TMethod;
   Method2: TMethod;
   pMethod: ^TMethod;
begin
   Method := MyObject.MyMethod;
   pMethod := @@Method; // hier werden zwei @@ eingfügt
   Method2 := pMethod^;

   Method; // funktioniert, es wird erfolgreich MyObject.MyMethod aufgerufen
   Method2; // Zugrifssverletzung
end;


jaenicke - Mi 12.05.10 10:52

Hier der komplette Assemblercode:

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:
Unit244.pas.38begin
00457E54 83C4F0           add esp,-$10
Unit244.pas.39: MyObject := TTest.Create;
00457E57 B201             mov dl,$01
00457E59 A1547C4500       mov eax,[$00457c54]
00457E5E E8C5B9FAFF       call TObject.Create
Unit244.pas.40: Method := MyObject.MyMethod;
00457E63 89442404          mov [esp+$04],eax
00457E67 C70424947E4500   mov [esp],$00457e94
Unit244.pas.41: pMethod := @Method;
00457E6E 8B0424           mov eax,[esp]
Unit244.pas.42: Method2 := pMethod^;
00457E71 8B10             mov edx,[eax]
00457E73 89542408          mov [esp+$08],edx
00457E77 8B5004           mov edx,[eax+$04]
00457E7A 8954240C          mov [esp+$0c],edx
Unit244.pas.44: Method; // funktioniert, es wird erfolgreich MyObject.MyMethod aufgerufen
00457E7E 8B442404         mov eax,[esp+$04]
00457E82 FF1424           call dword ptr [esp]
Unit244.pas.45: Method2; // Zugrifssverletzung
00457E85 8B44240C         mov eax,[esp+$0c]
00457E89 FF542408         call dword ptr [esp+$08]
Unit244.pas.46end;
00457E8D 83C410           add esp,$10
00457E90 C3               ret 
00457E91 8D4000           lea eax,[eax+$00]
Unit244.pas.52: ShowMessage('aa');
00457E94 B8A87E4500       mov eax,$00457ea8
00457E99 E89644FDFF       call ShowMessage
Der Delphicode dazu:

Delphi-Quelltext
 
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
{ ... }
procedure TForm244.Button1Click(Sender: TObject);
type
  TMethod = procedure of object;
var
  Method: TMethod;
  Method2: TMethod;
  pMethod: ^TMethod;
  MyObject: TTest;
begin
  MyObject := TTest.Create;
  Method := MyObject.MyMethod;
  pMethod := @Method;
  Method2 := pMethod^;

  Method; // funktioniert, es wird erfolgreich MyObject.MyMethod aufgerufen
  Method2; // Zugrifssverletzung
end;

{ TTest }

procedure TTest.MyMethod;
begin
  ShowMessage('aa');
end;
// Ich sehe schon die Antwort, aber ich poste es einfach einmal trotzdem inkl. Ergänzung:
Mit @@ dann sieht der Code so aus:

Delphi-Quelltext
 
9:
10:
11:
{ ... }
00457E67 C70424907E4500   mov [esp],$00457e90
Unit244.pas.41: pMethod := @@Method;
00457E6E 8BC4             mov eax,esp
Man sieht also, dass jetzt direkt der Wert aus esp genommen wird statt dem Wert an der referenzierten Speicherstelle.


BenBE - Mi 12.05.10 12:32

Jap. Der Delphi-Compiler vergisst bei der Zuweisung TROTZ dem angegebenen @ einmal die Adresse zu bilden. Manchmal kann einem die Compiler-Magic wirklich auf den Keks gehen.