Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Methodenzeiger in DLL verursacht Fehler


Gewuerzgurke - Fr 25.09.09 17:23
Titel: Methodenzeiger in DLL verursacht Fehler
Hallo,
mein neues Problem hat große Ähnlichkeit mit diesem http://www.delphi-forum.de/topic_OUTDeklaration+funktioniert+nicht+in+DLL_94735.html.
Ich brauche nämlich noch eine "Rückruf-Funktion", mit der die DLL bestimmte Aktionen im Programm durchführen kann...

Also (Dieses Mal Originalquelltext ;-) ):

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:
// in meinem Programm habe ich folgende procedure:
procedure TPresentation.Callback(ClassID,Flags : integer; Action : cardinal);
begin
 showmessage('hy');
end;

// diese wird dann als Pointer an die DLL uebergeben:
ObjectClasses[i].Create(i,@TPresentation.Callback);  // <-- Methodenzeiger auf Start-Procedure der DLL

// in der DLL wird dieser Pointer entsprechend gecastet:
(...)
type
 TPresCallback = procedure(ClassID,Flags : integer; Action : cardinal);

var
 DllCallback : TPresCallback;

(...)

procedure Create(Id : integer; Callback : Pointer);
begin
 showmessage('1');
 DllCallback := TPresCallback(Callback);
 if (assigned(DllCallback)) then DllCallback(0,0,0);
 showmessage('2');
end;


Beim Ausführen kommt dann korrekter Weise "1" --> "hy" --> "2". Wenn die Procedure in der DLL fertig ist, kommt der Fehler.
(Wenn if (assigned(DllCallback)) then DllCallback(0,0,0); noch in einer Klasse in der DLL steht, kommt erst nochmal "hy", und dann der Fehler :gruebel: )

Weil dieses Problem erhebliche Ähnlichkeit mir meinem Vorherigen (als ich ShareMem noch nicht richtig eingebunden hatte) zeigt, gehe ich davon aus, dass es ebenfalls durch mangelhafte Speicherverwaltung ausgelöst wird, aber jetzt habe ich ShareMem, das ich derzeit noch verwende, eingebunden...

Kann es sein, dass man derartige Rückrufe normaler Weise über Windows-Messages löst? Ich meine, dass währe dadurch langsamer und auch stark eingeschränkt...
Was soll ich machen / was habe ich falsch gemacht?


Gewuerzgurke - Fr 25.09.09 17:32

Oh, Augenblick...

Ich glaub' mir ist eben eine mögliche Lösung eingefallen...


Gewuerzgurke - Fr 25.09.09 18:27

Mpf! War ja klar, dass mir die Lösung erst einfällt, wenn ich schon gefragt habe...

Also: Das Problem sind die Parameter der procedure Callback; mit denen scheint ShareMem wohl Schwierigkeiten zu haben.
Deswegen habe ich die Parameter einfach weggelassen und übergebe die Werte, indem ich einfach noch einen Pointer auf eine Variable (in meinem Fall halt ein (packed) Record) mitgebe. Diese Variable kann die DLL dann ohne Probleme "bearbeiten" und die Procedure Callback liest dann statt ihren eigenen Parametern diese Variable.

Um das nochmal an dem Schema oben aufzuzeigen:

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:
// in meinem Programm:
type
 TPresCallbackParams = packed record
  ClassID : integer;
  Flags   : integer;
  Action  : cardinal;
 end;

var PresCallbackParams : TPresCallbackParams;  //  <-- Darf nicht in Klasse stehen!


(...)

procedure TPresentation.Callback;
begin
 showmessage(IntToStr(PresCallbackParams.ClassID));
 // Dieses Mal einen Wert ausgeben, um zu zeigen, dass Zuweisung korrekt ist.
end;

// Callback-Procedure und PresCallbackParams an DLL uebergeben:
ObjectClasses[i].Create(i,@TPresentation.Callback,@PresCallbackParams);  // <-- Methodenzeiger auf Start-Procedure der DLL

// in der DLL:
(...)
type
 TPresCallback = procedure;
 TPresCallbackParams = packed record
  ClassID : integer;
  Flags   : integer;
  Action  : cardinal;
 end;
 PPresCallbackParams = ^TPresCallbackParams;

var
 DllCallback       : TPresCallback;
 DllCallbackParams : PPresCallbackParams;

(...)

