Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Meine erste Butterfahrt - Eine dll mit dem BCB erstellen (https://www.delphipraxis.net/147437-meine-erste-butterfahrt-eine-dll-mit-dem-bcb-erstellen.html)

messie 8. Feb 2010 17:19


Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Moin,

mit dlls habe ich mich bisher wenig beschäftigt, ich möchte aber Anzahl Berechnungen in eine dll kapseln. Die liegen im BCB vor, also wollte ich die dll auch dort erstellen und später aus dem BCB und Delphi darauf zugreifen.

Dabei bin ich schon beim Assistenten zum Erstellen gestolpert: Quelltyp C++ ist mir klar, VCL verwenden ist für Berechnungen nicht erforderlich, ebensowenig Multi-Threading. Aber "dll im VC++-Stil"?? Was ist denn dann anders?

Gibt es irgendwo ein Tutorial, wo man sich das mal reinziehen kann? Was ist einfacher? Die dll in Delphi zu erstellen und eine C-Headerdatei zu bauen oder wie geplant eine C++-dll in Delphi nutzen?

Grüße, Messie

Medium 8. Feb 2010 17:26

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Solange du nichts delphispezifisches in der DLL nutzt (also alles was die Unit ShareMem voraussetzt, z.B. Delphi-Strings oder dynamische arrays im Delphi-Stil), und für jede Funktion/Prozedur stdcall als Aufrufkonvention nutzt, ist eine Delphi-DLL aus Interface-Sicht nicht von einer C++ DLL zu unterscheiden. (Name-Mangling mal aussen vor, falls das hier überhaupt mit reinspielt.)

messie 11. Feb 2010 15:26

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
So,

das waren ja Krämpfe, aber mit dem Builder geht es jetzt. Lustig ist, dass sich das dll-Projekt nach dem Erstellen der dll selbst zerstört :shock:
Ich habe jetzt zum Probieren eine dll gebaut:
Code:
extern "C" __declspec(dllexport) double Multi(double Zahl1, double Zahl2);

double Multi(double Zahl1, double Zahl2)
{
  double result;
  result = Zahl1 * Zahl2;
  return result;
}
Wozu das alles entscheidende extern "C" reinmuss, ist mir nicht ganz klar geworden.

Nun wollte ich die Funktion mal in einem Delphi-Projekt testen. Dazu erstmal statisch geladen:
Delphi-Quellcode:
var
  Form1: TForm1;
  function Multi(Zahl1 : double;Zahl2:double) : double; external 'Project9.DLL';


implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  test : double;
begin
  test := Multi(9,6);
  ShowMessage(FloatToStr(test));
end;
Da wird der Prozedureinsprungpunkt "Multi" nicht gefunden. Was habe ich da falsch gemacht?
Beim Builder kann man das statische Laden durch Einbinden der lib in das Projekt erledigen. Gibt es etwas Vergleichbares in Delphi?

Danke, Messie

Edit: Ich habe mich gerade mal kurz in den Dependency Walker eingelesen und damit herausgefunden, wie die Prozedur tatsächlich heißt ("_Multi").
Ist denn das eine Festlegung, dass der Unterstrich immer da ist, wenn ich extern "C" benutze?

Jaynder 11. Feb 2010 15:43

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Zitat:

Zitat von messie
Beim Builder kann man das statische Laden durch Einbinden der lib in das Projekt erledigen. Gibt es etwas Vergleichbares in Delphi?

Wirst wohl
Delphi-Quellcode:
 
LoadLibrary (PChar (FN));
...
@Proc := GetProcAddress (HW, 'Xyz');
...
Proc (...);
...
FreeLibrary (HW);
verwenden müssen, für einzelne Calls auch kein Problem;

messie 12. Feb 2010 12:04

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Moin,

nochmal eine andere Frage zum Thema: der Linker meckert die Exceptions an. Ist in dlls ein try...catch - Block zulässig?

Grüße, Messie

hoika 12. Feb 2010 12:43

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Hallo,

Zitat:

_Multi
Ist denn das eine Festlegung, dass der Unterstrich immer da ist, wenn ich extern "C" benutze
Jepp.
Damit können mit verschiedenen C-Compilern erstellte Objekt-Dateien
verwendet werden, quasi kleinster gemeinsamer Nenner für Methoden-Beschreibung.

Ohne das extern "C" wird der Name durch Name-Mangling erzeugt,
der die Anzahl und Art der Parameter beinhaltet,
z.B. _Multi@36
Damit können überladene Methoden unterschieden werden.
Jeder Compiler macht das aber anders.

Zitat:

Beim Builder kann man das statische Laden durch Einbinden der lib in das Projekt erledigen. Gibt es etwas Vergleichbares in Delphi?
Das hast du mit dem external ja in etwa schon gemacht.

Delphi-Quellcode:
function Multi(Zahl1 : double;Zahl2:double) : double; external 'Project9.DLL' name '_Multi';

Heiko

messie 12. Feb 2010 13:41

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Nochmal eine Frage: ich muss jetzt eine ganze Menge Parameter übergeben. Das wird nicht wirklich übersichtlich. Ein record oder ein struct wäre sinnvoll. Das werde ich ja nicht direkt als Parameter übergeben können, oder vielleicht doch?
Oder muss ich die Struct im Programm ebenso wie in der dll definieren und dann vom Programm aus einen Pointer draufsetzen? Wie gebe ich den Pointer nach der Berechnung wieder ans Programm zurück?

Danke, Messie

Jaynder 12. Feb 2010 14:48

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
definiere den Record-Typen und übergebe ihn als const- oder var-Parameter, dann kommt auf jeden fall in der dll der Pointer an. Kannst du dann dort direkt ändern. Du muss natürlich darauf achten, das das C-struc exakt übereinstimmt, z.b. der align-Parameter muss passen. Schreibe am besten eine Assertion mit der konkreten "sizeof" der Delphi- und C-Typen, dann wird es schon passen. Oder schreibe einen call, der den ersten und letzen Parameter gezielt verändert (z.B. Inc oder *2) und prüfe nach dem Aufruf, ob der Wert sich wie gewollt verändert hat. Dann kannst du sicher sein, das die beiden Typen zueinander passen.

hoika 12. Feb 2010 17:01

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Hallo,

in Delphi nimmst du einen var-Parameter,
das ist dann automatisch ein Pointer.

Den Record musst du natürlich in Delphi und in BCB je einmal definieren,
packed record wäre mal ein Stichwort.

In der C-DLL ist der var-Parameter dann ein Pointer-Parameter.


BTW:
Kleiner Hinweis: BCB kann auch Pas-Dateien kompilieren ...


Heiko

messie 12. Feb 2010 17:14

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Zitat:

Zitat von hoika
Hallo,

in Delphi nimmst du einen var-Parameter,
das ist dann automatisch ein Pointer.

Den Record musst du natürlich in Delphi und in BCB je einmal definieren,
packed record wäre mal ein Stichwort.

In der C-DLL ist der var-Parameter dann ein Pointer-Parameter.


BTW:
Kleiner Hinweis: BCB kann auch Pas-Dateien kompilieren ...


Heiko

Danke, ich werde mich da mal reinwurschteln. :wink:

Die Routinen liegen bereits als BCB-Code vor und ich möchte sie nicht erst in Delphi umbasteln.

Grüße, Messie

messie 19. Feb 2010 09:56

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
So, ich bin da mal wieder dran...

Ich habe jetzt einen struct in der dll erstellt und den als Parameter der function _Multi übergeben. Der Aufruf aus dem Builder klappt, beim Aufruf aus Delphi klemmt es.

Delphi-Quellcode:
TParList = record
    Par1 : double;
    Par2 : double;
    TempLow : double;
    TempHi : double;
    PE : bool;
  end;
sollte identisch sein mit
Code:
typedef struct ParList
{
   double Par1;
   double Par2;
   double TempLow;
   double TempHi;
   bool PE;
} TParList;
Und
Delphi-Quellcode:
function _Multi(var Values : TParList) : double; external '2008.dll';
mit
Code:
extern "C" __declspec(dllimport) double Multi(TParList *Values);
In der function selbst wird nur ein Eingabewert als Ergebnis durchgereicht.
Der Aufruf im Delphi-Programm liefert aber Müll zurück. Ich habe die TParList in Delphi lokal definiert und übergebe sie dann als var-Parameter, während ich sie in C++ mit new erzeuge. Kann das ein Problem sein? Ich dachte, beim record wird der Speicher automatisch zugewiesen.

Grüße, Messie

Jaynder 19. Feb 2010 10:08

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Ja, der Speicher kommt doch schon von Delphi, also im C-Teil einfach mit dem übergebenen Pointer arbeiten und auf keinen Fall mit New anlegen, dann müsste es gehen.

Jaynder 19. Feb 2010 10:16

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Oder noch besser:

Delphi-Quellcode:
type PParList = ^TParList;
function _Multi(Values : PParList) : double; external '2008.dll';
und Aufruf mit
Delphi-Quellcode:
var P: TParList;
begin
  _Multi (@P)
end;
Wenn du dann noch mit $T+ die Typprüfung für den @-Operator einschaltest, hast dann auch Sicherheit, das nur passende Parameter übergeben werden können.

messie 19. Feb 2010 10:33

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Hmmm, das Ergebnis bleibt gleich - keine sinnvolle Antwort.

New habe ich auch nur in meinem C++ Testprojekt verwendet, nicht in der dll.

Kann man sich denn da mit dem Debugger reinhängen? Alle Projekte sind als debug-Version erstellt.

Grüße, Messie

Jaynder 19. Feb 2010 10:44

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Auch nicht mit der Delphi-Pointer-Variante?

messie 19. Feb 2010 10:59

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Zitat:

Zitat von Jaynder
Auch nicht mit der Delphi-Pointer-Variante?

Nee, leider nicht. Der Aufruf scheint zu funktionieren, ich bekomme aber einen Zufallswert nahe 0 zurück.

Die Funktion in der dll ist auch nicht besonders anspruchsvoll.
Code:
double Multi(TParList *Values)
{
   double result;
   result = Values->TempLow;
   return result;
}
Und der Aufruf aus C++ liefert den übergebenen Wert.


Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Test : TParList;
  result : double;
begin
  Test.TempLow := -333;
  result := _Multi(Test);
  ShowMessage(FloatToStr(result));
end;
Mit der Pointer-Variante kommt ein identisches Ergebnis

Grüße, Messie

Jaynder 19. Feb 2010 11:13

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
dann müsste es an der struktur oder der Reihenfolge der Felder liegen. Vereinfache dein Beispiel erstmal auf einen Record mit nur einem double.

messie 19. Feb 2010 12:02

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Zitat:

Zitat von Jaynder
dann müsste es an der struktur oder der Reihenfolge der Felder liegen. Vereinfache dein Beispiel erstmal auf einen Record mit nur einem double.

Auch das funzt nicht. Direkt als double-Parameter ohne den record geht, mit der record-Struktur nicht.
:wall:

Jaynder 19. Feb 2010 12:12

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
dann versuch mal cdecl, hatte ich bisher leider übersehen:

Delphi-Quellcode:
function _Multi(Values : PParList) : double; cdecl; external '2008.dll';

messie 19. Feb 2010 13:08

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Jo, nu' geht es. Danke!

Auf was alles hat denn das cdecl einen Einfluss? Also auf double-Parameter ja nicht. Aber dann kommt alles mit Feldern in Verdacht: records, arrays, Objekte?

Ich hab' mal einen AnsiString probiert, da kommt ein Haufen Linkermeldungen. Da wird dann wohl eher PChar in Frage kommen.

Grüße, Messie

Jaynder 19. Feb 2010 13:57

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Auf so ziemlich alles: Parameterreihenfolge auf dem Stack, verwaltet die Prozedur oder der Caller den Stack usw. Das läuft in C alles ziemlich anders als unter Delphi. Das Wiki Aufrufkonvention gibt einen schnellen Überblick. War jedenfalls eher Zufall, dass es mit dem einfachen double funktioniert hat.

Für WinAPI-Calls musst du z.B. immer stdcall nehmen.

Die Methode mit den Pointertypen und dem $T+ kann ich dir übrigens sehr empfehlen, erspart eine Unmenge an harten Crash's wegen falscher Typen, sobald du mehr als zwei bis drei Calls verwenden musst. Die Delphi-Calls ans WinAPI sind jedenfalls auch so umgesetzt.

Na, nun gutes Gelingen bei deinem Projekt.

Jürgen

messie 10. Mär 2010 15:42

Re: Meine erste Butterfahrt - Eine dll mit dem BCB erstellen
 
Upps,

jetzt bin ich auf die Nase gefallen (borlndmm.dll nich gefunden). Warum fragt die Anwendung denn zu Beginn nach dem Borland-Speichermanager? Das sollte eigentlich doch nur sein, wenn man Borland-spezifische Sachen wie Strings benutzt...

Liegt das daran, dass ich VCL in der dll benutze? Ich fange Exceptions in der dll ab, könnte das die Ursache sein?

Grüße, Messie

Edit: hat sich erledigt. Ich hatte zwar die Linkeroption "dynamische RTL verwenden" im Programm abgewählt, im dll-Projekt stand es aber noch drin. Das hätte der dll-Wizard eigentlich für mich erledigen sollen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:04 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz