JclDotNet и некоторые странные шаблоны вызовов с использованием ассемблера - PullRequest
1 голос
/ 22 апреля 2009

У нас есть собственный клеевой слой-code-thingamajig, который позволяет нам размещать среду выполнения .NET в нашей программе Win32 Delphi. Это позволило нам постепенно перейти на .NET со временем.

Но время от времени у нас возникают некоторые проблемы, и вчера я увидел здесь ответ на SO, касающийся реализации хоста JNET .NET, поэтому я решил посмотреть, есть ли очевидные различия .

Оказывается, есть, но я не понимаю, что он делает, почему и нужно ли мне делать то же самое. Я, конечно, попробую, но я бы очень хотел, чтобы кто-то еще, понимающий причину этого странного кода, рассказал мне, что он делает.

Со временем мы могли бы перейти на использование реализации Jcl, но, поскольку у нас есть готовящийся к выпуску выпуск, если только это не является абсолютно необходимым для решения текущих проблем, то капитальный ремонт на этом уровне кода не оправдан, поэтому пожалуйста, не предлагайте нам просто поменяться.

В любом случае, разница заключается в том, как они обращаются к функциям .NET для загрузки и привязки к среде выполнения .NET, в основном к тому, как они вызывают экспортированные функции из библиотеки .NET.

Вот мой код:

type
  TCorBindToRuntimeEx = function(pwszVersion: PWideChar;
    pwszBuildFlavor: PWideChar;
    startupFlags: DWord; rclsid, riid: PGUID;
    out ppv: IUnknown): Integer; stdcall;
...
var
  CorBindToRuntimeEx  : TCorBindtoRuntimeEx = nil;
...
CorBindToRuntimeEx := GetProcAddress(Runtimehandle, 'CorBindToRuntimeEx');
...
clsid := CLASS_CorRuntimeHost;
iid := IID_ICorRuntimeHost;
rc := CorBindToRuntimeEx('v2.0.50727', 'wks', 0, @clsid,
    @iid, UnkRuntimeEngine);

Теперь здесь я просто использую GetProcAddress, чтобы загрузить адрес экспортируемой функции в переменную, типизированную как указатель на функцию stdcall, а затем вызываю ее. Это работает, вроде. Как я уже сказал, некоторые проблемы с нечетными сообщениями об ошибках в некоторых случаях.

Хорошо, вот их код, и обратите особое внимание на функцию с кодом ассемблера.

function CorBindToRuntimeEx(pwszVersion, pwszBuildFlavor: PWideChar;
  startupFlags: DWORD; const rclsid: TCLSID; const riid: TIID;
  out pv): HRESULT; stdcall;
{$EXTERNALSYM CorBindToRuntimeEx}
...
var
  _CorBindToRuntimeEx: Pointer = nil;

function CorBindToRuntimeEx;
begin
  GetProcedureAddress(_CorBindToRuntimeEx, mscoree_dll,
    'CorBindToRuntimeEx');
  asm
    mov esp, ebp
    pop ebp
    jmp [_CorBindToRuntimeEx]
  end;
end;
...
OleCheck(CorBindToRuntimeEx(PWideCharOrNil(ClrVer),
  PWideChar(ClrHostFlavorNames[Flavor]), Flags,
  CLASS_CorRuntimeHost, IID_ICorRuntimeHost,
  FDefaultInterface));

Обратите внимание, что я немного переформатировал код, чтобы избежать здесь горизонтальных полос прокрутки в SO, но только для того, чтобы добавить несколько разрывов строк и отступов, код не изменился.

Последний вызов, вероятно, не имеет значения, в основном он будет передавать те же параметры, что и мы (обратите внимание, что мы передаем 0 в качестве значения параметров, но мы также пробовали с теми же конкретными аргументами, которые использует код Jcl, и проблемы все еще присутствуют).

Итак, мой вопрос: что он ** делает на ассемблере? Я знаю, что он делает в техническом смысле, я раньше программировал ассемблер, поэтому он манипулирует указателями стека.

Вопрос в том, почему он должен это делать. Я просто не понимаю.

Может быть, стековые фреймы совсем не stdcall?

Пожалуйста, научите меня чему-нибудь сегодня.


Редактировать : Хорошо, изменил мой код соответственно, но проблема у нас все еще существует, так что это не так. Похоже, я все-таки попробую WinDbg покопаться в стороннем коде.

1 Ответ

5 голосов
/ 22 апреля 2009

Код ассемблера удаляет стековый фрейм CorBindToRuntimeEx. Если вы вызываете CorBindToRuntimeEx, все параметры помещаются в стек (=> stdcall). Затем функция вызывает GetProcedureAddress, чтобы инициализировать глобальную переменную _CorBindToRuntimeEx, которая теперь указывает на функцию CorBindToRuntimeEx.

После возврата GetProcedureAddress должна быть вызвана функция _CorBindToRuntimeEx. Но здесь у нас проблема. Delphi автоматически добавил «push ebp; mov ebp, esp» в код (где «начало»). И для удаления этого стекового кадра используется "mov esp, ebp; pop ebp". Затем «jmp [_CorBindToRuntimeEx]» устанавливает указатель выполнения на функцию _CorBindToRuntimeEx, которая затем использует адрес возврата нашей функции CorBindToRuntimeEx.

...