Autor Beitrag
Gewuerzgurke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 152

Win XP
Lazarus
BeitragVerfasst: Fr 25.09.09 17:23 
Hallo,
mein neues Problem hat große Ähnlichkeit mit diesem www.delphi-forum.de/...ht+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 ;-) ):
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:
// 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 152

Win XP
Lazarus
BeitragVerfasst: Fr 25.09.09 17:32 
Oh, Augenblick...

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


Zuletzt bearbeitet von Gewuerzgurke am Fr 25.09.09 18:31, insgesamt 1-mal bearbeitet
Gewuerzgurke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 152

Win XP
Lazarus
BeitragVerfasst: 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:
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:
// 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 152

Win XP
Lazarus
BeitragVerfasst: 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: forum.delphi-treff.d...owthread.php?t=22781
Tryer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 152

Win XP
Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: Sa 26.09.09 22:26 
Vielleicht hilft das:
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:
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.

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:
  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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 152

Win XP
Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: So 27.09.09 13:12 
Nur als Spielerei, müsste auch funktionieren:
ausblenden 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