Callback prozeduren aus dll zu Class.Prozedur?

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Bora4d
Beiträge: 290
Registriert: Mo 24. Dez 2007, 13:14
OS, Lazarus, FPC: WinXP-Pro-Sp3, Xubuntu 12.04, (Laz 1.1-SVN Mai2012, FPC 2.6.1 / 2.6.0-Linux)
CPU-Target: AMD64X2

Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von Bora4d »

Kann man in Delphi/Pascal von Dll aus Klassen-Prozuduren aufrufen? Ist das in Delphi/Pascal grundsätzlich möglich?
Ich meine es so:

Code: Alles auswählen

unit irgend-eine-komponente;
...
type 
  TClassProz = procedure(AParam1:integer) of object;
 
  TEins = class(TWinControl)
  public
    RufeDasAusDll : TClassProz;
  end;
...
begin
  //hier erstelle ich die Klasse(EinObjekt) und übergebe die Adresse von EinObjekt.RufeDasAusDll  zu DLL
end;
Und das ist die DLL.

Code: Alles auswählen

library irgende-in-dll;
...
...
type
  TClassProz = procedure(AParam1:integer) of object;
 
  TDllKlasse = class(xy)
  public
    VonClas :TClassProz;
   procedure einprozedur;
   end;
procedure TDllKlasse.einprozedur;
begin
   VonClas(1); //<- funktion von  TEins.RufeDasAusDll wird aufgerufen
end;
 
procedure x(a:TClassProz);
 //wird von irgend-eine-komponente aufgerufen, und sorgt dafür dass ein TDllKlasse überstellt wird
//variablen initiert werden.
Wenn ich aus dll unit mache funktioniert callback Aufruf eines Klassen-Prozedurs. Aber ein Aufruf aus dll erzeugt externe Exception (SIGILL?!?).

Ich mache z.Z. diese Webbrowser Komponente von hier:
http://www.lazarus.freepascal.org/index ... 762.0.html

Ohne die Ereignisse zweifach oder dreifach auszuwerten wollte ich die Ereignisse aus dll direkt zu zugehörige Objekte verteilen.
Mit normalem Callback-Prozedur muß ich in der Komponenten-Unit ankommende Ereignisse an die richtigen Objekte verteilen.

Benutzeravatar
theo
Beiträge: 10895
Registriert: Mo 11. Sep 2006, 19:01

Re: Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von theo »

Wozu brauchst du denn die DLL überhaupt? Kannst du das nicht einfach ohne machen?

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von mschnell »

Wenn die DLL auch mit FP erstellt ist sollte es genauso gehen, als wäre der Code nicht in einer DLL, sondern Statisch als Unit angelinked. Dann musst Du aber einen Trick verwenden, um den Memory-Manager der DLL und des Hauptprogramms dazu zu bringen, denselber heap zu verwalten und nicht zwei getrennte. Ich weiß nicht, wie das mit Lazarus geht (In delphi kenne ich den Trick :) ) .

Wenn die DLL aber in einer anderen Sprache ist, geht das so nicht. Dann muss das Interface zwischen DLL und Hauptprogramm "Flach" sein, also keine Klassen (und anderes Pascal spezifisches Zeug) benutzen.

Ein Callback geht dann, indem Du der DLL als Parameter in einem Funktionsaufruf explizit den pointer auf eine "flache" Funktion übergibst, die die DSLL dann aufruft. von da aus musst Du dann Deine Klassenfunktion aufrufen.

in etwa so:

Code: Alles auswählen

type 
  Tcallback=procedure(parameter: Integer); stdcall; 
  Tmyclass = class
    procedure callback(parameter: Integer);
  end;
 
 
var
  myclass: Tmyclass;
 
 
procedure initialize_callback(Tcallbac: callback); stdcall; external 'mydll.dll';
 
procedure flat_callback(parameter: Integer); stdcall;
begin
   myclass.callback(parameter);  
end;
 
