| Autor |
Beitrag |
Atoll
Hält's aus hier
Beiträge: 9
|
Verfasst: Sa 13.10.07 14:03
Hallo liebe delphi gemeinde,
irgendwie ist mir eine sache noch nicht ganz klar.
Folgende funktion wird aus einer c++ dll importiert:
Delphi-Quelltext 1: 2: 3:
| function SfHeader(sf:pointer;index:longint;str:pchar;lines:pointer;var error:integer):Longint; {$IFDEF WIN32} CDECL {$ENDIF}; external 'SPECFILE'; |
Specfile ist ein pointer auf ein file mit mehreren messdaten. index gibt den index des jeweiligen satzes von daten an. Jeder Datensatz hat einen Header. Die funktion liest den header, speichert diesen in 'lines' ab und gibt die anzahl zeilen des headers zurueck. Mein Problem liegt nun in der behandlung von 'lines'. Ich behandle 'lines' als Array of pchar.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var val,i:integer; headerlines : Array of pchar; error:integer; Scanheader : Tstrings;
begin val:=sfheader(sf,20,#0,@headerlines,error); if error<>0 then showmessage(sferror(error)); memo_header.Lines.Clear; for I := 0 to val-1 do memo_header.lines.add(headerlines[i]); end; |
Wenn ich diese Funktion benutze, dann funktioniert alles. Meine Frage ist aber ob die uebergabe an das array headerlines so gut ist. Wird auf diese Weise nicht womoeglich anderer wichtiger speicherbereich ueberschrieben? Die laenge des arrays ist irgendwie ja nicht definiert. Length(headerlines) gibt eine viel zu hohe zahl aus. Gibt es vielleicht eine guenstigere implementation?
Atoll
Moderiert von Christian S.: Code- durch Delphi-Tags ersetzt
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Sa 13.10.07 14:37
Hi Atoll,
Atoll hat folgendes geschrieben: | Folgende funktion wird aus einer c++ dll importiert:
Delphi-Quelltext 1: 2: 3:
| function SfHeader(sf:pointer;index:longint;str:pchar;lines:pointer;var error:integer):Longint; {$IFDEF WIN32} CDECL {$ENDIF}; external 'SPECFILE'; |
Specfile ist ein pointer auf ein file mit mehreren messdaten. index gibt den index des jeweiligen satzes von daten an. Jeder Datensatz hat einen Header. Die funktion liest den header, speichert diesen in 'lines' ab und gibt die anzahl zeilen des headers zurueck. Mein Problem liegt nun in der behandlung von 'lines'. Ich behandle 'lines' als Array of pchar.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var val,i:integer; headerlines : Array of pchar; error:integer; Scanheader : Tstrings;
begin val:=sfheader(sf,20,#0,@headerlines,error); if error<>0 then showmessage(sferror(error)); memo_header.Lines.Clear; for I := 0 to val-1 do memo_header.lines.add(headerlines[i]); end; |
Wenn ich diese Funktion benutze, dann funktioniert alles. Meine Frage ist aber ob die uebergabe an das array headerlines so gut ist. Wird auf diese Weise nicht womoeglich anderer wichtiger speicherbereich ueberschrieben? Die laenge des arrays ist irgendwie ja nicht definiert. Length(headerlines) gibt eine viel zu hohe zahl aus. Gibt es vielleicht eine guenstigere implementation? |
Das sieht erstmal alles korrekt aus. Ob es aber wirklich richtig ist, kann man nur anhand der Doku oder eines C Beispielsources beurteilen. Du koenntest Dir mal im Debugger ansehen, was eigentlich passiert:
Nach der Rueckkehr aus der Funktion muss Headerlines (das von Delphi mit NIL initialisiert werden sollte) einen anderen Wert haben und alle darin gespeicherten Pointer muessen auf Speicher verweisen, der in der DLL alloziert wurde.
Apropos Speicher: Was sagt die Doku ueber die Speicherverwaltung? Wie wird der von dem Array belegte Speicher freigegeben? Oder ist er statisch?
twm
|
|
Atoll 
Hält's aus hier
Beiträge: 9
|
Verfasst: Sa 13.10.07 14:43
Ein weiteres Problem ist halt, dass ich kein Setlength anwenden kann. Dies waere wichtig wenn ich weitere operationen auf dem array durchfuehren will. Ich kann die laenge zwar setzen, aber nach einlesen von ein paar headern hintereinander gibt es dann eine exception 
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Sa 13.10.07 14:54
Atoll hat folgendes geschrieben: | | Ein weiteres Problem ist halt, dass ich kein Setlength anwenden kann. Dies waere wichtig wenn ich weitere operationen auf dem array durchfuehren will. Ich kann die laenge zwar setzen, aber nach einlesen von ein paar headern hintereinander gibt es dann eine exception :( |
Guter Punkt, das hatte ich ganz vergessen: Delphi gibt von dynamischen Arrays belegten Speicher automatisch frei, wenn sie out of scope gehen. Also muss ich mich korrigieren: Es ist doch nicht OK so.
Du musst das Array so deklarieren:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| type THeaderArray: array[0..MAX_LEN] of PChar; var headerlines: ^THeaderLines; begin val := sfheader(sf, 20, #0, @headerlines,error); |
MAX_LEN muss passend gesetzt werden, wobei der Wert eigentlich egal ist, denn Delphi reserviert dafuer keinen Speicher. Das gesamte Array muss nur < 2 GB sein, sonst compiliert die Deklaration nicht.
Wenn Du dann das Array als dynamisches Array weiterverwenden willst (Ich persoenlich wuerde die Strings lieber in eine TStringList kopieren.), musst Du ein neues Array verwenden, mit SetLength dessen Laenge passend setzen und dann in einer For-Schleife oder mittels Move die Pointer kopieren:
Delphi-Quelltext 1: 2: 3: 4: 5:
| var Headers: array of PChar; begin SetLength(Headers, val); Move(HeaderLines^[0], Headers[0], Val * SizeOf(PChar)); |
twm
|
|
Atoll 
Hält's aus hier
Beiträge: 9
|
Verfasst: Sa 13.10.07 15:11
Dann muesste ich doch einfach nach deiner obigen definition eines Arrays einer festen ausreichenden Groesse spaeter setlength anwenden koennen, denn setlength kann soweit ich weiss auf bestehende dynamische arrays angewendet werden oder etwa nicht? Das umkopieren macht fuer mich irgendwie keinen sinn.
Ich arbeite spaeter auch mit tstring weiter weil das memo sonst flackert.
Danke dir
Atoll
Bloedsinn was ich da geschrieben habe, es ist ja dann statisch und nicht dynamisch. Aber auf das dynamische sollte ich an sich setlength anwenden koennen , oder was spricht dagegen
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Sa 13.10.07 16:49
Hi Atoll,
Atoll hat folgendes geschrieben: | Dann muesste ich doch einfach nach deiner obigen definition eines Arrays einer festen ausreichenden Groesse spaeter setlength anwenden koennen, denn setlength kann soweit ich weiss auf bestehende dynamische arrays angewendet werden oder etwa nicht? Das umkopieren macht fuer mich irgendwie keinen sinn.
[...]
Bloedsinn was ich da geschrieben habe, es ist ja dann statisch und nicht dynamisch. Aber auf das dynamische sollte ich an sich setlength anwenden koennen , oder was spricht dagegen |
Das Problem hierbei ist, dass das dynamische Array von der DLL initialisiert wird und nicht von Delphi. Solange es nicht initialisiert ist, ist es lediglich ein NIL-Pointer. Wird es mit SetLength initialisiert, so merkt sich Delphi auch die aktuelle Laenge. Ein weiteres SetLength beachtet die bisherige Laenge und gibt evtl. nicht mehr benoetigten Speicher frei. Dasselbe passiert, wenn die Variable out of scope geht. Nun wurde das Array aber von der DLL initialisiert, die Laenge wurde also nicht geschrieben (wenn ich mich recht erinnere, steht die in den 4 Bytes vor dem 0. Element) sondern enthaelt vermutlich irgendwelchen Speichermuell. Wenn sich Delphi jetzt mit SetLength daran zu schaffen macht, geht das unweigerlich schief. Ebenso, wenn das Array freigegeben werden soll. Und selbst wenn die Laengenangabe korrekt waere: Delphi wuerde dann versuchen, den Speicher seinem internen Speichermanager zuzuteilen. Der Speicher stammt aber aus der DLL und damit aus einem Bereich, den der Delphi-Speichermanager nicht kennt. Spaetestens am Programmende knallt es dann, wenn der Speichermanager seine internen Puffer aufloesen will.
Meiner Ansicht nach kommst Du um ein Kopieren der PChar-Pointer nicht herum. Ausserdem ist nicht gesagt, dass die Pointer gueltig bleiben. Schon der naechste Aufruf einer Funktion in der DLL kann ihren Inhalt ueberschreiben. Deshalb unbedingt nachlesen, wie die Speicherverwaltung der DLL funktioniert. Das muss irgendwo dokumentiert sein. Am Sichersten ist es, wenn Du die PChars schnellstmoeglich in Strings kopierst.
twm
|
|
|