AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Array über Schleife ausfüllen

Ein Thema von haentschman · begonnen am 11. Feb 2021 · letzter Beitrag vom 11. Feb 2021
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#1

Array über Schleife ausfüllen

  Alt 11. Feb 2021, 15:01
Delphi-Version: 10.1 Berlin
Hallo...

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



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.

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... Er konnte es auch nicht verstehen!

Geändert von haentschman (11. Feb 2021 um 15:41 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Gausi
Gausi

Registriert seit: 17. Jul 2005
885 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 15:16
Nutzt du eine sehr alte Delphi-Version (vor D2009), oder eine neuere? Denn in den neueren Versionen ist diese Zeile das Problem, denke ich:
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 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...
The angels have the phone box.

Geändert von Gausi (11. Feb 2021 um 15:18 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#3

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 15:32
Danke...

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.

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...

Geändert von haentschman (11. Feb 2021 um 15:39 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#4

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 15:45
Nur so ne Idee: Unterschied zwischen Parameters[I + 6] := PAnsiChar(IntToStr(I)); und 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.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#5

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 16:04
Nein, mit PChar auf einen veränderlichen String geht sowas garnicht.

Delphi nutzt für das Result dieses IntToStr-Aufrufs (PAnsiChar(IntToStr(I)) ) die selbe Variable, denn es ist in der Schleife auch der selbe Code , 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).
$2B or not $2B

Geändert von himitsu (11. Feb 2021 um 16:22 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#6

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 16:06
Zitat:
Wenn die PAnsiChar gültig bleiben sollen, dann musst du den Speicher dafür selbst bereitstellen.
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...
Miniaturansicht angehängter Grafiken
11.png   14.png  

Geändert von haentschman (11. Feb 2021 um 16:12 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#7

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 16:21
Mit gefüllter FileList:

Zitat:
Wenn dieses PChar-Array "Parameters" nur als "Ausgabe" genutzt wird
wegen:
InitError := FGsApiInitWithArgs(FGsInstance, Length(FParameters), FParameters); ...er will array of PAnsiChar
Miniaturansicht angehängter Grafiken
files.jpg  

Geändert von haentschman (11. Feb 2021 um 16:27 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#8

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 16:26
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ß.
$2B or not $2B

Geändert von himitsu (11. Feb 2021 um 16:32 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.388 Beiträge
 
Delphi 12 Athens
 
#9

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 16:34
Zitat:
siehe mein letzter Post.
Values [6-9] aus TStringList/TStrings...

Geändert von haentschman (11. Feb 2021 um 16:36 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#10

AW: Array über Schleife ausfüllen

  Alt 11. Feb 2021, 16:43
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)
$2B or not $2B

Geändert von himitsu (11. Feb 2021 um 16:52 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:45 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