Entwickler-Ecke
Dateizugriff - Probleme mit ddl aus Visual C++ unter delphi 7
safexy - Fr 03.09.04 03:26
Titel: Probleme mit ddl aus Visual C++ unter delphi 7
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:
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: 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 - 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.
AndyB - 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".
safexy - 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 - 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.
safexy - 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 - 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. :)
AndyB - Fr 03.09.04 14:17
| safexy hat folgendes geschrieben: |
| noch jemand eine Idee? |
Nimm einen gescheiten Compiler (Borland C++ :wink: ).
| 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.
AndyB - 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.
safexy - Fr 03.09.04 14:59
Hi,
immerhin mal wieder einen winzigen Schritt weiter... (man weiß woran es liegt :o) )
jemand eine Ahnung wie die Def-Datei für mein Beispiel Programm aussieht?
bye
safexy
safexy - Fr 03.09.04 16:40
Vielen Dank
scheint jetzt zu funktionieren, kann dann nun auch endlich ins WE :o)
bye
Florian
AndyB - Fr 03.09.04 21:31
Wärst du halt gleich bei Borland geblieben :wink: 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.
Johannes Maier - 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 ;)
AndyB - Sa 04.09.04 17:28
| Johannes Maier hat folgendes geschrieben: |
| So mach ichs immer ;) |
Nicht nur du. :wink:
Johannes Maier - Sa 04.09.04 18:44
:D
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 :?:
AndyB - 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.
:roll:
Hier mal ein Link, __stdcall Funktionsnamen von verschienden Compiler angegeben sind.
http://mywebpage.netscape.com/yongweiwu/stdcall.htm
safexy - 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 - 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
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: 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 - 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
safexy - Di 07.09.04 12:13
| chille07 hat folgendes geschrieben: |
OK...
Aber was ist mit der *.DEF
|
Hi,
wenn du die Def-Datei schon geschrieben hast, dann "vorhandenes Element hinzufügen",
wenn nicht, dann "neues Element hinzufügen" (dort dan DEF-DAtei auswählen, unter CODE).
nun klar?
bye
safexy
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!