| Autor |
Beitrag |
safexy
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: Fr 03.09.04 03:26
Hi,
bin gerade dabei zu verstehen, wie man ddl von C(++) in Delphi einbindet und verwendet...
und stehe vor einem unerklärbarem Fehler.
Benutz Visual Studio 2003 .net und Delphi 7
Inhalt der C-Datei
Quelltext 1: 2: 3: 4: 5: 6: 7:
| #include "dlltest.h"
__declspec(dllexport) int addiere(int zahl1, int zahl2)
{ return zahl1+zahl2; } |
Inhalt der C-Header-Datei
Quelltext 1: 2: 3: 4:
| #if!defined DLLTEST_H #define DLLTEST_H __declspec(dllexport) int addiere(int zahl1, int zahl2); #endif |
Delphi-Source:
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: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TSummenFunktion = function(zahl1, zahl2: integer): integer; stdcall; TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; procedure Button1Click(Sender: TObject);
private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
function addieren(zahl1, zahl2: integer; var summe: integer): integer; var SummenFunktion: TSummenFunktion; Handle: THandle; begin Handle:=LoadLibrary(PChar(ExtractFilePath(ParamStr(0))+'dlltest.dll')); if Handle <> 0 then begin @SummenFunktion := GetProcAddress(Handle, 'addiere'); if @SummenFunktion <> nil then begin summe:=SummenFunktion(zahl1, zahl2); result:=1; end else result:=0; FreeLibrary(Handle); end else result:=0; end;
procedure TForm1.Button1Click(Sender: TObject); var wert,summe: integer;
begin if (addieren(strtoint(edit1.Text),strtoint(edit2.Text),summe) <> 0) then begin wert:=summe; edit3.Text:=inttostr(wert) end else edit3.Text:= 'Fehler: DLL nicht ladbar!'; end;
end. |
Source der DLL die unter Delpi erzeugt wurde (als Gegenprobe)
der Name "rechnen" der Datei bei dem Aufruf wurde selbstverständlich dann angepasst
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| library rechnen;
uses SysUtils, Classes;
{$R *.res}
function addiere(zahl1, zahl2: integer): integer; stdcall; begin result:=zahl1+zahl2; end;
exports addiere;
begin
end. |
bei Aufruf unter obigen delphi-porgramm liefert die dll (mit Visual C++ erzeugt) den richtigen Wert zurück. Weise ich den zurückgegebenen Wert summe der Delphi-Funktion "function addieren(zahl1, zahl2: integer; var summe: integer): integer" außerhalb dieser, einem Objekt zu, kommt es zu einem Fehler, sobald er die Zuweisung ausführen soll.
Bei Verwendung der unter Delphi generierten DLL tritt dieser Fehler nicht auf.
was mache ich falsch?
bye
safexy
p.s.: auch wenn ich summe global unter delphi deklariere, passiert dieser mir unverständliche Fehler
Moderiert von Klabautermann: Code durch Delphi-Tags ersetzt.
|
|
Sprint
      
