Autor Beitrag
doll1
Hält's aus hier
Beiträge: 3

Win7, Linux
Delphi XE, VS2010
BeitragVerfasst: Di 06.03.12 15:45 
Guten Tag,

ich habe recherchiert und weiß, dass schon einiges zum Thema C++-DLL's und Delphi geschrieben wurde. Leider hat es mir nicht sehr geholfen. Das kann auch daran liegen, dass das Thema Delphi an sich für mich relativ neu ist.

Ich habe hier eine C++-DLL, die mir Funktionen zur Kommunikation mit einem externen Gerät zur Verfügung stellt. Die kann ich mit C# z.B. auch wunderbar benutzen. Es gelingt mir auch, die Funktionen von Delphi aus aufzurufen. Es sieht aber so aus, als ob die Parameter-Übergabe nicht funktioniert.

Vom Entwickler der DLL habe ich die Info, dass sie mit 'stdcall' kompiliert ist. Ich habe auch das .h-File.

Ich denke, mein Problem sind die Parameter in der Funktionsdeklaration im Delphi-Quelltext.

Es sollen ein paar Integers und zwei Zeiger auf array of structure übergeben werden. Rückgabewert ist ein String.

Ich habe mal ein paar Code-Schnippsel zusammengesetzt um es zu verdeutlichen.

Die C++-Deklaration sieht so aus:

ausblenden volle Höhe 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:
typedef struct _Scale
{
  int    masterAddress;
  LPWSTR  ipAddress;
  int    txPort;
  int    rxPort;
  LPWSTR  model;
  LPWSTR  display;
  LPWSTR  section;
  int    group;
  LPWSTR  logsPath;
}Scale;

typedef struct _Item 
{
  int    code;
  int    directKey;
  double  price;
  LPWSTR  name;
  int    type;
  int    section;
  LPWSTR  expiryDate;
  int    alterPrice;
  int    number;
  int    priceFactor;
  LPWSTR  textG;
}Item;

extern "C"  // Exportable functions
{
  DIBALSCOP_API LPSTR WINAPI RegistersSend (Scale * myScales, int iNumScales, Register myRegisters[], int numRegisters, int iShowWindow, int iCloseTime);
  DIBALSCOP_API LPSTR WINAPI ItemsSend (Scale * myScales, int numScales, Item * myItems, int numItems, int showWindow, int closeTime);
  DIBALSCOP_API LPSTR WINAPI DataSend (void);
  DIBALSCOP_API int WINAPI ReadRegister (int * iServerHandle, char * sReceiveBuffer, char *scaleIpAddress, int scalePortTx,LPWSTR * pcIpAddress, int pcPortRx, int timeOut, char * pathLogs);
  DIBALSCOP_API int WINAPI CancelReadRegister(int *iServerHandle,  char * pathLogs);
}


Wie gesagt, es werden Zeiger auf Arrays solcher Strukturen erwartet. Mein letzter Versuch in Delphi sieht so aus:

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:
61:
62:
63:
64:
65:
66:
interface

type
  _Scale = record
    masterAddress: integer;
    ipAddress: PChar;
    txPort: integer;
    rxPort: integer;
    model: PChar;
    display: PChar;
    section: PChar;
    group: integer;
    logsPath: PChar;
  end;

type
  _Artikel = record
  code: integer;
  directKey: integer;
  price: double;
  itemName: PChar;
  wheighty: integer;
  section: integer;
  expiryDate: PChar;
  alterPrice: integer;
  plu: integer;
  priceFactor: integer;
  gText: PChar;
  end;

function ItemsSend(myScales: Pointer; numScales: integer; myItems: Pointer; numItems: integer; showWindow: integer; closeTime: integer): PChar; stdcall;
function RegistersSend(myScales: Pointer; numScales: integer; myRegisters: Pointer; numRegisters: integer; showWindow: integer; closeTime: integer): PChar; stdcall;

implementation

function ItemsSend(myScales: Pointer; numScales: integer; myItems: Pointer; numItems: integer; showWindow: integer; closeTime: integer): PChar; external 'Dibalscop.dll';
function RegistersSend(myScales: Pointer; numScales: integer; myRegisters: Pointer; numRegisters: integer; showWindow: integer; closeTime: integer): PChar; external 'Dibalscop.dll';

procedure TBWDForm.ArtikelEintragenAendernButtonClick(Sender: TObject);
var
  ergebnis: string;
  artikelName: widestring;
  myScales: array of _Scale;
  myArticles: array of _Artikel;
begin
  Setlength(myScales, 1);
  ScalesInit(myScales);

  artikelName := widestring(ArtikelNameEdit.Text);

  Setlength(myArticles, 1);
  myArticles[0].code := StrToInt(ArtikelNummerEdit.Text);
  myArticles[0].directKey := StrToInt(ArtikelTasteEdit.Text);
  myArticles[0].price := StrToFloat(ArtikelPreisEdit1.Text);
  myArticles[0].itemName := @artikelName;
  myArticles[0].wheighty := 0;
  myArticles[0].section := 0;
  myArticles[0].expiryDate := '29/02/2012';
  myArticles[0].alterPrice := 0;
  myArticles[0].plu := StrToInt(ArtikelTasteEdit.Text);
  myArticles[0].priceFactor := 0;

  ergebnis := ItemsSend(@myScales, 1, @myArticles, 112);

  ArtikelNameEdit.Text := ergebnis;
