1

我们有自己的粘合层代码thingamajig,它允许我们在我们的Win32 Delphi 程序中托管.NET 运行时。这使我们能够随着时间的推移逐步过渡到 .NET。

但是,我们不时会遇到一些问题,昨天我在 SO 上看到了一个关于 Jcl 的 .NET 主机实现的答案,所以我想看看是否有一些明显的差异。

原来有,但我不明白它做了什么,为什么,以及我是否需要这样做。我当然会尝试它,但我非常希望其他了解这个奇怪代码背后原因的人告诉我它的作用。

随着时间的推移,我们可能会切换到使用 Jcl 实现,但由于我们即将发布,除非为了解决当前问题绝对有必要,否则在这个级别的代码进行大修是不合理的,所以请不要t 只是建议我们切换。

无论如何,不​​同之处在于它们如何调用 .NET 函数以加载和绑定到 .NET 运行时,基本上它们如何调用从 .NET dll 导出的函数。

这是我的代码:

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 来挖掘第三方代码。

4

1 回答 1

5

汇编代码删除 CorBindToRuntimeEx 的堆栈帧。如果您调用 CorBindToRuntimeEx,则所有参数都被推入堆栈(=> stdcall)。然后该函数调用 GetProcedureAddress 来初始化全局 _CorBindToRuntimeEx 变量,该变量现在指向“CorBindToRuntimeEx”函数。

GetProcedureAddress 返回后必须调用 _CorBindToRuntimeEx 函数。但是这里我们有一个问题。Delphi 自动在代码中添加了“push ebp; mov ebp,esp”(“begin”所在的位置)。为了删除该堆栈帧,使用了“mov esp,ebp; pop ebp”。“jmp [_CorBindToRuntimeEx]”然后将执行指针设置为 _CorBindToRuntimeEx 函数,然后该函数使用来自我们的 CorBindToRuntimeEx 函数的返回地址。

于 2009-04-22T10:27:55.667 回答