Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

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

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 понятны.

Интересная статья о вызове функции C с переменным числом параметров из Delphi, используя функцию wsprintf в качестве примера.

Автор предлагает решение этой проблемы созданием "обёртки" функции в Delphi, которая принимает массив параметров const и использует ассемблерный код для вызова функции C с корректными параметрами. Обёртка функция реализована на языке Pascal, но использует ассемблерный код для манипуляции стека и вызова функции C.

Рассмотрим шаги решения:

  1. Обёртка функция sprintf принимает три параметра: out, fmt и массив параметров const args.
  2. Обёртка функция создает буфер buf для хранения параметров на стеке.
  3. Затем обёртка функция проходит по массиву args, конвертируя каждый параметр в его корректный формат (например, целые числа хранятся как 4-байтовые значения, расширенные значения хранятся как 8-байтовые значения и т.д.). Конвертированные параметры хранятся в буфере buf.
  4. После подготовки буфера обёртка функция использует ассемблерный код для pushing необходимых параметров на стек и вызова функции C wsprintf. Ассемблерный код специфичен для архитектуры x86.
  5. Наконец, обёртка функция сохраняет результат вызова функции wsprintf и восстанавливает стек к его оригинальному состоянию.

Автор отмечает, что это решение требует ручной манипуляции стека с помощью ассемблерного кода, что может быть ошибочно. Однако, это предоставляет рабочее решение для вызова функций C с переменным числом параметров из Delphi.

sworth noting, что есть другие способы достижения этой цели, такие как использование интерфейсов или компонентов COM, но это решение специфично для вызова функций C и требует ручной манипуляции стека.

В статье описывается вызов C-функции с переменным числом параметров, используя функцию-переходник в языке Pascal для маскирования типов вызова и обеспечения корректного формирования буфера параметров.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Процедуры и функции ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-19 13:29:56
2024-11-21 14:20:14/0.014492988586426/1