Преобразование аргументов переменных в методе интерфейса C stdcall в Delphi - PullRequest
0 голосов
/ 17 сентября 2018

Я конвертирую интерфейс ICallFrame в Delphi, и я не уверен, как обработать метод ICallFrame :: Invoke .

Это определение:

HRESULT Invoke(
  void *pvReceiver,
  ...  
);

В соответствии с документацией директива varargs работает только с внешними процедурами и только с соглашением о вызовах cdecl.

Это немного странно, поскольку Invoke объявляется с STDMETHODCALLTYPE, то есть __stdcall, что является соглашением о чистоте вызываемого абонента. Но функция variadic не может быть очищена от вызываемого абонента, поскольку вызываемый объект не знает, сколько параметров было передано. Есть ли какая-то магия компилятора, если вы делаете это с Visual Studio?

Даже если магия компилятора использует cdecl, компилятор Delphi этого не принимает (потому что он не внешний) E2070 Unknown directive: 'varargs'

ICallFrame = interface(IUnknown)
  ['{D573B4B0-894E-11d2-B8B6-00C04FB9618A}']
  // other methods left out
  function Invoke(pvReceiver: PVOID): HRESULT; cdecl; varargs;
end;

Судя по ответу Руди, это может сработать:

type
    TfnInvoke = function(this: Pointer; pvReceiver: Pointer): HRESULT; cdecl varargs;

implementation 

function GetInterfaceMethod(const intf; methodIndex: dword) : pointer;
begin
  Result := Pointer(pointer(dword_ptr(pointer(intf)^) + methodIndex * SizeOf(Pointer))^);
end;
...
var
  Invoker: TfnInvoke;
  ...
  // Don't forget IUnknown methods when counting!
  Invoker := GetInterfaceMethod(myIntf, 21);
  Invoker(myIntf, FObject, 11, 17.3);

Я проверю это и сообщу ... РЕДАКТИРОВАТЬ: проверено и работает.

1 Ответ

0 голосов
/ 17 сентября 2018

Stdcall не может использовать переменные аргументы. Любая функция C или C ++, принимающая переменные аргументы, должна иметь значение __cdecl, поскольку только в этом соглашении о вызовах вызывающая программа (код), которая знает, сколько параметров было передано, очищает стек. Даже если соглашение о вызовах по умолчанию равно stdcall, функции var args будут cdecl.

Delphi не может генерировать таких функций, но может использовать существующие внешние функции C (в DLL или .obj-файле) с использованием директивы varargs.

Итак, функция C (или C ++), такая как

HRESULT Invoke(void *pvReceiver, ...);

следует преобразовать в:

function Invoke(pvReceiver: Pointer); cdecl; varargs;

Обратите внимание, что вы опускаете часть ... в декларации Delphi. Предполагается, когда вы используете директиву varargs.

И это позволяет вам использовать его в Delphi, как в C или C ++:

Invoke(someReceiver, 17, 1.456);

Другой пример: например, хорошо известная функция C printf():

int printf(const char *format, ...);

объявлен как

function printf(format: PAnsiChar {args}): Integer; cdecl; varargs;

in System.Win.Crtl.pas .

Обновление

Кажется, это метод интерфейса.

Не уверен, что это работает, но я бы попробовал:

type
  TInvoke = function(Intf: IInterface; pvReceiver: Pointer): HRESULT; cdecl varargs; 
  // No semicolon between cdecl and varargs.

и используйте его как:

MyResult := TInvoke(@myIntf.Invoke)(myIntf, someReceiver, 11, 17.3);

Этот метод может быть объявлен как __stdcall (с помощью макроса STDMETHODCALLTYPE), но даже тогда, если он вариационный, компилятор (VC ++) сгенерирует __cdecl, как Реми Лебо обнаружил в Блог Раймонда Чена .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...