Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Array über Schleife ausfüllen (https://www.delphipraxis.net/206950-array-ueber-schleife-ausfuellen.html)

haentschman 11. Feb 2021 14:01

Delphi-Version: 10.1 Berlin

Array über Schleife ausfüllen
 
Hallo...8-)

Ich möchte ein dynamisches Array über eine Schleife füllen. Für die Demo wird die Schleifenvariable als String in den Parameter geschrieben.
Delphi-Quellcode:
function TGhostscript.PDFMerge(FileName: string; FileList: TStrings): Boolean;
var
  Parameters: array of PAnsiChar;
  InitError: Integer;

  procedure CreateMergeFiles;
  var
    I: Integer;
  begin
    for I := 0 to FileList.Count - 1 do
    begin
      Parameters[I + 6] := PAnsiChar(IntToStr(I));//PAnsiChar(AnsiString(FileList[I]));
    end;
  end;

begin
  if FDLLHandle = 0 then
  begin
    if not LoadDLL(FDLLPath) then
    begin
      Result := False;
      Exit;
    end;
  end;
  try
    SetLength(Parameters, FileList.Count + 6);

    Parameters[0] := '';
    Parameters[1] := '-dNOPAUSE';
    Parameters[2] := '-dBATCH';
    Parameters[3] := '-dPDFSETTINGS=/ebook';
    Parameters[4] := '-sDEVICE=pdfwrite';
    Parameters[5] := PAnsiChar(AnsiString('-sOutputFile=' + FileName));

//    Parameters[6] := PAnsiChar(AnsiString(FileList[0]));
//    Parameters[7] := PAnsiChar(AnsiString(FileList[1]));
//    Parameters[8] := PAnsiChar(AnsiString(FileList[2]));
//    Parameters[9] := PAnsiChar(AnsiString(FileList[3]));

    CreateMergeFiles;
Ergebnis nach jedem Schleifendurchlauf:
1:
[6] = 0
[7] = nil
[8] = nil
[9] = nil
I = 0

2:
[6] = 1
[7] = 1
[8] = nil
[9] = nil
I = 1

3:
[6] = 2
[7] = 2
[8] = 2
[9] = nil
I = 2

3:
[6] = 3
[7] = 3
[8] = 3
[9] = 3
I = 3

:shock:

Wenn ich die Parameter hard codiere (auskommentierter Text):
[6] = 0
[7] = 1
[8] = 2
[9] = 3

Erleuchtet mich mal. Ist normalerweise das 1*1 ... Arrays. :roll:

Was ich schon probiert habe (ohne Erfolg):
* Index des Parameters in der Schleife extern berechnet
* Mit jedem Schleifendurchlauf Array um 1 vergrößert
* Schleife rückwärts
* Parameter als private Variable
* Übergabe als var Parameter an CreateMergeFiles
* Optimierung EIN/AUS
...

PS: Einen Delphi Entwickler habe ich schon geschockt...:P Er konnte es auch nicht verstehen!

Gausi 11. Feb 2021 14:16

AW: Array über Schleife ausfüllen
 
Nutzt du eine sehr alte Delphi-Version (vor D2009), oder eine neuere? Denn in den neueren Versionen ist diese Zeile das Problem, denke ich:
Delphi-Quellcode:
Parameters[I + 6] := PAnsiChar(IntToStr(I));
IntToStr liefert einen Unicode-String, mit zwei Byte pro Zeichen, wovon jedes zweite oft gleich 0 ist (zumindest bei Zahlen ist das der Fall). Ein Cast auf PAnsiChar bewirkt dann, dass der String nach dem ersten Zeichen zu Ende ist - das zweite Byte ist dann der Nullterminator.

Mit
Delphi-Quellcode:
PAnsiChar(AnsiString(IntToStr(I)))
sollte es klappen.

Edit: Ok, nach dem zweiten Lesen ist das keine direkte Antwort auf die Frage, aber vielleicht liegt da ja eine Nebenwirkung der Casts...

haentschman 11. Feb 2021 14:32

AW: Array über Schleife ausfüllen
 
Danke...:P

BERLIN

Zitat:

