Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 8 мая!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

БОЛЬШОЙ FAQ ПО DELPHI



Вызов c-шной функции с переменным числом параметров

Автор: Владимир Переплетчик

Комментарий к статье по поводу wsprintf

Сама по себе статья вызывает мало интереса, кроме того, что поднята интересная проблема - вызов с-шной функции с переменным числом параметров. В ответах с использованием массивов вообще, IMHO, ошибка - на стек попадет адрес массива, а в с это совсем не то. Но решение проблемы существует, правда надо ручками повозиться со стеком. Приводимая ниже функция на скорую руку переделывается из работающей в реальном проекте похожего буфера с-паскаль, но там функция в dll имеет тип вызова cdecl и другие обязательные параметры, в связи с чем возможны "опечатки"


 // Пишем функцию-переходник, маскируя с-шные "..." паскалевским
 // array of const
 
 function sprintf(out, fmt: Pchar; args: array of const): Integer;
 var
   I: Integer;
   BufPtr: Pchar;
   S: string;
   buf: array[0..1024] of char;
 begin
   BufPtr := buf;
   // Формируем буффер параметров. Можно, конечно, и прямо на стеке,
   // но головной боли слишком много - проще так
   for I := low(Par) to High(Par) do
     case Par[I].VType of
       vtInteger: // Здесь все просто - 4 байта на стек
         begin
           Integer(Pointer(BufPtr)^) := Par[I].VInteger;
           Inc(BufPtr, 4);
         end;
       vtExtended: // Здесь хуже - слова надо местами поменять :-((
         begin
           Integer(Pointer(BufPtr)^) :=
             Integer(Pointer(Pchar(Par[I].VExtended) + 4)^);
           Inc(BufPtr, 4);
           Integer(Pointer(BufPtr)^) :=
             Integer(Pointer(Par[I].VExtended)^);
           Inc(BufPtr, 4);
         end;
       vtPChar: // Здесь тоже все хорошо - 4 байта
         begin
           Pointer(Pointer(BufPtr)^) := Par[I].VPchar;
           Inc(BufPtr, 4);
         end;
       vtString, vtAnsiString: // А здесь во избежание чудес надо
         // копию строки снять
         begin
           if Par[I].VType = vtString then
             S := Par[I].VString^
           else
             S := string(Par[I].VAnsiString);
           Pointer(Pointer(BufPtr)^ :=
             StrPCopy(StrAlloc(Length(S) + 1), S);
             Inc(BufPtr, 4);
         end;
     end;
   // Поддержку других типов доделывать самостоятельно,
   // вооружившись толковым пособием по с и ассемблеру
 
   I := (BufPtr - buf) div 4; // Сколько раз на стек слово положить
 
   asm
       push dword ptr [out]
       push dword ptr [fmt]
       mov ecx, dword ptr [i]
       mov eax, dword ptr [buf]  // stdcall - параметры в прямом
                                 // порядке
       @@1:
       push dword ptr [eax]
       add  eax, 4
       loop @@1
       call [wsprintf]
       mov  dword ptr [Result], eax // Сохранить результат
       mov eax, dword ptr [i]       // Привести в порядок стек
       shl eax, 2
       add eax, 8
       add esp, eax
   end;
   // Почистить строки
   for I := low(Par) to High(Par) do
     case Par[I].VType of
       vtInteger: Inc(BufPtr, 4);
       vtExtended: Inc(BufPtr, 8);
       vtPChar: Inc(BufPtr, 4);
       vtString, vtAnsiString:
         begin
           StrDispose(PChar(PPointer(BufPtr)^));
           Inc(BufPtr, 4);
         end;
     end;
 end;
 

В таком виде методика уже имеет смысл. Изменения при типах вызова cdecl / pascal понятны.




<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ



Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS