Autor Beitrag
safexy
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: 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
ausblenden 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

ausblenden Quelltext
1:
2:
3:
4:
#if!defined DLLTEST_H
#define DLLTEST_H
__declspec(dllexport) int addiere(int zahl1, int zahl2);
#endif



Delphi-Source:
ausblenden volle Höhe 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

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 849



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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 Threadstarter
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: 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
ausblenden Delphi-Quelltext
1:
2:
type
  TSummenFunktion = function(zahl1, zahl2: integer): integer; stdcall;


in

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: Fr 03.09.04 12:14 
Mach mal ein extern "C" {} um deine Funktion:

ausblenden 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.

_________________
Ist Zeit wirklich Geld?
safexy Threadstarter
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 849



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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.

_________________
Ist Zeit wirklich Geld?
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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 Threadstarter
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 849



BeitragVerfasst: 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 Threadstarter
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: Fr 03.09.04 16:40 
Vielen Dank

scheint jetzt zu funktionieren, kann dann nun auch endlich ins WE :o)

bye

Florian
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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.

_________________
Ist Zeit wirklich Geld?
Johannes Maier
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 173

Win XP
D7 Prof
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: Sa 04.09.04 17:28 
Johannes Maier hat folgendes geschrieben:
So mach ichs immer ;)

Nicht nur du. :wink:

_________________
Ist Zeit wirklich Geld?
Johannes Maier
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 173

Win XP
D7 Prof
BeitragVerfasst: 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 :?:

_________________
MfG
Johannes ehem. jbmaier
AndyB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1173
Erhaltene Danke: 14


RAD Studio XE2
BeitragVerfasst: 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.
mywebpage.netscape.c...ongweiwu/stdcall.htm

_________________
Ist Zeit wirklich Geld?
safexy Threadstarter
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: 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 Threadstarter
Hält's aus hier
Beiträge: 11

Win 2000, Linux
D7 Prof
BeitragVerfasst: 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
ausblenden 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
ausblenden 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
ausblenden 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
ausblenden volle Höhe 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
Hält's aus hier
Beiträge: 8

Win XP Prof, WIN 2000
D6
BeitragVerfasst: 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