Mit PAnsiChar(AnsiString(IntToStr(I))) sollte es klappen.
...hast Recht, aber die Werte sind immer gleich obwohl I sich geändert hat.
PS: PAnsiChar(AnsiString(IntToStr(I))) ändert nichts an der Tatsache. :cry:

Es schaut aus, daß die Parameter[6 + x] einen gleichen "Pointer" hätten...was aber nicht der Realität entpricht. :?

Die Frage ist: Wo liegt der Unterschied zwischen dem direktem Zuweisen und der Schleife...:gruebel:

Uwe Raabe 11. Feb 2021 14:45

AW: Array über Schleife ausfüllen
 
Nur so ne Idee: Unterschied zwischen
Delphi-Quellcode:
Parameters[I + 6] := PAnsiChar(IntToStr(I));
und
Delphi-Quellcode:
Parameters[6] := PAnsiChar(AnsiString(FileList[0]));
ist, dass IntToStr einen String zurückgibt, dessen Scope nur temporär ist. Gleiches gilt für die Konvertierung über AnsiString. Wenn die PAnsiChar gültig bleiben sollen, dann musst du den Speicher dafür selbst bereitstellen.

himitsu 11. Feb 2021 15:04

AW: Array über Schleife ausfüllen
 
Nein, mit PChar auf einen veränderlichen String geht sowas garnicht.

Delphi nutzt für das Result dieses IntToStr-Aufrufs (
Delphi-Quellcode:
PAnsiChar(IntToStr(I))
) die selbe Variable, denn es ist in der Schleife auch der selbe Code :zwinker:, also im nächsten Durchlauf sind somit die vorherigen Pointer ungültig.
Selbes gilt in Schleifen für alle Funktionsaufrufe und Casts.

Und du kannst froh sein, dass hier der neue String im nächsten Durchlauf zufällig auf der selben Speicheradresse gelandet ist, womit die alten PChar rein zufällig wieder auf "diesen" String zeigten ... der neue String-Variableninhalt hätte aber auch genauso gut sonstwo landen können.


Man kann nun z.B. mit Sowas wie Delphi-Referenz durchsuchenNewStr arbeiten,
oder vor dem PChar/PAnsiChar muß der String irgendwo "sicher" gespeichert werden (RefCount größer 1)
z.B. durch eine "dauerhafte" Kopie der Strings in einer TStringList oder einem TArray<string>, mindestens so lange wie diese PChar's benötigt werden.
Und wer bissl verrückt mutig ist, der könnte auch die Referenzzählung kurz anheben (+1) und am Ende über das PChar-Array und einen String-Cast die Referenzzählung aller Zeiger wieder zurücksetzen (-1).


Bzw., da hier ANSI (warum nicht UTF-8 ?) nötig ist, ein TArray<AnsiString> / TArray<UTF8String> oder eine TAnsiStringList, zum Speichern.