Beiträge: 849
|
Verfasst: Fr 03.09.04 04:17
*Reine Vermutung*
Integer ist unter Delphi ein LongInteger (32 Bit groß). Ein int unter C/C++ muss aber nicht unbedingt genau so groß sein.
Ich glaube int ist so groß wie SmallInt unter Delphi. Ersetzt mal in deiner C DLL int durch long. Dann sollte das wohl funktionieren.
_________________ Ciao, Sprint.
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Fr 03.09.04 09:50
| Sprint hat folgendes geschrieben: | Integer ist unter Delphi ein LongInteger (32 Bit groß). Ein int unter C/C++ muss aber nicht unbedingt genau so groß sein.
Ich glaube int ist so groß wie SmallInt unter Delphi. Ersetzt mal in deiner C DLL int durch long. Dann sollte das wohl funktionieren. |
Wohl bei 16-Bit stehen geblieben. VC++ erzeugt 32-Bit Programme. Und da "int" ein generischer Typ ist, wie "Integer" auch, ist er unter 32-Bit Systemen auch 32-Bit lang.
Das Problem ist nicht der "int"->"Integer" sondern das "stdcall". Nur weil die WinAPI alles als "stdcall" exportiert, wird nicht jede Funktion durch das "__declspec(dllexport)" gleich mit "stdcall" versehen. Sie bleibt im "cdecl". Ersetze einfach das "stdcall" durch ein "cdecl".
_________________ Ist Zeit wirklich Geld?
|
|
safexy 
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: Fr 03.09.04 11:48
Hi,
ja, da hab ich wohl was vergessen...
ändere ich beide Köpfe in C in
__declspec(dllexport) int __stdcall addiere(int zahl1, int zahl2)
sollte es ja auch eigentlich mit stdcall unter Delphi funktionieren..., macht es aber nicht, jetzt findet er einfach die Funktion addiere in der DLL nicht mehr...
belasse ich es in
__declspec(dllexport) int addiere(int zahl1, int zahl2)
und ändere im Delphiprogramm
Delphi-Quelltext 1: 2:
| type TSummenFunktion = function(zahl1, zahl2: integer): integer; stdcall; |
in
Delphi-Quelltext 1: 2:
| type TSummenFunktion = function(zahl1, zahl2: integer): integer; cdecl; |
dann funktioniert es.
Meine Frage ist, was muss ich tun, damit ich es unter delphi auch als stdcall aufrufen kann?
bye
safexy
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Fr 03.09.04 12:14
Mach mal ein extern "C" {} um deine Funktion:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| #define EXPORT __declspec(dllexport) extern "C" {
int EXPORT __stdcall addieren(int p0, int p2) { }
} |
extern "C" verhintert, dass C++ das sog. Name-mangling anwendet wodurch "addieren" ein paar Informationen über die Parametertypen angehängt werden. Das sieht bei MSVC meisten so aus: "??functionname@@Parameter1@@Parameter2"
Moderiert von Klabautermann: Delphi durch Code-Tags ersetzt.
_________________ Ist Zeit wirklich Geld?
|
|
safexy 
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: Fr 03.09.04 13:38
| AndyB hat folgendes geschrieben: | Mach mal ein extern "C" {} um deine Funktion:
|
Hi,
da die datei als .c deklariert ist, braucht man dies nicht und funktioniert auch gar nicht (Compilerfehler)
(nur wenn man .cpp benutzt kann man extern "C" benutzen)
Hab das ganze dann mal als .cpp datei gemacht, mit deinem erwähnten etxern "C", aber der Fehler bleibt der gleiche.
Dann noch ein kleiner Flüchtigkeitsfehler von dir: die funktion heißt addiere (nicht addieren).
Habe dies aber auch schon beachtet.
noch jemand eine Idee?
verwende Windows XP SP2, liegt es daran?
bye
safexy
|
|
Sprint
      
Beiträge: 849
|
Verfasst: Fr 03.09.04 13:45
| AndyB hat folgendes geschrieben: | | Wohl bei 16-Bit stehen geblieben. VC++ erzeugt 32-Bit Programme. Und da "int" ein generischer Typ ist, wie "Integer" auch, ist er unter 32-Bit Systemen auch 32-Bit lang. |
Naja, deswegen hatte ich ja geschrieben *reine Vermutung*.
| Zitat: | | Das Problem ist nicht der "int"->"Integer" sondern das "stdcall". |
Jetzt wo du es sagst, fällt's mir auch auf. War wohl gestern Nacht ein bißchen spät für mich. 
_________________ Ciao, Sprint.
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Fr 03.09.04 14:17
| safexy hat folgendes geschrieben: | | noch jemand eine Idee? |
Nimm einen gescheiten Compiler (Borland C++  ).
| Zitat: | | verwende Windows XP SP2, liegt es daran? |
Nein, denn wenn es daran liegen würde, würde nicht ein einziges Programm mehr laufen.
Ich habe mal einen Test mit dem MSVC 8 gemacht. Der schiebt, auch wenn ich ihm __stdcall angebe und die Funktion in einen extern "C" Block einschließe, einen Unterstrich voran, und hängt ein "@" + {Länge des Funktionsnamen} an.
Macht also "_addiere@7". Da gibt es sicherlich (besser: hoffentlich) auch einen Schalter, mit dem man diesen Schrott abstellen kann.
_________________ Ist Zeit wirklich Geld?
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Fr 03.09.04 14:24
So jetzt habe ich "die Option" gefunden. MSVC braucht eine DEF-Datei. Ohne die macht er das _name@len(name) immer. Na dann viel Spaß beim schreiben/generieren der DEF Datei.
_________________ Ist Zeit wirklich Geld?
|
|
safexy 
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: Fr 03.09.04 14:59
Hi,
immerhin mal wieder einen winzigen Schritt weiter... (man weiß woran es liegt  ) )
jemand eine Ahnung wie die Def-Datei für mein Beispiel Programm aussieht?
bye
safexy
|
|
Sprint
      