procedure Create(Id : integer; Callback,CallbackParams : Pointer);
begin
 showmessage('1');
 DllCallbackParams := PPresCallbackParams(CallbackParams);
 DllCallbackParams^.ClassID := 5;    // <-- Zu Testzwecken beliebigen Wert zuweisen.
 DllCallback := TPresCallback(Callback);
 if (assigned(DllCallback)) then DllCallback;
 showmessage('2');
end;


Hihi, kreativ muss man sein ;-)


Gewuerzgurke - Sa 26.09.09 17:15

Naja, ganz so genial war die Idee wohl doch nicht...

Das Problem ist, dass der Pointer auf die Callback-Procedure mit @TPresentation.Callback bestimmt wird. Dadurch wird dann die Methode Callback nur in der exe aufgerufen und nicht in der geladenen Klasse TPresentation, die in meinem Programm mit Presentation := TPresentation.Create; erzeugt wird. Deswegen hat diese Callback-Methode dann auch keinen Zugriff auf die Variablen in ihrer Klasse.

Ich habe schon versucht, einen Pointer auf die geladene Klasse mitzugeben, jedoch ist diese weit im Speicher verstreut... - oder kurz gesagt: Ging nicht!

Wie bekomme ich also den Pointer auf die geladene Callback-Methode?

Mit @Callback schonmal nicht. Das hat hier schon jemand versucht: http://forum.delphi-treff.de/showthread.php?t=22781


Tryer - Sa 26.09.09 20:37

Grübel.. TMethod .. TPresCallBack = procedure of object; ..
Zu TMethod sollte sich noch was finden lassen, vielleicht hilft das weiter


Gewuerzgurke - Sa 26.09.09 21:04

of object hilft leider nicht. Dadurch bekomme ich nur einen internen Fehler C4905. TPresCallback steht ja in der DLL.

Ich überlege gerade, wie man mein Problem mit 2 Sätzen erklären kann...

Also: Wie bekommt man einen Pointer auf eine Procedure in einer Klasse?
Ich habe gelesen, dass Proceduren in Klassen immer eine Eigenschaft "self" erwarten. Ich denk' mal, die ist es, die in meinem Programm oben nicht stimmt.

Ist da was dran?


Tryer - Sa 26.09.09 22:26

Vielleicht hilft das:

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:
library dllproj;

uses
  SysUtils, 
  Classes;

{$R *.res}

type
  TCallbackFunc = procedure of object;
  
var
  M: TMethod;

procedure RegisterCallBack(Instance, Proc: Pointer); stdcall;
begin
  m.Code := Proc;
  m.Data := Instance;
end;

procedure DoCallback; stdcall;
begin
  TCallBackFunc(m);
end;

exports
  RegisterCallBack,
  DoCallback;

begin
end.


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:
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  published
    procedure Callback;
  end;

  procedure RegisterCallback(Instance, Proc: Pointer); stdcall;
  procedure DoCallback; stdcall;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure RegisterCallBack(Instance, Proc: Pointer); external 'dllproj.dll' name 'RegisterCallBack';

procedure DoCallback; stdcallexternal 'dllproj.dll' name 'DoCallback';

procedure TForm1.Button1Click(Sender: TObject);
var p: pointer;
begin
  p := ;
   RegisterCallBack(Form1, MethodAddress('Callback'));
end;

procedure TForm1.Callback;
begin
  Button1.Caption :='bestanden';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DoCallback;
end;

MethodAddress funzt nur für published - Methoden.


Gewuerzgurke - So 27.09.09 10:57

Das habe ich gesucht! TMethod.Data ist genau dieser Pointer, der bei mir oben gefehlt hat. :D

Ich habe bei RegisterCallBack(Form1, MethodAddress('Callback')); statt Form1 noch self eingesetzt. Das erspart einem wieder einen Pointer. :wink:

[EDIT:] Dann lag's wohl doch an "of object". Wenn man weiß, wie's geht...


Tryer - So 27.09.09 13:12

Nur als Spielerei, müsste auch funktionieren:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
//dll
procedure RegisterCallBack(Instance, Proc: Pointer);

//prog
TRegisterCallBack = procedure(Proc: Pointerof object;
..
  m: TMethod;
begin
  m.Data := Self;
  m.Code := GetProcAddress(hMyDll, 'RegisterCallBack');
  TRegisterCallBack(m)(MethodAddress('Callback'));
..

Das ganze Spiel anders herum: "So tun als wäre eine Prozedur eine procedure of object"

Grüsse, Dirk