This fragment is about to be reported (you'll remain on this page): You can enter a comment to clarify the mistake if you would like to: |
A few days ago I was writing my own SQLite wrapper for Delphi 7 (I think it later will be available under this address: SQLite for Delphi 7). SQLite has its own version of standard C printf() function – mprintf() – and just like C's it accepts a variable argument list.
This post, however, is about its vprintf() version which takes that list as a Pointer variable instead of multiple arguments:
printf("%s %s", "str1", "str2");
vprintf("%s %s", "str1\0str2");
I will demonstrate how to call the second version in Delphi 7 using WinAPI wvsprintf() function. If you need to call a real C-style variable argument list function check this thread on StackOverflow for the solution.
wvsprintf() is defined in C++ as:
int wvsprintf(LPTSTR lpOutput, LPCTSTR lpFormat, va_list ArgList);
…and in Delphi's Windows.pas as:
pascalfunction wvsprintfW(Output: PWideChar; Format: PWideChar; arglist: va_list): Integer; stdcall;
However, you can't call it directly because you need to construct the arglist: va_list parameter. This can be done by hand (check the first example in this post) or using this generic function (download demo project):
pascalfunction CallWvsprintf(Format: WideString; const Args: array of const): WideString; type TVAList = array[0..$FFFF] of Pointer; var VA: TVAList; StrI, I, Size: Integer; Strings: array[0..High(VA)] of WideString; begin StrI := 0; Size := Length(Format) * 2; for I := 0 to Length(Args) - 1 do with Args[I] do if I >= Length(VA) then raise EConvertError.CreateFmt('Too many format arguments (more than %d).', [Length(VA) - 1]) else case VType of vtInteger: begin VA[I] := Pointer(VInteger); Inc(Size, 20); end; vtPChar, vtAnsiString: begin // Delphi will automatically convert String or PChar // into WideString and vice versa: Strings[StrI] := VPChar + #0; VA[I] := @Strings[StrI][1]; Inc(Size, Length(Strings[StrI]) * 2 - 2 {null-terminator}); Inc(StrI); end; vtPWideChar, vtWideString: begin VA[I] := VPWideChar; Inc(Size, Length(VPWideChar) * 2); end; else raise EConvertError.CreateFmt('Invalid format argument of VType = %d.', [VType]); end; SetLength(Result, Size + Size div 10); {10% extra space} SetLength(Result, wvsprintfW(@Result[1], PWideChar(Format), @VA)); end;
var
S: WideString;
begin
// a trick to prevent Delphi from corrupting a Unicode string:
S := WideString(#$65E5) + WideString(#$672C) + WideString(#$8A9E);
S := CallWvsprintf('Wide str: %s; ANSI: %s; int: %d; hex: %04X.',
[S, 'hello!', 5, $ABC]);
MessageBoxW(0, PWideChar(S), 'wvsprintf demo', mb_IconInformation);
end.
wvsprintf()'s format characters are explained here.
Note: in a real project you would probably use standard Delphi's Format and WideFormat functions because not only they require no arglist and null-terminated string juggling but also support more format characters such as %f (float).
Comments