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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  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) <> 0then
  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 user profile iconKlabautermann: 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 user profile iconKlabautermann: 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


Sprint - 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 [http://www.softgames.de/forum/viewtopic.php?t=24917&highlight=def#132694]


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:
: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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  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) <> 0then
  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