![]() |
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:
Nun sollte der Array ja theoretisch ein Element enthalten.
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; } Ich rufe das ganze in Delphi auf:
Delphi-Quellcode:
Klappt nicht - ContactTypes enthält 0 Elemente, c zeigt ins Nichts.
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; Sieht jemand, was ich falsch mache, oder hat eine Idee, wie ich das ganze stattdessen lösen könnte? Mit liebem Gruß, Tobias |
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:
Funktion außerhalb der DLL
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;
Delphi-Quellcode:
var
MeineElemente: Array of Element; Anzahl : Integer; begin Anzahl := Dll_GibWerte(nil, 0); SetLength(MeineElemente, Anzahl); Dll_GibWerte(@MeineElemente[0], Anzahl); [...] end; |
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. |
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; |
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)
|
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.
|
Re: C++-DLL --> Delphi - dynamischer struct-Array?
Zitat:
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! |
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 |
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; |
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 18:27 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz