Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   C++-DLL --> Delphi - dynamischer struct-Array? (https://www.delphipraxis.net/94362-c-dll-delphi-dynamischer-struct-array.html)

Legato 20. Jun 2007 14:18


C++-DLL --> Delphi - dynamischer struct-Array?
 
Huhu,

ich habe eine Funktion in C++, die einen Pointer auf eine struct entgegennimmt und diese dynamisch füllen soll. Die Anzahl der Elemente wird erst in der Funktion selber bekannt. Hier eine reduzierte Testversion davon:
Code:
struct ContactType
{
//   __int64 id;
//   char *name;
   int protocol;
};

extern "C" __declspec(dllexport) int ContactManagerSOAP_GetContactTypes(ContactType* result)
{
   int count = 1;
   result = new ContactType[count];
   for (int i = 0; i <= count; i++) {
      result[i].protocol = 3;
   }
   return count;
}
Nun sollte der Array ja theoretisch ein Element enthalten.

Ich rufe das ganze in Delphi auf:

Delphi-Quellcode:
type
   ContactManagerSOAP_ContactType = record
//      a: INT64;
//      b: PChar;
      c: Integer;
   end;
  ContactManagerSOAP_ContactTypeArray = array of ContactManagerSOAP_ContactType;

function _ContactManagerSOAP_GetContactTypes(var result: ContactManagerSOAP_ContactTypeArray) : Integer; cdecl; external '../DLL/ObjectWebservice.dll';

procedure TDataDisplayForm.btnGetContactTypesClick(Sender: TObject);
var
  ContactTypes: ContactManagerSOAP_ContactTypeArray;
  count: Integer;
begin
//  SetLength(ContactTypes, 1);
  count := _ContactManagerSOAP_GetContactTypes(ContactTypes);
  ShowMessage(IntToStr(ContactTypes[0].c));
end;
Klappt nicht - ContactTypes enthält 0 Elemente, c zeigt ins Nichts.

Sieht jemand, was ich falsch mache, oder hat eine Idee, wie ich das ganze stattdessen lösen könnte?

Mit liebem Gruß,
Tobias

SirThornberry 20. Jun 2007 14:22

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
aua, ein dynamisches Delphiarray ist was ganz anderes als was deine C-Funktion erwartet. Ein Dynamisches Array in Delphi ist wie ein dynamischer String in Delphi nur ein Pointer der auf die Daten und noch einige andere Infos zeigt.

Ich hoffe dir ist auch bewusst das deine DLL den Speicher freigeben muss wenn sie ihn angefordert hat. Laut deinem bisherigen Quelltext würdest du in der DLL speicher anfordern der nie frei gegeben wird.

Üblich ist folgendes:

Funktion in der DLL
Delphi-Quellcode:
function GibWerte(Speicher, AnzahlDerSpeicherElemente): Integer;
begin
  AnzahlderVorhandenenElemente;
  //in Speicher nur soviel schreiben wie rein passt und vorhanden ist
  //Wenn AnzahlderVorhandenenElemente also größer ist als AnzahlDerSpeicherElemente darfst du trotzdem nur AnzahlDerSpeicherElemente schreiben
  //ist AnzahlderVorhandenenElemente kleiner oder gleich AnzahlDerSpeicherElemente kannst du natürlich AnzahlderVorhandenenElemente schreiben.
  result := AnzahlDerVorhandenenElemente;
end;
Funktion außerhalb der DLL
Delphi-Quellcode:
var
  MeineElemente: Array of Element;
  Anzahl      : Integer;
begin
  Anzahl := Dll_GibWerte(nil, 0);
  SetLength(MeineElemente, Anzahl);
  Dll_GibWerte(@MeineElemente[0], Anzahl);
  [...]
end;

Robert Marquardt 20. Jun 2007 14:34

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
Es ist immer wieder erschreckend wie wenig Ahnung die Leute haben (heute habe ich Lust zum Lamentieren).
"array of" ist Delphi-spezifisch. Ein new von C++ aus erzeugt kein "array of". Das API der DLL ist daher unbrauchbar.
Das von new erzeugte Objekt hat keine Laengeninformation, die Delphi zugreifen koennte.

Die uebliche Loesung ist das man erst per API-Funktion fragt wie viele Elemente denn kommen und dann einen Puffer dieser Groesse bereitstellt (hier von Delphi aus) und ihn dann ausfuellen laesst. SetupDiGetDeviceInterfaceDetail ist z. B. so eine Funktion bei Windows.

Will man in einer DLL dynamisch alloziierte Dinge zurueckliefern, so muss man immer eine Dealloziierungsfunktion bereitstellen. Es arbeiten ueblicherweise zwei Memorymanager. Einer im Hauptprogramm und einer in der DLL. Gibt man speicher vom einen im anderen frei, so sind schnell beide Memorypools korrupt und das Programm stuerzt ab. Man kann natuerlich mit einer win32-funktion alloziieren und freigeben, aber das ist meist ineffizient.

ste_ett 20. Jun 2007 14:43

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
Änderungen:

1) "packed record" statt "record", da die DLL aus der C-Welt kommt
2) Definition und Deklaration eines typisierten Pointers ("PContactManagerSOAP_ContactType")
3) Funktionsdeklaration angepasst