....
  myclass := Tmyclass.Create;
  ...
  initialize_callback(@flat_callback);  
  ...
-Michael

Bora4d
Beiträge: 290
Registriert: Mo 24. Dez 2007, 13:14
OS, Lazarus, FPC: WinXP-Pro-Sp3, Xubuntu 12.04, (Laz 1.1-SVN Mai2012, FPC 2.6.1 / 2.6.0-Linux)
CPU-Target: AMD64X2

Re: Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von Bora4d »

theo hat geschrieben:Wozu brauchst du denn die DLL überhaupt? Kannst du das nicht einfach ohne machen?
In dll gibts delphi's webbrowser kompenente. Ich habe noch nie mit Win32Api-Aufrufen Ole-Container in Fenster plaziert. Ich mußte nachlesen wie das geht.
Einen vorhandene Delphi Komponente zuverwenden ist z.Zt. einfacher. Außerdem ist die dll-Dateigröße sehr klein.
z.Zt. gilt mein Hauptinteresse zu Webkit/Gtk und Linux. Ich wollte nur nachschauen ob ich IE-Komponente unter Win verwenden kann. Dann hätte ich bzw. die Lazarusgemeinde eine Browserkomponente sowohl für Linux als auch für Win.

mschnell:
Das verwende ich ja zur Zeit. Ich wollte nicht von dll "procedure flat_callback(parameter: Integer);" aufrufen sondern direkt "myclass.callback(parameter);" aufrufen.
Und das war mein Fehler :oops: Ich hatte einmal gelesen das Klassenprozeduren nur einmal gibt auch wenn man von einer Klasse 100 instanzen erstellt.
Jetzt ist mir etwas eingefallen ich probiere es.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von mschnell »

Bora4d hat geschrieben:Ich wollte nicht von dll "procedure flat_callback(parameter: Integer);" aufrufen sondern direkt "myclass.callback(parameter);" aufrufen.
Das geht nur, wenn die Class ABI (Application binary Interface) zwischen DLL und Hauptprogramm identisch ist und außerdem die Memory-Manager der DLL und des Hauptptogramms denselben Heap verwalten. Ich glaube kaum dass diese Bedingungen zwischen Free-Pascal und Delphi zutreffen.

-Michael

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von Socke »

Man kann auch Methoden und Klassenmethoden abflachen ohne eine extra Aufruf-API zu schreiben. Man kann diese nur nicht exportieren.
Dazu muss der Parameterliste bei Methoden ein Self von der Klasse des Objektes und bei Klassenmethoden ein Self von der Klasse der Klasse (so richtig?) übergeben werden. Welcher Typ ist theoretisch egal, da die Parameter eh binär übergeben werden und die Typensicherheit nur in der Hochsprache existiert; wichtiger ist, dass sie die selbe Größer eines Pointers hat.

Code: Alles auswählen

type
  TNotify = procedure (Sender: TObject) of object; // original existiert so
  TFlatNotify = procedure (Self: TObject; Sender: TObject); // abgeflacht
  TMyClass = class
    class procedure MyClassProc(Sender: TObject); // orignial Klassenmethoden
  end;
  TFlatClassMethod = procedure (Self: TMyClass; Sender: TObject); // abgeflacht
Zu beachten ist aber immer, dass die Klassentypen in dem Programm in der Regel nicht bekannt sind. Und selbst wenn, wird nicht garantiert, dass sie in der Bibliothek und im Programm genau gleich existierten (binär) und das müssen sie, damit man sie auch in ObjektPascal als Objekte bearbeiten kann.
An den Self-Parameter kommt man über einen Typcast der Methoden zu TMethod heran.

Wenn du den Unterschied zwischen Methode, Funktion und Prozedure (da ist einer!) nicht kennst, empfehle ich mschnells Variante (ist einfacher zu verstehen, umzusetzen und greift nicht so tief in die Sprachinternals ein). Alternativ gibt die Dokumentation (Sprachfeferenz und Programmers guide von der FreePascal Webseite) einen tiefen Einblick in die Sprache.

