Конференция "WinAPI" » wvsprintf и размер буфера для нее
 
  • Тыщ (25.04.08 18:31) [0]
    Как узнать, какой велечины буфер будет требоваться функции wvsprintf?

    Если это невозможно, посоветуйте замену wvsprintf с совместимыми правилами форматирования (%s, %d, %.14g и т.п.).
  • Игорь Шевченко © (25.04.08 19:36) [1]

    > посоветуйте замену


    Format ?


    > Как узнать, какой велечины буфер будет требоваться функции
    > wvsprintf?


    Это ее возвращаемое значение
  • Тыщ (25.04.08 20:14) [2]
    Игорь Шевченко ©   (25.04.08 19:36) [1]

    > Format ?

    Все бы хорошо, да только в программе ожидается такой же вид параметров, что и у wvsprintf...

    > Это ее возвращаемое значение

    В том и странность, что это значение возвращается уже после того, как строка записана. И если буфер был недостаточно велик, получим либо AV, либо порчу памяти.
  • palva © (25.04.08 23:26) [3]
    В MSDN пишут, что максимум заполнения буфера 1024.
  • Тыщ (26.04.08 08:49) [4]
    palva ©   (25.04.08 23:26) [3]

    Спасибо.

    Пните в сторону того, как на стеке выглядят параметры для wvsprintf?
    Хочу написать конверсию параметров для другой функции.
  • Игорь Шевченко © (26.04.08 11:04) [5]
    Тыщ   (25.04.08 20:14) [2]


    > В том и странность, что это значение возвращается уже после
    > того, как строка записана. И если буфер был недостаточно
    > велик, получим либо AV, либо порчу памяти.


    передать nil в качестве буфера не помогает ? (Сам не пробовал, ничего не могу сказать)


    > Пните в сторону того, как на стеке выглядят параметры для
    > wvsprintf?


    +0 адрес возврата
    +4 строка формата
    +8 параметр1
    +12 параметр2
    ...
  • palva © (26.04.08 13:48) [6]

    > передать nil в качестве буфера не помогает ? (Сам не пробовал,
    >  ничего не могу сказать)

    Я вчера попробовал, у меня не получилось. Было исключение по защите.


    > +0 адрес возврата
    > +4 строка формата
    > +8 параметр1
    > +12 параметр2


    Эта функция имеет фиксированное число параметров. Последний параметр является адресом области памяти, содержащей величины, которые интерпретируются в соответствии с форматной строкой. Я попробовал следующий код:

    {$APPTYPE CONSOLE}
    function wvsprintf(Output: PChar; Format: PChar; arglist: Pointer): Integer;
    stdcall; external 'user32' name 'wvsprintfA';
    var
     a: array [0..1000] of Char;
     ii: array [0..1] of Integer;
    begin
     ii[0] := 42334543;
     ii[1] := 534;
     wvsprintf(a, '%d %d', @ii[0]);
     WriteLn(a);
     ReadLn
    end.


    +0 адрес буфера с результирующей строкой
    +4 строка формата
    +8 адрес области параметров
  • Тыщ (26.04.08 19:56) [7]
    Я так понял, что любой параметр всегда будет занимать 4 байта?
    Числа с плавающей точкой будут всегда приводиться в single, или передаваться с помощью указателя?
    Строки - с помощью указателя?

    Со всем остальным понятно, спасибо.
  • palva © (26.04.08 20:38) [8]
    > Я так понял, что любой параметр всегда будет занимать 4 байта?
    Ну да, хотя в документации я не видел такого описания.
    Параметры должны выглядеть так же, как будто они были бы положены в стек при вызове функции printf в языке си. Что произойдет с плавающими, я не знаю, поскольку не в курсе о наличии соответствующих форматов и не могу проверить. А символы и строки - да.

    {$APPTYPE CONSOLE}
    function wvsprintf(Output: PChar; Format: PChar; arglist: Pointer): Integer;
    stdcall; external 'user32' name 'wvsprintfA';
    var
     a: array [0..1000] of Char;
     rec: record
       i: Integer;
       c1: Char;
       // Здесь будет вставлено три пустых байта
       s: String;
     end;
    begin
     rec.i := 42334543;
     rec.c1 := 'q';
     rec.s := 'zxcv';
     wvsprintf(a, '%d %c %s', @rec.i);
     WriteLn(a); // 42334543 q zxcv
    end.

  • Тыщ (26.04.08 23:12) [9]
    Спасибо за пример.

    Выяснил, что числа с плавающей точкой будут занимать 8 байт, в том числе и в стеке, то есть тип - double.
    А также, что wvsprintf не работает с ними - выводит буковку.
    А вот vsprintf из msvcrt.dll - работает.

    {$APPTYPE CONSOLE}
    function vsprintf(Output: PChar; Format: PChar; arglist: Pointer): Integer;
     cdecl; external 'msvcrt.dll';

    function wvsprintf(Output: PChar; Format: PChar; arglist: Pointer): Integer;
     stdcall; external 'user32' name 'wvsprintfA';

    {$A4}
    var
     a: array [0..1023] of Char;
     rec: record
       i: Integer;
       c1: Char;
       s: String;
       p: pointer;
       fd: double;
       fd2: double;
     end=(
       i: 42334543;
       c1: 'q';
       s: 'zxcv';
       p: Ptr($DEADBEEF);
       fd: 1.01;
       fd2: 1.01;
     );
    const fmt='%d %c %s %p %f %.14g';
    begin
     wvsprintf(a, fmt, @rec.i);
     WriteLn('wvsprintf: ' + a); // 42334543 q zxcv DEADBEEF f g
     vsprintf(a, fmt, @rec.i);
     WriteLn(' vsprintf: ' + a); // 42334543 q zxcv DEADBEEF 1.010000 1.01
    end.



    P.S. в Win98 wvsprintf даже указатель не выводит.
  • Leonid Troyanovsky © (27.04.08 10:03) [10]

    > Тыщ   (26.04.08 23:12) [9]

    >  в том числе и в стеке, то есть тип - double.
    > А также, что wvsprintf не работает с ними - выводит буковку.

    А чего, wvsprintf обязан работать с float?

    --
    Regards, LVT.
  • Тыщ (27.04.08 10:23) [11]
    Leonid Troyanovsky ©   (27.04.08 10:03) [10]
    > А чего, wvsprintf обязан работать с float?

    По идее да, если эта функция позиционируется, как "Win32 Equivalent for C Run-Time Function".
  • Leonid Troyanovsky © (27.04.08 10:44) [12]

    > Тыщ   (27.04.08 10:23) [11]

    > По идее да, если эта функция позиционируется, как "Win32
    > Equivalent for C Run-Time Function".

    Не всякому рантайму потребны float.
    Что там в msdn написано насчет возможных форматов?
    Кста, arglist - это, IMHO, указатель на массив поинтеров.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (27.04.08 10:53) [13]

    > Leonid Troyanovsky ©   (27.04.08 10:44) [12]

    > Кста, arglist - это, IMHO, указатель на массив поинтеров.

    Фу-ты, это первый элемент массива поинтеров, sorry.

    --
    Regards, LVT.
  • Тыщ (27.04.08 10:58) [14]
    Leonid Troyanovsky ©   (27.04.08 10:44) [12]

    > Что там в msdn написано насчет возможных форматов?

    Ограниченность wvsprintf там отражена.

    > Кста, arglist - это, IMHO, указатель на массив поинтеров.

    Да нет же, см. последний пример, указатель на массив разного всего.
  • Игорь Шевченко © (27.04.08 15:41) [15]
    palva ©   (26.04.08 13:48) [6]

    Пардон, перепутал с wsprintf, буковка v из головы вылетела. В случае с буковкой v все совсем просто - первый аргумент адрес формата, второй аргумент адрес arglist (который va_list, и по которому надо бегать вручную за каждым аргументом, в зависимости от его типа)
  • Тыщ © (29.04.08 13:10) [16]
    {$APPTYPE CONSOLE}
    uses Windows;

    function Int2Str(Value:integer):string;
    begin
     Str(Value,Result);
    end;

    function Int2Hex(Num:integer):string;
    asm
     // edx = Pointer to string
     // eax = Num
     push  esi
     push  edi

     bswap eax
     push  eax

     mov   edi,edx
     mov   esi,esp

     mov   eax,edx
     mov   edx,8
     call  System.@LStrSetLength

     mov   edi,[edi]

     mov   ecx,4
    @@next:
     lodsb
     mov   ah,al
     and   al,$0F
     cmp   al,10
     sbb   al,$69
     das
     xchg  al,ah
     shr   al,$04
     cmp   al,10
     sbb   al,$69
     das
     stosw
     loop @@next
     
     pop   eax
     pop   edi
     pop   esi
    end;

    function Double2Str(D:double; Width,Precision:integer; CutEndZeroes:boolean):string;
    var Temp:string;
    begin
     if not CutEndZeroes then
     begin
      Str(D:0:Width,Temp);
      if Precision<Width*2-Length(Temp) then Precision:=Width*2-Length(Temp);
     end else Precision:=15;

     Str(D:Width:Precision,Result);
     if CutEndZeroes then
     begin
      while Result[Length(Result)]='0' do Delete(Result,Length(Result),1);
      if Result[Length(Result)]='.' then Delete(Result,Length(Result),1);
      Insert(StringOfChar(' ',Width-Length(Result)),Result,1);
     end;
    end;

    function vsprintf(Output,Format:pchar; ArgList:pointer):integer;
     cdecl; external 'msvcrt.dll';

    function dprintf(const Fmt:string; ArgList:pchar):string;
    // Supported form: %[width][.precision]type
    const
     FMT_CHAR=['c','d','f','g','p','s'];
     FMT_NUM=['0','1','2','3','4','5','6','7','8','9'];
    var
     I,Width,Precision:integer;
     WidthSpecified:boolean;
    begin
     if Fmt='' then
     begin
      Result:='';
      Exit;
     end;
     I:=1;
     repeat
      if Fmt[I]<>'%' then
      begin
       Result:=Result+Fmt[I];
      end else begin
       Inc(I);
       if I>Length(Fmt) then break;
       Width:=0;
       WidthSpecified:=false;
       Precision:=0;

       while Fmt[I] in FMT_NUM do
       begin
        WidthSpecified:=true;
        Width:=Width*10+Ord(Fmt[I])-48;
        Inc(I);
        if I>Length(Fmt) then Exit;
       end;

       if Fmt[I]='.' then
       begin
        Inc(I);
        if I>Length(Fmt) then break;
        while Fmt[I] in FMT_NUM do
        begin
         Precision:=Precision*10+Ord(Fmt[I])-48;
         Inc(I);
         if I>Length(Fmt) then Exit;
        end;
       end;
       if Precision=0 then Precision:=1;

       case Fmt[I] of
        'c': begin
         Result:=Result+ArgList^;
         Inc(ArgList,4);
        end;
        'd': begin
         Result:=Result+Int2Str(PInteger(ArgList)^);
         Inc(ArgList,4);
        end;
        'f': begin
         if not WidthSpecified then Width:=8;
         Result:=Result+Double2Str(PDouble(ArgList)^,Width,Precision,false);
         Inc(ArgList,8);
        end;
        'g': begin
         if not WidthSpecified then Width:=1;
         Result:=Result+Double2Str(PDouble(ArgList)^,Width,Precision,true);
         Inc(ArgList,8);
        end;
        'p': begin
         Result:=Result+Int2Hex(PInteger(ArgList)^);
         Inc(ArgList,4);
        end;
        's': begin
         Result:=Result+PChar(Ptr(PInteger(ArgList)^));
         Inc(ArgList,4);
        end;
        else continue;
       end;

      end;
      Inc(I);
     until I>Length(Fmt);
    end;

    var
     a: array [0..1023] of Char;
     rec: packed record
      i: Integer;
      c1: Char; _dummy:array[0..2] of byte;
      s: String;
      p: pointer;
      fd: double;
      fd2: double;
      fd3: double;
      fd4: double;
     end=(
      i: 42334543;
      c1: 'q';
      s: 'zxcv';
      p: Ptr($DEADBEEF);
      fd: 1.01;
      fd2: 1.01;
      fd3: 1.01;
      fd4: 1.01;
     );
     s:string;
    const fmt='!%d %c %s %p %5.14f %5.14g %f %g %zs!';
    //const fmt='!%d %c %s %p %.f %.g %f %g %zs!';
    begin
     vsprintf(a,fmt,@rec); WriteLn('vsprintf: '+a);
     s:=dprintf(fmt,@rec); WriteLn(' dprintf: '+s);
    end.

  • Тыщ © (29.04.08 13:12) [17]
    Вот, собственно, написал замену wvsprintf и vsprintf :)
    Если кому пригодится - я буду только рад.
  • Игорь Шевченко © (29.04.08 17:50) [18]

    > Вот, собственно, написал замену wvsprintf и vsprintf :)


    >  rec: packed record
    >   i: Integer;
    >   c1: Char; _dummy:array[0..2] of byte;
    >   s: String;
    >   p: pointer;
    >   fd: double;
    >   fd2: double;
    >   fd3: double;
    >   fd4: double;
    >  end=(
    >   i: 42334543;
    >   c1: 'q';
    >   s: 'zxcv';
    >   p: Ptr($DEADBEEF);
    >   fd: 1.01;
    >   fd2: 1.01;
    >   fd3: 1.01;
    >   fd4: 1.01;
    >  );


    а эту rec каждый раз вручную кодировать ? :)
    Format пользовать всяко проще...
  • Тыщ © (29.04.08 18:35) [19]
    Игорь Шевченко ©   (29.04.08 17:50) [18]

    Да не, rec - это для тестирования.
    Писал же для сишной библиотеки, которая ожидает функцию формы (w)vsprintf.
 
Конференция "WinAPI" » wvsprintf и размер буфера для нее
Есть новые Нет новых   [134433   +21][b:0.001][p:0.005]