delphi调用DLL中的函数,函数类型要一样吗?请大家看看

wzg1031 2007-05-21 09:20:53
情况如下:
用delphi做一DLL,里面有一输出函数;定义如下:
function GetMax: integer; stdcall;
实现代码:
function GetMax: Integer;
begin
Result := 100;
end;
工程文件:
library Project1;

uses
SysUtils,
Classes,
Unit1 in 'Unit1.pas' {Form1};

exports
GetMax;

{$R *.res}

begin
end.

调用DLL的代码如下:
procedure button1OnClick(sender: TObject);
var
a: function(b: PChar; c: boolean): Integer; // 此函数类型与DLL中定义的函数参数个数不一样,但调用没有问题,不知道是什么原因?
max: integer;
LibHandle: THandle;
begin
LibHandle := LoadLibrary('DLL路径'); // 成功
try
if LibHandle <> 0 then
begin
@a := GetProcAddress(LibHandle, 'GetMax'); // 取函数地址
max := a('',true); // 成功,取得100,为什么,DLL中定义的方法是没有参数的啊?
end;
finally
FreeLibrary(LibHandle);
end;
end;

请各位高手帮忙解释下,小弟搞不懂是什么原因,为什么只要函数名称和DLL中定义的函数名称相同,参数不一样都可以调用啊。
...全文
605 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
liangpei2008 2007-05-24
  • 打赏
  • 举报
回复
学习ing..
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
//四个参数入栈,所以下一条指令偏移16个字节
--------------
应该说四个整型入栈. 另外, 上面的程序没有利用第四个参数Len. 不过应该可以人工控制了.
上面的程序, 函数中的RET指令, 已经越过了编译器给的RET指令, 直接返回.

^_^
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
终于明白了:

ret X //这个X就是所有通过栈传递的实参的长度和

例如:

function Test(a,b,c,Len: integer): integer; stdcall;//函数作用:返回前三个参数和
asm
mov ebp, esp
xor eax, eax
add eax, [ebp+$08]//第1个参数
add eax, [ebp+$0C]//第2个参数
add eax, [ebp+$10]//第3个参数,这三个参数累加到EAX做为返回值
pop ebp
ret 16 //四个参数入栈,所以下一条指令偏移16个字节
end;

procedure TForm1.Button1Click(Sender: TObject);
var
R : integer;
begin
R := Test(10,20,30,16);
ShowMessage(IntToStr(R));//60
end;
王集鹄 2007-05-24
  • 打赏
  • 举报
回复
返回值就是通过eax寄存器得到的
function f(): Integer;
begin
Result := $1234;
end;
//...
var
I: Integer;
begin
I := f();
end;

译为:
@f:
mov eax,$00001234
ret
//....
call f
mov esi,eax
ScriptBaby 2007-05-24
  • 打赏
  • 举报
回复
函数被调用的位置是不可预测的, 所以无论如何一定有一个地方会保存运行中的返回地址, 但是究竟怎么实现我也不知道, 你们写的汇编我能看懂一小部分就已经万幸了...

另外eax返回我在楼上就已经知道了哈
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
或者说:我如何才能得到本应由编译器给出的那个ret后面的偏移地址,通过压栈,传入函数,然后直接在函数里ret返回。
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
ret X '这个X是一个偶数立即数,是编译器给出的,使指针返回到调用这个子程序的下一句程序。我的意思不是要函数返回值。
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
其实,伴水,我的意思是:
function Test:integer; stdcall;
asm
push ebp
mov ebp, esp
mov ecx, [ebp+$08]//---//那就用ECX吧
//... // |
pop ebp // |
ret X <--------------/ 子程序直接从这里返回
end;//以下是编译后才有的
pop ebp //以下是编译器将添加的两句, 直接越过它们返回
ret $C //
...

to ScriptBaby(Kanna·Naraku) :
返回值就是通过EAX,如果返回值是64位的,就用EDX:EAX双寄存器返回。
ScriptBaby 2007-05-24
  • 打赏
  • 举报
回复
坐井观天同学研究比我要深入, 不佩服不行
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
再画蛇添足一把:

