动态链接库和静态链接库连接方式的差异

3

我有一个C DLL,并希望从Delphi XE3 Update 2中调用它。 奇怪的是,在我的项目中,动态调用与静态调用是不同的。这里是复制代码的“最小”代码(我已经更改了Lib /函数名称):

program testProject;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.classes, Windows;

function keylist_open (keylist: PPointer): Integer; external 'libLib';

var
  Handle: HINST;
  DLLName: PChar = 'libLib.dll';

type
  Tkeylist_open = function(keylist: PPointer): Integer; stdcall;

const
  keylist_openDynamic: Tkeylist_open = nil;

var
  keylist: Pointer;


begin
  Handle := LoadLibrary(DLLName);
  if Handle = 0 then
      Exit;
  @keylist_openDynamic := GetProcAddress(Handle, 'keylist_open');

  keylist_open(@keylist);
  if (keylist = nil) then
      Writeln('static: keylist is nil');
  keylist_openDynamic(@keylist);
  if (keylist = nil) then
      Writeln('dynamic: keylist is nil');
end.

输出结果为:
static: keylist is nil

这意味着动态调用函数与静态调用不同。通过动态调用确实可以正确初始化keylist。查看生成的汇编代码后,我意识到变量'keylist'被放入eax寄存器中:

testProject.dpr.34: keylist_open(@keylist);
004D16A2 B804B04D00       mov eax,$004db004
004D16A7 E8ECC6FFFF       call keylist_open

那么

testProject.dpr.12: function keylist_open (keylist: PPointer): Integer; external 'libLib';
004CDD98 FF255CC54D00     jmp dword ptr [$004dc55c]

还有另一个跳转

libLib.keylist_open:
5B364508 E903A23D00       jmp $5b73e710

但是DLL(我不知道这是哪个函数,可能是某些入口点或关键列表例程)存在。

5B73E710 55               push ebp
5B73E711 8BEC             mov ebp,esp
5B73E713 81ECDC000000     sub esp,$000000dc
5B73E719 53               push ebx
5B73E71A 56               push esi
5B73E71B 57               push edi
5B73E71C 8DBD24FFFFFF     lea edi,[ebp-$000000dc]
5B73E722 B937000000       mov ecx,$00000037
5B73E727 B8CCCCCCCC       mov eax,$cccccccc
...

看起来eax参数正在eax中被覆盖。 两行后,动态调用的代码如下:

testProject.dpr.37: keylist_openDynamic(@keylist);

004D16CE 6804B04D00       push $004db004
004D16D3 FF15F0564D00     call dword ptr [$004d56f0]

跳转到
libLib.keylist_open:
5B364508 E903A23D00       jmp $5b73e710

因此,参数传递到相同的代码中。但是由于参数现在没有存储在eax中,覆盖eax的值就不重要了。

请问哪位能解释一下,出了什么问题,也就是说,我的静态代码有什么问题,为什么?


糟糕!就这样了!不应该依赖Delphi教程,它链接了一个Delphi DLL,所以我认为stdcall仅用于动态链接:(...非常感谢! - complexM
1个回答

2
两个版本的不同在于调用约定。运行时链接变量使用 stdcall,而加载时链接变量使用 register
让调用约定匹配,一切都会好起来。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接