end;


Alles, was ich bis jetzt hinbekommen habe, ist ein Kommunikations-Statusfenster, in dem die Einträge, die ich aus meinen C#-Versuchen kenne, fehlen. Dann schmiert das Programm ab. Deswegen vermute ich eine misslungene Parameter-Übergabe.

Sorry, dass ich, kaum registriert, gleich mit sowas komme, aber ich komm da grad so nicht weiter.

Bin für jeden Hinweis dankbar.

Ach ja, ich hoffe, ich habs im richtigen Unterforum gepostet.


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Di 06.03.2012 um 14:58
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Mi 07.03.12 23:18 
Du solltest PACKED RECORD verwenden und PWideChar für LPWSTR (in den Records). Außerdem @myScales[0] übergeben wenn du dynamische Array verwendest.

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit

Für diesen Beitrag haben gedankt: doll1
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 378
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Do 08.03.12 00:24 
Hallo,

vielleicht hast Du ja ein klein wenig Verständnis für Assembler-Code. Dann hilft folgender Weg:

Breakpoint auf den Aufruf einer DLL-Funktion setzten, dann nach Auflaufen auf den Breakpoint das Debug-Fenster CPU-Fenster einschalten und Code schrittweise ausführen.

Dann kannst Du verfolgen, wie der Delphi-Code die Variablen auf den Stack packt und der C++ Code es wieder abholt bzw. nutzt. Stimmt die Datenlänge der enzelnen Daten? (z. B. Integer gibt es als 16 Bit, 32 Bit und 64 Bit). Wird ein Pointer abgelegt oder der Zahlenwert? Stimmt die Reihenfolge? Und schließlich: Wer räumt den Stack am Ende der Funktion auf, die DLL-Funktion oder das aufrufende Programm?

Meine Assemblerzeit liegt mehr als 20 Jahre zurück, dennoch reichen meine Kenntnisse dafür, es sind also wirklich nur Grundkenntnisse nötig. Ich bin jedenfalls bisher auf diesem Weg immer schnell zum Ziel gekommen.

Gruß
GuaAck

Für diesen Beitrag haben gedankt: doll1
doll1 Threadstarter
Hält's aus hier
Beiträge: 3

Win7, Linux
Delphi XE, VS2010
BeitragVerfasst: Do 08.03.12 16:03 
user profile iconuall@ogc hat folgendes geschrieben Zum zitierten Posting springen:
Du solltest PACKED RECORD verwenden und PWideChar für LPWSTR (in den Records). Außerdem @myScales[0] übergeben wenn du dynamische Array verwendest.


Da hab ich wieder was gelernt :) Vielen Dank dafür. Geht!

Jetzt habe ich nur noch das Problem, dass der String, den die Funktion zurückgeben soll, ein nicht lesbarer ist.

ausblenden Quelltext
1:
DIBALSCOP_API LPSTR WINAPI ItemsSend (Scale * myScales, int numScales, Item * myItems, int numItems, int showWindow, int closeTime);					

Wegen dem LPSTR in der Deklaration habe ich die Funktion in Delphi mal als PChar deklariert. Deklariere ich sie gleich als ansistring, schmiert sie mir auch gleich ab.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function ItemsSend(myScales: Pointer; numScales: integer; myItems: Pointer; numItems: integer; showWindow: integer; closeTime: integer): PChar; stdcall;

  ergebnis: PChar;

  ergebnis := ItemsSend(@myScales[0], 1, @myArticles[0], 1110);

  ArtikelNameEdit.Text := ergebnis^; {zeigt mir eine chinesische Glyphe}
  ArtikelNameEdit.Text := ergebnis; {auch}
  ArtikelNameEdit.Text := widestring(ergebnis^); {ebenso}
  ArtikelNameEdit.Text := ansistring(ergebnis^); {gibt mir ein Fragezeichen}
  ArtikelNameEdit.Text := ansistring(ergebnis); {auch}


Wo denke ich da schon wieder falsch?
Ach ja, im Erfolgsfall sollte da einfach 'OK' und im Fehlerfall eine IP-Adresse erscheinen... (Tut es auch, jedenfalls mit C#) :(

user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:
Hallo,

vielleicht hast Du ja ein klein wenig Verständnis für Assembler-Code. Dann hilft folgender Weg:
[...]
Meine Assemblerzeit liegt mehr als 20 Jahre zurück, dennoch reichen meine Kenntnisse dafür, es sind also wirklich nur Grundkenntnisse nötig. Ich bin jedenfalls bisher auf diesem Weg immer schnell zum Ziel gekommen.

Gruß
GuaAck


Ich hab mal ausgiebig in 8052- und 68k-Assembler programmiert. Danke für den Tipp.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 08.03.12 21:16 
Du hast XE, steht in deinem Profil. Dann wäre der passende Typ PAnsiChar.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."

Für diesen Beitrag haben gedankt: doll1
doll1 Threadstarter
Hält's aus hier
Beiträge: 3

Win7, Linux
Delphi XE, VS2010
BeitragVerfasst: Fr 09.03.12 10:32 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Du hast XE, steht in deinem Profil. Dann wäre der passende Typ PAnsiChar.

Wow. Danke, geht. Vielleicht kann ich mich ja doch noch irgendwann mit Delphi anfreunden ;)
Mit dem Forum auf jeden Fall.