Beiträge: 849
|
Verfasst: Fr 03.09.04 16:02
| safexy hat folgendes geschrieben: | | jemand eine Ahnung wie die Def-Datei für mein Beispiel Programm aussieht? |
Steht ganz ausführlich in der Hilfe. Ansonsten findest du hier noch ein Beispiel was ich mal geschrieben hab.
DEF Datei für DLL
_________________ Ciao, Sprint.
|
|
safexy 
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: Fr 03.09.04 16:40
Vielen Dank
scheint jetzt zu funktionieren, kann dann nun auch endlich ins WE  )
bye
Florian
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Fr 03.09.04 21:31
Wärst du halt gleich bei Borland geblieben  dann hätte das "__declspec(dllexport) __stdcall" ausgereicht und du wärst den Unterstich sowie das "@" los.
bcc32 version 5.5 gibt es kostenlos zum Download bei Borland, und bcc32 5.6.4 gibt es kostenlos durch den C++BuilderX Personal Edition download.
BC++ hat einige Vorteile, vor allem was Fehlermeldungen angeht. MSVC spuckt so sachen aus wie " '<' gefunden, aber Bezeichner erwartet." So nun macht man sich auf die Suchen und muss feststellen, das das Template vollkommen richtig deklariert ist. Also sucht und denkt man ewig lang, bis man auf die Idee kommt, das Programm mal duch den Borland Compiler zu schicken, und siehe da: "Undefinierter Bezeichner XXX". Nun war klar, es fehlte ein header-include. Und von solchen Falsch-Meldungen gibt es noch mehr. Der Abschuss mit solchen Falsch-Meldungen macht aber immer noch, ungeschlagen, der gcc.
_________________ Ist Zeit wirklich Geld?
|
|
Johannes Maier
      
Beiträge: 173
Win XP
D7 Prof
|
Verfasst: Sa 04.09.04 14:27
Naja, wieso das mit dem stdcall nicht funktioniert, kommt mir auch komisch vor (man siehts ja daran, dass Delphi und VC++ unterschiedliche Funktionen in die DLL schreiben mit depends).
Allerdings solange du die Möglichkeit hast, in der C++ - DLL Veränderungen vornehmen zu können, dann lass __stdcall einfach weg, und schreib das cdecl in die Delphi-Unit, dann braucht man kein DEF-File.
btw: Das mit dem extern "C" {} in .c oder .cpp - Dateien kann man einfach lösen, indem man im DLL-Header immer das hier schreibt:
| Zitat: | #ifdef __cplusplus
extern "C" {
#endif
// DEKLARATIONEN
#ifdef __cplusplus
}
#endif |
So mach ichs immer 
_________________ MfG
Johannes ehem. jbmaier
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Sa 04.09.04 17:28
| Johannes Maier hat folgendes geschrieben: | So mach ichs immer  |
Nicht nur du. 
_________________ Ist Zeit wirklich Geld?
|
|
Johannes Maier
      
