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:

Calling printf from Delphi

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;

Usage example:

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 RSS20

Your name: Your homepage:

Text & signature markup:You can use UverseWiki markup. In short: **bold**, //italic//, %%code%%, ((URL link)), >inline quote, <[ multiline quote ]>.

Humans! Please enter "J" here: (or turn JavaScript on for automatic verification)
Subscribe by e-mail (manage):
Ctrl+Enter »