Сама по себе статья вызывает мало интереса, кроме того, что поднята интересная
проблема - вызов с-шной функции с переменным числом параметров.
В ответах с использованием массивов вообще, IMHO, ошибка - на стек попадет адрес
массива, а в с это совсем не то. Но решение проблемы существует, правда надо
ручками повозиться со стеком. Приводимая ниже функция на скорую руку
переделывается из работающей в реальном проекте похожего буфера с-паскаль, но
там функция в dll имеет тип вызова cdecl и другие обязательные параметры, в
связи с чем возможны "опечатки"
// Пишем функцию-переходник, маскируя с-шные "..." паскалевским// array of constfunction sprintf(out, fmt: Pchar; args: arrayofconst): Integer;
var
I: Integer;
BufPtr: Pchar;
S: string;
buf: array[0..1024] of char;
begin
BufPtr := buf;
// Формируем буффер параметров. Можно, конечно, и прямо на стеке,// но головной боли слишком много - проще такfor I := low(Par) to High(Par) docase 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: // А здесь во избежание чудес надо// копию строки снятьbeginif 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) docase 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.
Рассмотрим шаги решения:
Обёртка функция sprintf принимает три параметра: out, fmt и массив параметров constargs.
Обёртка функция создает буфер buf для хранения параметров на стеке.
Затем обёртка функция проходит по массиву args, конвертируя каждый параметр в его корректный формат (например, целые числа хранятся как 4-байтовые значения, расширенные значения хранятся как 8-байтовые значения и т.д.). Конвертированные параметры хранятся в буфере buf.
После подготовки буфера обёртка функция использует ассемблерный код для pushing необходимых параметров на стек и вызова функции C wsprintf. Ассемблерный код специфичен для архитектуры x86.
Наконец, обёртка функция сохраняет результат вызова функции wsprintf и восстанавливает стек к его оригинальному состоянию.
Автор отмечает, что это решение требует ручной манипуляции стека с помощью ассемблерного кода, что может быть ошибочно. Однако, это предоставляет рабочее решение для вызова функций C с переменным числом параметров из Delphi.
sworth noting, что есть другие способы достижения этой цели, такие как использование интерфейсов или компонентов COM, но это решение специфично для вызова функций C и требует ручной манипуляции стека.
В статье описывается вызов C-функции с переменным числом параметров, используя функцию-переходник в языке Pascal для маскирования типов вызова и обеспечения корректного формирования буфера параметров.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.