Entwickler-Ecke

Windows API - C++ DLL in Delphi ansprechen!?!


Alice - Di 27.05.08 16:14
Titel: C++ DLL in Delphi ansprechen!?!
hi,

ich versuche hier eine *.dll (kommend aus MS C++) anzusprechen, also deren
funktion in delphi zu nutzen:

hier der code aus C++:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
// test.cpp : Definiert die Initialisierungsroutinen für die DLL.
//

#include "stdafx.h"
#include "test.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

# define DLL_EXPORT __declspec(dllexport)

DLL_EXPORT double _stdcall calc( long nubof , char *txtdatei );


und so habe ich es versucht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Button1Click(Sender: TObject);
var
  hLibrary      : HMODULE;
  DiiOpenDevice : procedure ( nubof : integer ; txtdatei: pchar);
begin

    hLibrary :=LoadLibrary('test.dll');

    if (hLibrary <> 0then
     @DiiOpenDevice := GetProcAddress(hLibrary, '?calc@@YGNJPAD@Z');

      if (@DiiOpenDevice <> Nilthen
       DiiOpenDevice(1,'test.txt');

    if (hLibrary <> 0then
     FreeLibrary(hLibrary);

end;


soweit ich sehen kann, wird die routine auch angesprochen
aber:

die übergebenen variablen kommen *verkehrt an, die nubof wir zu ??? (irgend ein num. wert)
und anstatt der test.txt (die durch die dll erstellt werden sollte) lautet die
datei z.b.: d„iBbfgiB

der inhalt der datei, jedoch wird ein ergebniss sein, dass auf ??? = nubof basiert.

wo liegt der fehler?

nebenbei:
ich konnte die routine nur über den namen '?calc@@YGNJPAD@Z' (ersehen in Dependency Walker)
ansprechen, eigentlich sollte sie nur 'calc' lauten.
wieso, ist das so?


cu and thx

alice

Moderiert von user profile iconAXMD: Code- durch C#-Tags ersetzt


Silas - Di 27.05.08 16:33
Titel: Re: C++ DLL in Delphi ansprechen!?!
Hallo,

das Problem besteht darin, dass die Pascal-Aufrufkovention "verkehrt herum" arbeitet, deswegen musst du dieselbe Aufrufkonvention auch in deinem Delphi-Code angeben:


Quelltext
1:
DLL_EXPORT double _stdcall calc( long nubof , char *txtdatei );                    

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.Button1Click(Sender: TObject);
var
  hLibrary      : HMODULE;
  DiiOpenDevice : procedure ( nubof : integer ; txtdatei: pchar); stdcall;
begin

...

end;


Edit: Wegen dem Namen: Das liegt daran, dass C++ (im Gegensatz zu z.B. Pascal und C) die Namen standardmäßig so "verunstaltet", um die zu übergebenden Parameter festzulegen.


Xentar - Di 27.05.08 16:34

Versuchs mal mit

Delphi-Quelltext
1:
  DiiOpenDevice : procedure ( nubof : integer ; txtdatei: pchar); stdcall;                    



Edit: Zu langsam.


Alice - Di 27.05.08 17:25

hi,

@Xentar
@Silas

danke! euch

cu

alice


Alice - Fr 06.06.08 06:08

hi,

eine änderung, es wurde ein callback auf meinen wunsch eingebaut.

siehe:

vorher:

Quelltext
1:
DLL_EXPORT double _stdcall calc( long nubof,char *txtdatei );                    

nun:

Quelltext
1:
2:
DLL_EXPORT typedef void (__stdcall *ShowProgress)(long progress);
DLL_EXPORT void _stdcall calc( long nubof,char * txtdatei,ShowProgress ProFunc);


wie binde ich das ganze nun ein?, also so das ich auf dieses ShowProgress zugreifen kann

cu

alice


uall@ogc - Fr 06.06.08 07:24


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure Progess;
begin
//
end;

calc(..,..,@Progress);


Alice - Fr 06.06.08 08:20

hi

hmm, so komme ich nicht weiter :(


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:
procedure Progess(x:Integer);
begin
 Form1.label1.caption := inttostr(x);
end;

.
.
.

var
  hLibrary      : HMODULE;
  /// ??? ich denke hier fehlt noch etwas? "DLL_EXPORT typedef void (__stdcall *ShowProgress)(long progress); "
  DiiOpenDevice : procedure ( nubofp : LongInt ; txtdatei: pchar; Proz: LongInt); stdcall;
begin

    hLibrary := LoadLibrary('test.dll');

    if (hLibrary <> 0then @DiiOpenDevice := GetProcAddress(hLibrary, '?calc@@YGXJPADP6GXJ@Z@Z');

    if (@DiiOpenDevice <> Nilthen begin
     DiiOpenDevice(12345,pchar('test.txt'),@Progess(Proz));
    end;

    if (hLibrary <> 0then FreeLibrary(hLibrary);


end;


was fehlt hier noch / bzw. ist falsch?

cu

alice


Martok - Fr 06.06.08 09:21

Du müsstest vielleicht irgendwo mal den Pointer übersetzen...


Delphi-Quelltext
1:
2:
3:
4:
type
  TShowProgress = procedure(progress: integer); stdcall{nicht ganz sicher wegen stdcall... müsste aber eigentlich.}
var
  DiiOpenDevice : procedure ( nubofp : LongInt ; txtdatei: pchar; Proz: TShowProgress); stdcall;


Alice - Mo 09.06.08 08:23

hi,

die dll wurde nun mit 2 versch. kompilern kompiliert ,
(die MS dll ist wesentlich scheller, ging aus test ohne callback hervor)
einmal mit borland c++ und MS c++,
das kompilat des borland kopilers funktioniert nun einwandfrei! (thx martok) incl. callback.

die MS dll jedoch nicht, die funktion springt zwar an,
der callback wird genau 2x angesprochen und bringt auch
ein bis dahin ein ergebniss, steigt dann aber aus (access violation)!?

warum? muss wenn ich eine MS dll nutze am folg. code etwas geändert werden
(bis auf den funkt. namen, den beide kompiler jeweils mit einem anderem namen versehen)?:



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:
procedure Progess(x:integer);
begin
 Form.ProgressBar.Position := x; 
end;

.
.
.

procedure TForm1.ButtonClick(Sender: TObject);
type
  TShowProgress = procedure(progress: integer); stdcall;
var
  DiiOpenDevice : procedure ( nubofp : LongInt ; txtdatei: pchar; Proz: TShowProgress); stdcall;
begin


   if borland.Checked then begin
    hLibrary := LoadLibrary('raw.dll'); // borland
    if (hLibrary <> 0then @DiiOpenDevice := GetProcAddress(hLibrary, '@calc$qqslpcpqqsl$v'); //borland
   end;

   if ms.Checked then begin
    hLibrary := LoadLibrary('pi.dll'); // ms
    if (hLibrary <> 0then @DiiOpenDevice := GetProcAddress(hLibrary, '?calc@@YGXJPADP6GXJ@Z@Z');   //ms
   end;

   if (@DiiOpenDevice <> Nilthen begin
     DiiOpenDevice(1024,pchar('test.txt'),@Progess);
   end;

   if (hLibrary <> 0then FreeLibrary(hLibrary);

end;


cu


Lossy eX - Mo 09.06.08 11:32

C++ Komplier versehen die Funktionsnamen mit einer Liste von Paramatern. Anzahl der Variablen und Parametertyp. Ganz komisch codiert. Kann aber je nach Kompiler unterschiedlich sein. Wenn du die Methoden in C++ als extern "C" deklarierst dann werden sie als C Methoden exportiert. Und dann bekommen sie nur den Namen der Methoden aber keine Parameter. Also würden dann nur noch "calc" heißen. Wie folgt sollte genügen.

C++ Header
1:
2:
3:
4:
5:
6:
7:
8:
9:
#ifdef __cplusplus
extern "C" {
#endif

// Funktionsdeklaration

#ifdef __cplusplus
}
#endif


Aufrufkonvention: Wenn du eine DLL ansprichst MUSST du immer auf die richtige Aufrufkonvention achten. Das ist sehr wichtig. C++ arbeitet meines Wissens nach per default mit stdcall. Pascal nicht. Bei stdcall werden die Paramater von Links nach Rechts auf dem Stack übergeben und der Aufgerufene räumt die Paramater wieder weg. Wenn du jetzt einen Pointer auf eine Methode hast die per Stdcall angesprochen wird aber die Parameter anders erwartet, dann kann das natürlich nicht gehen. Deswegen bei DLL besser immer eine Konvention angeben. Und zwar auf beiden Seiten.

Bei dir ist das Problem, dass deine Methode Progess keine Konvention hat. Also stdcall; anhängen.

PS: Außerdem sollte es genügen, wenn du die Methode Progess direkt übergibst. Da explizit den Pointer übergeben zu müssen sollte nicht nötig sein. Bzw ein konstanter String muss auch nicht nach pChar gecastet werden.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure Progess(x:integer); stdcall;
begin
 Form.ProgressBar.Position := x; 
end;

DiiOpenDevice(1024'test.txt', Progess);