Beim Aufrufen der Funktion wird in der DLL ein Array erstellt/umkopiert/etc, so dass die Variable auf das erste Array-Element zeigt.
Wie oben schon geschrieben, darf man kein "array of" nutzen!

ContactType zeigt auf das erste Element im Array. Anhand des Rückgabewertes (Name: count) kennt man die Anzahl der Elemente im Array und kann diese In eienr Schleife durchlaufen, in dem man den typisierten Pointer bei jedem Durchlauf weiterschiebt. :)



Delphi-Quellcode:
type
    PContactManagerSOAP_ContactType = ^ContactManagerSOAP_ContactType;
   ContactManagerSOAP_ContactType = packed record
//      a: INT64;
//      b: PChar;
      c: Integer;
   end;

function _ContactManagerSOAP_GetContactTypes(result: PContactManagerSOAP_ContactType) : Integer; cdecl; external '../DLL/ObjectWebservice.dll';

implementation

{$R *.dfm}

procedure TDataDisplayForm.btnGetContactTypesClick(Sender: TObject);
var
  ContactTypes: PContactManagerSOAP_ContactType;
  i, count: Integer;
begin
  count := _ContactManagerSOAP_GetContactTypes(ContactTypes);

  for i := 0 to count -1 do
  begin
    ShowMessage(IntToStr(ContactTypes^.c));
    Inc(ContactTypes);
  end;

// TODO:
// der DLL mitteilen, dass der Speicher wieder freigegeben werden kann
end;

SirThornberry 20. Jun 2007 14:46

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
wer gibt denn in dem Fall jetzt den Speicher frei? Ist keine gut durchdachte Variante (ist eben an die Ursprüngliche angelehnt). Es hat schon seinen Sinn das die win32-api das anders handelt. (siehe das von mir und Robert)

Robert Marquardt 20. Jun 2007 14:54

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
Erstens stammt deine DLL aus der C++ Welt und zweitens hat packed nichts damit zu tun. Wie bei Delphi wird das Alignment von Strukturen per Compilerswitch oder Pragma in den Sourcen eingestellt. Das C++ von Microsoft hat weitestgehend die gleichen Defaults wie Delphi beim Alignment. Man weiss aber nie wie es in einer fremden DLL eingestellt ist. Das muss dokumentiert werden.

ste_ett 20. Jun 2007 14:55

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
Zitat:

Zitat von SirThornberry
wer gibt denn in dem Fall jetzt den Speicher frei? Ist keine gut durchdachte Variante (ist eben an die Ursprüngliche angelehnt). Es hat schon seinen Sinn das die win32-api das anders handelt. (siehe das von mir und Robert)

Das ist klar, aber welche Variante ist wahrscheinlicher?

1) Die DLL wird umprogrammiert.

2) Man muss es nehmen wie es ist.

? :D

Freigeben des Speichers muss noch erledigt werden, s. Kommentar im Code.


Ich persönlich finde die Vorgehensweise bei der Win32-API auch bei Weitem besser!

Legato 20. Jun 2007 15:00

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
Hallo ihr,

vielen Dank - für beide Varianten.

Deine Methode funktioniert toll, SirThornberry - nur der doppelte Aufruf ist unschön: Meine Funktion ruft eine Funktion eines Webservice auf, und das müsste ich dann entweder doppelt machen oder zwischenspeichern.

Robert, dann wirst du vermutlich noch viel bei mir zu lamentieren haben - ich programmiere erst seit ungefähr 2 Monaten Delphi (und das ist auch das erste Mal, dass ich eine DLL baue, von daher wusste ich noch nicht mal, wo es genau scheitert). Find es angesichts dieser Zeit noch nicht besonders erschreckend, schließlich lerne ich noch. Übrigens danke, dass du trotzdem geantwortet hattest. :)

Ah, und zu letzterem - meine DLL, kann sie frei ändern. Von daher muss ich sie nicht "nehmen wie sie ist". ;)

Mit liebem Gruß,
Tobias

SirThornberry 20. Jun 2007 15:06

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
ok, doppelt aufrufen musst du nicht unbedingt, du kannst auch gleich genügend speicher mitgeben.
Delphi-Quellcode:
var
  MeineElemente: Array of Element;
  Anzahl      : Integer;
begin
  Anzahl := 1000; //Wie groß die Zahl ist weißt nur du, ich weiß nicht wieviel werte deine dll maximal liefert
  SetLength(MeineElemente, Anzahl);
  Anzahl := Dll_GibWerte(@MeineElemente[0], Anzahl);
  //Prüfen ob der Speicher gereicht hat, wenn nicht machen wir das ganze diesmal mit der richtigen Anzahl
  if (Anzahl < Length(MeineElemente) then
  begin
    SetLength(MeineElemente, Anzahl);
    Dll_GibWerte(@MeineElemente[0], Anzahl);
  end;
  [...]
end;

Legato 20. Jun 2007 16:29

Re: C++-DLL --> Delphi - dynamischer struct-Array?
 
Huhu SirThornberry,

vielen Dank für deine Hilfe. Die Idee ist gut. :)

Gruß,
Tobias


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:51 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