How to set string (or AnsiString) constant in the TVarRec?
Asked Answered
R

3

5

I want to pass the formatting arguments Args into the Format function. I found some examples of that, but I can't find out how to assign string constant in the TVarRec member. The following code fails on compilation with E2089 Invalid typecast.

procedure TForm1.Button1Click(Sender: TObject);
var Arguments: array of TVarRec;
begin
  SetLength(Arguments, 2);

  Arguments[0].VInteger := 111;
  Arguments[1].VAnsiString :=  PAnsiString('Text'); // I want to set this member

  ShowMessage(Format('Decimal: %d, String: %s', Arguments));
end;

Can anyone suggest me how to set the string (or AnsiString) constant to the TVarRec member ? I'm using Delphi 2009.

Thanks a lot

Retroversion answered 19/5, 2011 at 12:37 Comment(6)
Relevant article for people who have never seen this hairy aspect of Delphi before: rvelthuis.de/articles/articles-openarr.htmlCostplus
I wrote an answer and deleted it, and someone else did too. This one was HARD. Good one TOndrej.Costplus
Just for the record: You don't have to do all of that. You can call Format directly (and much more easily): ShowMessage(Format('Decimal: %d, String: %s', [111, 'Text']));. This works anywhere TVarArgs is called for (typically for an array of const) - you can create the VarArgs array by just passing it as [StringItem, IntegerItem, BooleanItem] and so forth.Plains
You're right however I was considering to pass the Args as an array of string with variable length as the arguments to the Format function.Retroversion
I figured you had a reason to want to do it this way (which is why I posted a comment and not an answer). I just didn't want someone new to Delphi to see your post and think this was the "normal" way to call Format.Plains
You're right, but hard to say if the one notice the comment prior to the accepted answer; however still would be fine to rephrase it to the common way using own function instead of FormatRetroversion
H
10

This seems to work in XE:

var
  Args: array[0..1] of TVarRec;
  S: AnsiString;
  U: UnicodeString;
begin
  S := 'Hello';
  U := 'world';
  Args[0].VType := vtAnsiString;
  Args[0].VAnsiString := Pointer(S);
  Args[1].VType := vtUnicodeString;
  Args[1].VUnicodeString := Pointer(U);

  Writeln(Format('%s, %s!', Args));
end;
Hamon answered 19/5, 2011 at 13:15 Comment(8)
+1 and accept. This works also in Delphi 2009. Thanks a lot :)Retroversion
Type-casting Pointer(S) should also work (thus allowing for empty strings). Seems the problem is merely with string literals.Eleonoraeleonore
I've modified the code to show both AnsiString and UnicodeString args.Hamon
Wow. That was harder than I thought it would be! Another previous deleted answer used SetLength on an Array of TVarRec, and that appears to fail at runtime, even though it compiles okay. I was surprised by that.Costplus
That's what I've tried for the first time, but because I missed to add a VType I was getting an access violation. Thanks to all !Retroversion
daemon_x: Pay careful attention to the difference between "array of TVarRec" which requires setLength, and array[0..1] which is fixed. Only the latter will work.Costplus
@Warren P - no, it works fine even if you use dynamic arrays; at least in D2009Retroversion
Really? So it was something about vtString versus vtUnicodeString that broke the other answer?Costplus
A
6

Just my two cents. TOndrej's answer is correct, but I just wanted to emphasize some points. And comments are not a good place to do it.

Please be aware that the AnsiString must be referenced during all time TVarRec will be used.

For instance, if you write a function setting an array of TVarRec, you should ensure that you made a temporary copy of the AnsiString created within the function to a variable which will remain until all time the TVarRec array is used. Otherwise you may have some random access violation (not every time, but only when the MM reassign the ansistring memory).

For instance, the following code is incorrect:

type
  TVarRec2 = array[0..1] of TVarRec;

procedure SetVarRec(a,b: integer; var Result: TVarRec2);
begin
  Result[0].VType := vtAnsiString;
  Result[0].VString := pointer(AnsiString(IntToStr(a)));
  Result[1].VType := vtUnicodeString;
  Result[1].VString := pointer(UnicodeString(IntToStr(b)));
end;

Because the AnsiString and UnicodeString temporary variables will be freed when the procedure ends, and Results[].VString will still point to those freed memory...

Using a class or a record with some temporary private string may do the trick:

type
  TMyVar2 = record
  private
    tmpA: AnsiString;
    tmpB: UnicodeString; 
  public
    VarRec: TVarRec2;
    procedure SetVarRec(a,b: integer);
  end;

procedure TMyVar2.SetVarRec(a,b: integer);
begin
  VarRec[0].VType := vtAnsiString;
  tmpA := AnsiString(IntToStr(a));
  VarRec[0].VString := pointer(tmpA);
  VarRec[1].VType := vtUnicodeString;
  tmpB := UnicodeString(IntToStr(b));
  VarRec[1].VString := pointer(tmpB);
end;

Of course, in your program you may have an already existing class. In this case, you would better use a method, and some private temporary strings. For the method to be multi-thread safe (i.e. re-entrant) you should provide the temporary strings as parameters...

I use this trick to have valid a dynamic array of TVarData containing some AnsiString content in a class. In fact, TVarData and TVarRec both uses such non referenced pointer to strings, which can be confusing.

Be aware that issues involving pointer(S) like statements can be difficult to track.

Avila answered 19/5, 2011 at 14:46 Comment(0)
T
0

you have to use array of ansistring value because you have to use point

temps: array of AnsiString;
value: array of TVarRec;
...
max_count := 10;
setlength(temps,max_count);
setlength(value,max_count);
for i := 0 to max_count-1 do begin
  temps[i] := IntToStr(i);
  value[i].VType := vtAnsiString;
  value[i].VString := Pointer(temps[i]); 
end;
AdoTable.insertrecord(value);
Theomachy answered 2/7, 2021 at 1:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.