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:
| procedure TPresentation.Callback(ClassID,Flags : integer; Action : cardinal); begin showmessage('hy'); end;
ObjectClasses[i].Create(i,@TPresentation.Callback); (...) 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:
| type TPresCallbackParams = packed record ClassID : integer; Flags : integer; Action : cardinal; end;
var PresCallbackParams : TPresCallbackParams;
(...)
procedure TPresentation.Callback; begin showmessage(IntToStr(PresCallbackParams.ClassID)); end;
ObjectClasses[i].Create(i,@TPresentation.Callback,@PresCallbackParams); (...) 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; 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 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; stdcall; external '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:
| procedure RegisterCallBack(Instance, Proc: Pointer);
TRegisterCallBack = procedure(Proc: Pointer) of 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
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!