如果用register约定调用函数, 则DLL函数应为:

function GetMax: integer; stdcall; export;
//~~~~~~这里虽为stdcall, 无关痛痒, 前面已解释
asm
push ebp
mov ebp, esp
add eax, edx //第1、2个参数
add eax, ecx //第3个参数
add eax, [ebp+$08]//第4个参数
add eax, [ebp+$0C]//第5个参数
pop ebp
ret 8 //第4、5个参数入栈,所以下一条指令偏移2*sizeof(integer)=8个字节
end;
ScriptBaby 2007-05-24
  • 打赏
  • 举报
回复
看来我蒙对了...
lihuasoft 2007-05-24
  • 打赏
  • 举报
回复
另外, 楼主把上面这个函数稍加改动, 放入DLL, 做为一个无参函数:

function GetMax: integer; stdcall; export;
asm
push ebp //////!!
mov ebp, esp
xor eax, eax
add eax, [ebp+$08]//第1个参数
add eax, [ebp+$0C]//第2个参数
add eax, [ebp+$10]//第3个参数,这三个参数累加到EAX做为返回值
pop ebp
ret 16//四个参数入栈,所以下一条指令偏移16个字节
end;

然后, 在application里调用:

var
Func : function(var1,var2,var3,var4 : integer): Integer; stdcall;
//~~~~~~~~~~
max: integer;
LibHandle: THandle;
begin
LibHandle := LoadLibrary('Test.dll');
@Func := GetProcAddress(LibHandle, 'GetMax');
max := Func(10,20,30,16);//60
Showmessage(inttostr(max));
FreeLibrary(LibHandle);
end;

应该就是不出错的了.

所以, 我认为, 之所以用register约定调用不出错, 是因为register约定前三个参数通过寄存器传递, 而如果超过三个参数, 将会是与stdcall一样的结果. 之所以stdcall会出错, 就是因为参数压栈修改了栈顶指针, 所以在子程序返回时一定要维护栈顶, 并指向下一条指令的正确位置.
lihuasoft 2007-05-23
  • 打赏
  • 举报
回复
算了, 看书去. 汗自己一个. ^_^
lihuasoft 2007-05-23
  • 打赏
  • 举报
回复
哦,我看错了. 是的是的.
lihuasoft 2007-05-23
  • 打赏
  • 举报
回复
要用到eax? 那函数的返回值?
王集鹄 2007-05-23
  • 打赏
  • 举报
回复
汇编里的ret和C里的return是有区别的
具体可以找找关于汇编的资料

return 0x01234567;
编译为:

mov eax,0x01234567
ret
lihuasoft 2007-05-23
  • 打赏
  • 举报
回复
也就是这个意思:
push ebp
mov ebp, esp
mov eax, [ebp+$08]//---
//... // |
pop ebp // |
ret X <--------------/

pop ebp //以下是编译器将添加的两句, 直接越过它们返回
ret $C //

占用楼主贴子了. 不好意思. 不过话题仍然与本贴有关.
lihuasoft 2007-05-23
  • 打赏
  • 举报
回复
我的意思就是说,在代码里这样写:

function Test: integer; stdcall;
asm
push ebp //保存基址?
mov ebp, esp
mov eax, [ebp+$08] //取第一个参数
//...
pop ebp
ret 数 //就是这个立即数,可不可以人工给出
end;

编译后将是

push ebp //保存基址?
mov ebp, esp
mov eax, [ebp+$08] //取第一个参数
//...
pop ebp
ret 数 //就是这个立即数,可不可以人工给出
pop ebp //让这两句失效
ret $C //

在摸索着学习,别笑我
lihuasoft 2007-05-23
  • 打赏
  • 举报
回复
有一事不明请教伴水清清:

编译器在子程序结束后, 有一句:

RET 立即数

这个立即数是主程序指令的段内偏移地址吗?必须由编译器给出吗?可不可以人工控制?
王集鹄 2007-05-23
  • 打赏
  • 举报
回复
鼓掌 啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪啪

首先感谢CCTV、感谢MTV、感谢政府、感谢党,让我把程序调试出来... ...
加载更多回复(8)

5,379

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