PS: Wenn dieses PChar-Array "Parameters" nur als "Ausgabe" genutzt wird, dann darf man es auch gern als AnsiString-Array deklarieren.
Einer DLL-Funktion ist es egal, da Delphi-Strings intern auch die Merkmale des PChars enthalten (Zeiger zeigt auf das erste Char und am Ende folgt eine #0 ... genauer sind es hier sogar zwei #0#0).

haentschman 11. Feb 2021 15:06

AW: Array über Schleife ausfüllen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Wenn die PAnsiChar gültig bleiben sollen, dann musst du den Speicher dafür selbst bereitstellen.
:gruebel: Ich weise doch nur einen String dem Parameter zu?
Zitat:

dass IntToStr einen String zurückgibt, dessen Scope nur temporär ist.
Test ???:
Delphi-Quellcode:
procedure CreateMergeFiles;
  var
    A: Integer;
    B: Integer;
    I: Integer;
    Temp: string;
  begin
    A := 10;
    B := -1;
    for I := 0 to FileList.Count - 1 do
    begin
      Inc(A);
      Inc(B);
      Temp := IntToStr(A);
      FParameters[B + 6] := PAnsiChar(AnsiString(Temp));
    end;
  end;
Zitat:

dass IntToStr einen String zurückgibt, dessen Scope nur temporär ist.
Zitat:

z.B. durch eine "dauerhafte" Kopie der Strings in einer TStringList
Gut. Aber die Strings in der FileListe sind doch seperate Pointer auf die Strings...oder? Trotzdem sind die Parameter falsch...

haentschman 11. Feb 2021 15:21

AW: Array über Schleife ausfüllen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Mit gefüllter FileList:

Zitat:

Wenn dieses PChar-Array "Parameters" nur als "Ausgabe" genutzt wird
wegen:
Delphi-Quellcode:
InitError := FGsApiInitWithArgs(FGsInstance, Length(FParameters), FParameters);
...er will array of PAnsiChar

himitsu 11. Feb 2021 15:26

AW: Array über Schleife ausfüllen
 
Zitat:

Ich weise doch nur einen String dem Parameter zu?
siehe mein letzter Post.

Nein, du weißt einen "anderen" String (nach Funktionsaufruf IntStStr(S) oder Typecast ala Ansistring(S) ) als PChar zu.
Und im nächsten SchleifenDurchlauf nochmal genau die selben Variablen, wodurch ihr vorherriger Inhalt überschrieben wird und deine alten Zeiger somit ins Nirvana zeigen.


Zitat:

er will array of PAnsiChar
egal ... darfst gern ein Array-of-AnsiString in ein Array-of-PAnsiChar casten, oder schreib bei dir den Funktionsheader um, damit man ein Array-of-AnsiString reingeben kann. (der DLL ist es egal, wenn es es nur ein Lesezugriff ist)

PS: genau sowas "ändere" ich bei Header-Imports gern mal, damit man möglichs mit Delphi-Typen arbeiten kann und eben nicht unbedingt mit PChars rumhantieren muß.

haentschman 11. Feb 2021 15:34

AW: Array über Schleife ausfüllen
 
Zitat:

siehe mein letzter Post.
Values [6-9] aus TStringList/TStrings...:lol:

himitsu 11. Feb 2021 15:43

AW: Array über Schleife ausfüllen
 
Und was hatte ich zum AnsiString-Cast gesagt?

Delphi-Quellcode:
FParameters[B + 6] := PAnsiChar(AnsiString(FileList[I]));
// Delphi macht daraus ein
ImpliziteAnsiStringVariable := AnsiString(FileList[I]);
FParameters[B + 6] := PAnsiChar(ImpliziteAnsiStringVariable);
Also ist das Selbe, wie mit deiner expliziten Temp-Variable.
Delphi-Quellcode:
Temp := IntToStr(A);
FParameters[B + 6] := PAnsiChar(AnsiString(Temp));
// wird zu
Temp := IntToStr(A);
ImpliziteAnsiStringVariable := AnsiString(Temp);
FParameters[B + 6] := PAnsiChar(ImpliziteAnsiStringVariable);
Also in der Schleife sind alle vorherrigen PAnsiChar ungültig, weil sie auf einen nicht mehr existierenden String-Inahlt zeigen, da die "selbe" Variable im nächsten Durchlauf überschieben wird.
Und das Schlimmste, direkt nach Verlassen der CreateMergeFiles sind auch noch die kompletten Variablen Temp und ImpliziteAnsiStringVariable futsch, da sie am Funktionsende freigegeben wurden, womit dann auch noch der letzte PAnsiChar ungültig ist.


Da eine TStringList (und nur diese ... es darf nicht auf andere TStrings bezogen werden, wie z.B. TMemoStrings eines TMemo.Lines) intern jede Zeile/Strings als einzelnen "String" speichert, nicht wie z.B. ein Memo nur den kompletten Text speichert und beim Zugriff auf .Strings[] die Zeile als neuer String rauskopiert wird.
Daher kann man auch "längerfristig" die Zeilen einer TStringList auch als PChar speichern (so lange dazwischen eben keine weiteren Funktionen/Casts sind, welche einen "zusätzlichen" String generieren, der nichts mit dem in der StringList zu tun hat)


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:15 Uhr.
Seite 1 von 2  1 2      

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