Beiträge: 173
Win XP
D7 Prof
|
Verfasst: Sa 04.09.04 18:44
Aber mich würde mal interessieren, woher dieses Problem kommt. Ich meine, __stdcall bzw. stdcall sind doch extra dafür da, um Funktionen in der DLL in einem Standard-Format abzuspeichern, um sie auch in anderen Sprachen nutzbar zu machen, wieso fügt MVC++ dann einen _ und ein @ und eine Zahl hinzu 
_________________ MfG
Johannes ehem. jbmaier
|
|
AndyB
      
Beiträge: 1173
Erhaltene Danke: 14
RAD Studio XE2
|
Verfasst: Sa 04.09.04 21:57
Da gibt es zwei Gründe:
- 1. Man möchhte die .DEF Dateien fördern. Wer C++ programmiert, soll auch mal was tippen.
- 2. Man möchte sich von anderen Compilern abheben. Wenn alle Compiler das so machen würden, hat man doch kein einzigartiges Produkt.
Hier mal ein Link, __stdcall Funktionsnamen von verschienden Compiler angegeben sind.
mywebpage.netscape.c...ongweiwu/stdcall.htm
_________________ Ist Zeit wirklich Geld?
|
|
safexy 
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: So 05.09.04 17:36
Hi,
ich bin auch kein MS-Freund, und umgehe es auch meistens.
An der Uni wo ich bin, wird die Informatik-Fakultät mit MS-Produkten "gesponsert" und daher kann man gar nicht um sie herum kommen.
Vermisse die Produkte von Borland (frage mich schon seit beginn, warum Borland nicht dies auch macht), aber wer weiß was MS wieder für Knepelverträge "ausgehandelt" hat.
Zur DEF-Datei:
Vielleicht hat MS ja ein Patent für dieses und muss der Welt kundtun, das man dies unbedingt braucht und alle anderen es auch in ihre Produkte aufhnehmen und dann Schotter dafür abtreten...
bye
safexy
|
|
safexy 
Hält's aus hier
Beiträge: 11
Win 2000, Linux
D7 Prof
|
Verfasst: So 05.09.04 20:28
Hier die Zusammenfassung zu
dll unter Visual C++ .net erstellen und in einem Delphi-Programm verwenden (Aufruf per stdcall)
das steht in der dlltest.cpp
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| #include "dlltest.h"
extern "C"{ int __declspec(dllexport) __stdcall addieren(int p0, int p2) { return p0+p2; } } |
das steht in der dlltest.h
Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| #if!defined DLLTEST_H #define DLLTEST_H
extern "C" { int __declspec(dllexport) __stdcall addieren(int p0, int p2); } #endif |
das steht in der dlltest.def
Quelltext 1: 2: 3: 4:
| LIBRARY dlladd
EXPORTS addieren; |
Das ist ein Beispielprogramm (in Delphi) welches die DLL dlladd.dll lädt und die funktion addieren aufruft
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: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TSummenFunktion = function(zahl1, zahl2: integer): integer; stdcall; TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; procedure Button1Click(Sender: TObject);
private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
function addieren(zahl1, zahl2: integer; var summe: integer): integer; var SummenFunktion: TSummenFunktion; Handle: THandle; begin Handle:=LoadLibrary(PChar(ExtractFilePath(ParamStr(0))+'dlladd.dll')); if Handle <> 0 then begin @SummenFunktion := GetProcAddress(Handle, 'addieren'); if @SummenFunktion <> nil then begin summe:=SummenFunktion(zahl1, zahl2); result:=1; end else result:=0; FreeLibrary(Handle); end else result:=0; end;
procedure TForm1.Button1Click(Sender: TObject); var summe: integer;
begin if (addieren(strtoint(edit1.Text),strtoint(edit2.Text),summe) <> 0) then begin edit3.Text:=inttostr(summe) end else edit3.Text:= 'Fehler: DLL nicht ladbar!'; end;
end. |
|
|
chille07
Hält's aus hier
Beiträge: 8
Win XP Prof, WIN 2000
D6
|
Verfasst: Di 07.09.04 10:54
OK...
Aber was ist mit der *.DEF
Wohin wird diese gespeichert, und sollte die nicht in irgendein Programm eingebunden werden, oder beim Compilieren verwendet werden?
mfG ©h
|
|