Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE (https://www.delphipraxis.net/144882-realloc-c-dll-schlaegt-fehl-bei-aufruf-von-delphi-ide.html)

Mastersargon 18. Dez 2009 18:24


realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Hallo zusammen,

ich verwende eine in C geschriebene DLL, bei der ich Probleme mit einer Funktion habe:

unsigned char* send(const unsigned long int* data_len, const unsigned char* data, unsigned long int* packet_len, unsigned char* packet);

Aufruf in Delphi:

FUNCTION send({CONST} VAR data_len: DWord; {CONST} data: PChar; VAR packet_len: DWord; packet: PChar): PChar cdecl; external 'test.dll' name 'send';

Grundsätzliche Funktionsweise: Es wird ein Bytefeld 'data' der Länge 'data_len' übergeben. Daraus erstellt die Funktion ein mehr als doppelt so langes Bytefeld 'packet' der Länge 'packet_len'. Dafür muß der Speicher in der Funktion natürlich angepasst werden, weshalb das Feld von 'packet', dessen Adresse vorher schon initialisiert wurde, mit 'realloc' verlängert wird. Zunächst betragen die Längen testweise max. 20 Bytes.

Im C-Testprogramm klappt es. In Delphi nur, wenn ich vor dem Aufruf genug Speicher alloziere und das Programm nicht in der IDE starte - ansonsten erscheint nur das CPU-Debuggerfenster und die Reallokation in der DLL schlägt fehl! :glaskugel:

Die Komplementärfunktion 'receive', die ähnlich aufgebaut ist, jedoch ein kürzeres Ergebnis liefert, läuft anstandslos...

Ich verwende Windows XP SP3 und Delphi 7.1 Enterprise. 'Sharemem' oder ähnliches kommt nicht zum Einsatz. Woran könnte es liegen? Warum klappt die Reallokation in der DLL nicht?

Für Ideen oder Lösungsansätze wäre ich dankbar!

Grüße!

Astat 18. Dez 2009 21:24

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Hallo Mastersargon.

Zitat:

Zitat von Mastersargon
unsigned char* send(const unsigned long int* data_len, const unsigned char* data, unsigned long int* packet_len, unsigned char* packet);

Aufruf in Delphi:

FUNCTION send({CONST} VAR data_len: DWord; {CONST} data: PChar; VAR packet_len: DWord; packet: PChar): PChar cdecl; external 'test.dll' name 'send';

Versuchs mal mit folgender Deklaration.

Delphi-Quellcode:
 
  function send(data_len: PLongInt; data: PByte;
    var packet_len: LongInt; var packet: Byte): PByte; cdecl;
      external 'test.dll' name 'send';
lg. Astat

Mastersargon 18. Dez 2009 23:30

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Hallo Astat!

Danke für Deine schnelle Antwort! Dein Vorschlag ist in der Tat eine äquivalente Alternative, nur leider besteht das gleiche Problem fort. Der Char (packet) wird angelegt, dessen Adresse (richtig) übergeben, aber eine Speichervergrößerung zum Char-Reihung scheitert...

Die data_len / packet_len Variablen (ob DWord, LongInt oder LongWord bzw. Zeiger darauf - je nach Aufruf) werden korrekt angenommen und verarbeitet. Die Frage ob Byte oder Char: das ist nur eine Darstellungsfrage.
Zusätzlich habe ich in der C-Funktion dafür gesorgt, daß die Speichergröße von packet um 1 größer als packet_len ist und das letzte Zeichen ein #0 ist, um bei einem typecast mit PChar() nicht auf die Nase zu fallen.
Auch Die Zeigeradressen von packet vor und nach der Reallokation sind gleich, sofern ich vorher schon genügend alloziere und nicht in der IDE starte... Überhaupt frage ich mich, warum mit Start in der IDE solche Probleme entstehen können. Kann es sein, daß Delphi ein "Hineinpfuschen" nicht gerne sieht?

Hier der Ausschnitt der C-Funktion:

Code:

// ...

typedef unsigned char byte; // 8 Bit, unsigned
typedef unsigned short word; // 16 Bit, unsigned
typedef unsigned long int dword; // 32 Bit, unsigned

// ...

// Globale Variable:

dword preamblelen = 6; // Zum Beispiel...

// ...

__declspec (dllexport) byte* send(const dword* data_len, const byte* data, dword* packet_len, byte* packet) {
  dword lc;
  word coded_;
  word* coded = &coded_;
  *packet_len = preamblelen + 1 + *data_len * 2;
  // 3 Zeilen für Debuging   
  // char msg[100];
  // sprintf(msg, "dl = %d, pl = %d, adr(d) = 0x%x, adr(p) = 0x%x, d = %s, p = %s", *data_len, *packet_len, data, packet, data, packet);
  // MessageBox (0, msg, "2", MB_ICONINFORMATION);
  packet = (byte*) realloc(packet, (*packet_len + 1) * sizeof(byte)); // <---------------- Dies ist die kritische Stelle
  if (packet == NULL) {
    // MessageBox (0, "Reallocation of Memory failed!\n", "Error!", MB_ICONINFORMATION);
    exit(EXIT_FAILURE);
  }
  else { 
    // ... // soweit kommt es nicht mehr...
  }
}
Dieser Code (Delphi Testprogramm) funktioniert - wie gesagt, jedoch nicht wenn er in der IDE gestartet wird:

Delphi-Quellcode:

// Globale Variablen:

  dl, pl  : LongWord;
  ds, ps  : ShortString;
  dc, pc  : Char;
  dpc, ppc : PChar;
  preamble_len: LongWord; // Im Quelltext mit einem konstanten Wert initialisiert

// ...

PROCEDURE TForm1.Button1Click(Sender: TObject);
BEGIN
  dl := Length(Edit1.Text);   // In Edit 1 steht: 'Edit1'
  pl := preamble_len + 2 + dl * 2; // Precalculate size of packet length
  ds := Edit1.Text;
  dpc := PChar(ds);
  ppc := AllocMem(pl); // Wenn ich hier nicht bereits den Speicher alloziere, klappt es nicht :(
  //ppc := dummy(dl, dpc, pl, ppc);
  ppc := dcpml_send(dl, dpc, pl, ppc); // bei der alten Deklaration, siehe 1. Post oben
  ps := String(ppc);
  Edit2.Text := ps;
END;
Übrigens habe ich die fragliche Funktion im Prinzip (als dummy-Funktion) in Delphi nachgebildet und dort auch ReallocMem genutzt. Da klappt es ohne weiteres...

Jetzt müßte ich nur noch herausfinden, wie es ohne vorherige Allokation in Delphi geht...

Grüße!

Astat 19. Dez 2009 00:27

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Hallo Mastersargon.

Wenn packet hier
Delphi-Quellcode:
ppc := nil; //-- AllocMem(pl); // Wenn ich hier nicht bereits den Speicher alloziere, klappt es nicht :(
zugewiesen wird, sollte ja realloc wie malloc arbeiten.

Delphi-Quellcode:
packet = (byte*) realloc(packet, (*packet_len + 1) * sizeof(byte));
Wird dies, bei (ppc := nil;) noch korrekt in der c-Library allociert, oder kracht es hier wieder?

Wenn Ja, was passiert wenn Du folgendes versuchst.

Delphi-Quellcode:
packet = (byte*) realloc(packet, 100);
ist dann packet allocation möglich?

lg. Astat

Mastersargon 19. Dez 2009 00:50

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Vielen herzlichen Dank, Astat!

Der Tipp mit dem Initialisieren des Zeigers mit nil hat's gebracht! Jetzt läuft alles super! :bounce2:

Hätte zwar mit einem bereits auf eine konkrete Speicherstelle verweisenden Zeiger auch funktionieren sollen/müssen(?) - wie eben in C - aber das ist doch auch eine saubere Lösung!


Nochmals vielen Dank!

P.S.: 07811109910403210511511603210010510103210810111612 21161010320901011051081010321101050991041160321031 01115099104114105101098101110033

sx2008 19. Dez 2009 01:00

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Zitat:

Zitat von Mastersargon
Dafür muß der Speicher in der Funktion natürlich angepasst werden, weshalb das Feld von 'packet', dessen Adresse vorher schon initialisiert wurde, mit 'realloc' verlängert wird. Zunächst betragen die Längen testweise max. 20 Bytes.

Warum so umständlich mit realloc?
Bei Benützung von DLLs ist es üblich (und notwendig) den Speicher vorher zu reservieren.
Delphi-Quellcode:
var
  data:string;
  data_len : integer;
  packet : string;
  packet_len : integer;
begin
  data := 'Testdaten...x';
  data_len := Length(data);
  packet_len := 2 * data_len; // es wird die doppelte Länge erwartet
  SetLength(packet, packet_len); // Speicher reservieren
  send(data_len, PChar(data), packet_len, PChar(packet)); // funktion aufrufen
  // Ergebnis ist in packet
Wenn man vorher nicht weiss, wieviel Speicher das Ziel benötigt ist es üblich, die Funktion zweimal aufzurufen.
Diese Technik wird in der Window API öfter verwendet.
Die DLL-Funktion berechnet dann lediglich die benötigte Länge und schreibt natürlich nicht auf den Zielpuffer (der ja nil ist).
Delphi-Quellcode:
begin
  data := 'Testdaten...x';
  data_len := Length(data);
  packet_len := 0;
  send(data_len, PChar(data), packet_len, nil); // funktion aufrufen, Zielpuffer ist nil
  // die benötigte Länge steht jetzt in packet_len
  SetLength(packet, packet_len); // Speicher reservieren
  send(data_len, PChar(data), packet_len, PChar(packet)); // nochmal aufrufen, diesmal mit Zielpuffer und korrekter Länge
Ich verwende sehr gerne AnsiStrings als Puffer; das ist sehr bequem und Delphi-like.

Mastersargon 19. Dez 2009 01:18

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Hallo sx2008,

die Methode mit dem zweifachen Aufruf war mir zwar auch bekannt, ich habe mich wohl waber zu sehr auf den Delphi-Code fixiert und nicht an ein Redesign der C-Funktion gedacht. :-D
Reallokation funktioniert jetzt ja aber auch.

Danke für Deinen Beitrag!

Muetze1 19. Dez 2009 01:46

Re: realloc in C-Dll schlägt fehl bei Aufruf von Delphi IDE
 
Man sollte sich vor allem ins Gedächtnis rufen, dass der Speichermanager in der DLL nichts vom Speichermanager deiner Delphi Anwendung weiß. Von daher kann der DLL Speichermanager nicht einen Block realloziieren welcher nicht von ihm verwaltet wird (sondern vom Delphi Speichermanager). Auch wenn er es vllt. frecher weise trotzdem macht hilft es keinem, da dann nach der Rückkehr der Delphi Speichermanager nichts mit der neuen Adresse anfangen kann, da diese nicht von ihm verwaltet wird.

Schon allein aufgrund dieses Hintergrundes muss eine strikte Trennung vollzogen werden und dann leuchtet einem auch der Zwang zu der Verwaltung wie in der WinAPI (exklusive ShellAPI) genutzt ein.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:52 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz