Autor Beitrag
Knulli
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 116
Erhaltene Danke: 2

Win2k, Win7, Win10
D5, D2005, D2006, D2007, D10.4.2
BeitragVerfasst: Mi 20.03.19 18:30 
Hi Leute,

ich möchte gerne die Format-Funktion mit einem dynamischen Array als Argumentenliste aufrufen.
--> Format(stFmt, Args)
Den FormatString und das Array für die Argumente möchte ich mir in Abhängigkeit diverser Bedingungen selber zusammenbasteln.

Leider zeschießt sich das Array beim dynamischen längermachen von selbst.
Ab dem vierten Eintrag wird der LastIDX-2 mit überschrieben.

Was mache ich falsch?

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
{**************************************************************************************************}
procedure TForm1.Button1Click(Sender: TObject);
type
  TMyVarRecArray = Array of TVarRec;
var
  stFMT: String;
  stResult: String;
  Args: TMyVarRecArray;
  Loop: Integer;
  //////////////////////////////////////////////////////////////////////////////////////////////////
  procedure AddToVarRecArray(var aVarRecArray: TMyVarRecArray; aValues: Array of const);
  var
    VarRec: TVarRec;
  begin
    for VarRec in aValues do
    begin
      SetLength(aVarRecArray, Length(aVarRecArray) + 1);
      Args[High(aVarRecArray)] := VarRec;
    end;
  end;
  //////////////////////////////////////////////////////////////////////////////////////////////////
begin

  stFmt := 'Das wird eine Zeile: '; SetLength(Args, 0);
  for Loop := 1 to 5 do
  begin
    stFmt := stFmt + ' %s ';
    AddToVarRecArray(Args, ['Wert ' + IntToStr(Loop)]);
  end;
  stResult := Format(stFmt, Args);
  MessageBox(0, PChar(stResult), 'NaNu', MB_OK);
end;
{**************************************************************************************************}


Knulli

_________________
Echte Männer schreiben Windows-Programme in Assembler unter edlin.
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Mi 20.03.19 19:18 
Hallo,

diese Zeile verstehe ich nicht und mein Compiler will si auch nicht übersetzen:

ausblenden Delphi-Quelltext
1:
   for VarRec in aValues do					


Gruß
GuaAck
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Do 21.03.19 08:25 
In Delphi 7 gibt es diese Möglichkeit noch nicht.
Daher meckert dein Compiler

Zum eigentlichen Problem:
VarRec ist ein Zeiger auf einen temporären Speicher, der in der Schleife immer wieder genutzt wird.
Durch die Vergrößerung deines Records reservierst du zwar neuen Speicher, aber weist dann doch einen anderen (den oben genannten temporären) Speicherbereich zu.
Dadurch, dass dieser Speicher immer wieder benutzt wird, bekommst du deine falschen Werte.

Das kann soweit führen, dass du im späteren Programmverlauf immer wieder andere Informationen in deinem Array stehen hast, weil der Speicher durch andere Daten überschrieben werden kann. Abgesehen davon dürftest du dadurch auch ein Memoryleak bekommen.

Du musst also bei den Zuweisung in dein Array die Inhalte und nicht die Zeiger zuweisen.
In TVarRec stehen unter VType auch Typ-Informationen. Bei AnsiStrings (und nicht nur da) gibt es noch das zusätzliche Problem, dass die auch nur als Zeiger im Record stehen. Auch dieser Speicherbereich ist nur temporär und wird innerhalb von Delphi sicher wieder mit anderen Informationen während des Programm gefüllt werden können.

Für diesen Beitrag haben gedankt: Knulli
Knulli Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 116
Erhaltene Danke: 2

Win2k, Win7, Win10
D5, D2005, D2006, D2007, D10.4.2
BeitragVerfasst: Do 21.03.19 15:12 
OK, habs (hoffentlich) begiffen:
In TVarRec werden nur Referenzen gespeichert. Und weil bei meinem Aufruf von AddToVarRecArray das Argument nur auf dem Stack existiert (also innerhalb von AddToVarRecArray), gabs Datensalat bei den nachfolgenden Aufrufen.
Ich muss also bis zum Ende der "Nutzzeit" von Args ( also bis nach nach Format(...) ) den Speicherplatz für die Argumente vorhalten. Richtig verstanden?

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
{**************************************************************************************************}
procedure TForm1.Button1Click(Sender: TObject);
type
  TMyVarRecArray = Array of TVarRec;
var
  stFMT: String;
  stResult: String;
  Args: TMyVarRecArray;
  Strings: Array of String;                             // Hier stehen die Strings WIRKLICH
  Loop: Integer;
  //////////////////////////////////////////////////////////////////////////////////////////////////
  procedure AddToVarRecArray(var aVarRecArray: TMyVarRecArray; aValues: Array of const);
  var
    VarRec: TVarRec;
  begin
    for VarRec in aValues do
    begin
      SetLength(aVarRecArray, Length(aVarRecArray) + 1);
      Args[High(aVarRecArray)] := VarRec;
    end;
  end;
  //////////////////////////////////////////////////////////////////////////////////////////////////
begin

  stFmt := 'Das wird eine Zeile: '; SetLength(Args, 0);
  for Loop := 1 to 5 do
  begin
    stFmt := stFmt + ' %s ';
    SetLength(Strings, Length(Strings) + 1);            // Platz schaffan
    Strings[High(Strings)] := 'Wert ' + IntToStr(Loop); // Argument zuweisen
    AddToVarRecArray(Args, [Strings[High(Strings)]]);   // Argument(e) als Referenz(en) übergeben
  end;
  stResult := Format(stFmt, Args);
  SetLength(Strings, 0);                                // erst jetzt darf der Speicher wieder freigegeben werden
  MessageBox(0, PChar(stResult), 'Hubba Hubba!!', MB_OK);
end;
{**************************************************************************************************}


Also kann ich das TVarRecArray nicht WIRKLICH effektiv nutzen. :-(

Frisst Format noch andere Arrays / Listen, die sich auch die WERTE statt nur Referenzen merken können?

Knulli

_________________
Echte Männer schreiben Windows-Programme in Assembler unter edlin.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Fr 22.03.19 08:57 
user profile iconKnulli hat folgendes geschrieben Zum zitierten Posting springen:

Also kann ich das TVarRecArray nicht WIRKLICH effektiv nutzen. :-(

Es ist zumindest nicht trivial.
Ich habe nochmal etwas recherchiert, weil ich zu wenig Zeit habe, eine Lösung für dich zu erarbeiten, die brauchbar ist. Dabei habe ich folgendes gefunden:
https://stackoverflow.com/questions/6058697/how-to-set-string-or-ansistring-constant-in-the-tvarrec
Insbesondere die letzte Antwort beschreibt einen Lösungsansatz, der dein Problem lösen kann. Ist aber dennoch etwas Aufwand.
user profile iconKnulli hat folgendes geschrieben Zum zitierten Posting springen:

Frisst Format noch andere Arrays / Listen, die sich auch die WERTE statt nur Referenzen merken können?

Format habe ich bisher noch nie genutzt. Da muss vielleicht mal ein anderer User was zu sagen.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 22.03.19 10:04 
In dem vorliegenden Fall ist es relativ simpel, da du ja das Array nur innerhalb deiner Methode benötigst. Es genügt daher, wenn du deine Strings in einer lokalen Variable hast (was ja schon der Fall ist) und du nicht versuchst den String in den Record zu bekommen, sondern (wie in den Links gezeigt) nur die Pointer auf diese vorhandenen Strings.

So habe ich das auch schon für die Verwendung mit Format gemacht.

user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Format habe ich bisher noch nie genutzt.
Wir benutzen das um Ausgabestrings nicht per Hand zusammenzuklöppeln, was einfach die Übersichtlichkeit verringern würde und die Übersetzung in andere Sprachen deutlich erschweren oder unmöglich machen würde.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Fr 22.03.19 11:03 
Off-Topic:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Format habe ich bisher noch nie genutzt.
Wir benutzen das um Ausgabestrings nicht per Hand zusammenzuklöppeln, was einfach die Übersichtlichkeit verringern würde und die Übersetzung in andere Sprachen deutlich erschweren oder unmöglich machen würde.

Genau dieser Bedarf existiert bei uns nicht/kaum, da wir nur Inhouse-Entwicklung machen. Es gibt zwar Anwendungen, die Protokolle erzeugen, aber die werden nur von der IT genutzt. Großartige Formatierungen sind daher nicht erforderlich.

On-Topic:
Die Strings sind eben nicht in einer lokalen Variablen. Es wird als Konstante (in einem Array of const) an die Verarbeitungsroutine übergeben. Somit wird der reservierte Speicher wieder frei gegeben und im späteren Schleifendurchlauf wieder genutzt. Das führt ja genau zu dem beschriebenen Problem.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 22.03.19 11:42 
user profile iconjasocul hat folgendes geschrieben Zum zitierten Posting springen:
Die Strings sind eben nicht in einer lokalen Variablen. Es wird als Konstante (in einem Array of const) an die Verarbeitungsroutine übergeben.
Doch, sind sie, in einer der Versionen:
Zitat:
ausblenden Delphi-Quelltext
1:
2:
    Strings[High(Strings)] := 'Wert ' + IntToStr(Loop); // Argument zuweisen
    AddToVarRecArray(Args, [Strings[High(Strings)]]);   // Argument(e) als Referenz(en) übergeben
An der Stelle muss jeder String wie in dem Link beschrieben als Pointer in das Argument geschrieben werden statt wie aktuell wiederum ein array of const daraus zu machen und an AddToVarRecArray zu übergeben.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Fr 22.03.19 12:38 
Ah, ok.
Das hatte ich bei der neuen Source-Variante übersehen.