Edit:
Es gibt Interfaces. Die sind sprachunabhängig und erlauben ein (fast) objektorientiertes Programmieren.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Bora4d
Beiträge: 290
Registriert: Mo 24. Dez 2007, 13:14
OS, Lazarus, FPC: WinXP-Pro-Sp3, Xubuntu 12.04, (Laz 1.1-SVN Mai2012, FPC 2.6.1 / 2.6.0-Linux)
CPU-Target: AMD64X2

Re: Callback prozeduren aus dll zu Class.Prozedur?

Beitrag von Bora4d »

Ich habe es gelöst. Gestern nacht hatte ich nach lange herum programmieren vor lauter Bäume den Wald nicht gesehen. :(
Dabei war es sehr einfach. Mir ging es darum im Hauptprogramm nicht ganze Komponentenliste durchzusuchen um ankommende Ereignisse passend weiter zureichen.
So habe ich es gemacht: (Für die jenigen die vielleicht so was brauchen)

die dll: (pseudo-code!!!)

Code: Alles auswählen

library wblib;
...
//exportierte funktionen
type 
 TProgressProc = procedure(AExVar: Longint; Progress: Integer); 
 
  function CreateEx(AExVar: Longint):Longint; stdcall;
  procedure SetProgressProc(ProcAusProgramm: TProgressProc); stdcall;
 
//dll-intern von außen unbekannt
type 
   TDllObj: class
       ExternVar : longint; 
      procedure OnProgress; //beispiel einer event. wird automatisch aufgerufen, wie z.b. OnKeyDown
  end;
 
var 
  DllObjList : TList;
  CallBackProzVomProgramm : TProgressProc ;
 
procedure TDllObj.OnProgress;
begin
   if CallBackProzVomProgramm<>nil then
      CallBackProzVomProgramm(ExternVar, 100); //100 ist Beispielwert
end;
 
 
function CreateEx(AExVar: Longint):Longint; stdcall;
var aobj: TDllObj;
begin
   aobj:= TDllObj.Create;
   aobj.ExternVar:=AExVar;
 
   .... //erstellte objekt in die DllObjList eintragen usw.
 
  Result:=longint(aOnbj);  //zeiger auf erstellte objekt zurückgeben um später vom Programm aus richtige dll-Objekte zu zugreifen.
end;
 
procedure SetProgressProc(ProcAusProgramm: TProgressProc); stdcall;
begin
   CallBackProzVomProgramm:=ProcAusProgramm;
end;
Und das Programm: (Pseudo-Code)

Code: Alles auswählen

//aus dll importiert:
type 
 TProgressProc = procedure(AExVar: Longint; Progress: Integer); 
 
function CreateEx(AExVar: Longint):Longint; stdcall;
procedure SetProgressProc(ProcAusProgramm: TProgressProc); stdcall;
 
//und haupt programm
type 
   TProgObj: class
    ....
   property OnProgress(Value: Integer);
   end;
 
var
  ProgObjList : Tlist;
 
procedure CallBackProzWirdVonDllAufgerufen(AExVar: Longint; Progress: Integer); 
begin
   TProgObj(AExVar).OnProgress(Progress);  //man brauch überhaupt nicht in der  ProgObjList durchzusuchen 
                                                                 //mann kann direkt Klassenobjekt aufrufen :)
 end;
 
begin
   //einmalig Ereignis-Callback setzen:
  SetProgressProc(CallBackProzWirdVonDllAufgerufen); 
 
   //beliebige instanzen von  TProgObj und in die Liste eintragen z..b.
   ErsteObjekt:= TProgObj.Create;
  //und das jetzt mit dll-objekt verknüpfen
  ersteObjekt.DllObjekt:= CreateEx(longint(ersteObjekt)): 
 
  //...
end;
Das wars. Man das schreiben dieses Textes länger gedauert als Programm zuschreiben. :lol:

Antworten