delphi调用DLL中的函数,函数类型要一样吗?请大家看看,该如何解决
delphi调用DLL中的函数,函数类型要一样吗?请大家看看
情况如下:
用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中定义的函数名称相同,参数不一样都可以调用啊。
------解决方案--------------------
实际上,对于函数GetMax来说,你只是使用了它的一个入口地址而已。并且DLL定义的函数GetMax,其调用约定是stdcall,而你在实际调用时,却没有遵守这个约定,而是使用了Delphi默认的register约定。所以,参数仍旧按register方式传进了函数a。我把你的函数和调用过程改了一下,你看一下:
//DLL函数:
function GetMax : integer; stdcall; export;
//下面的句子, 是把EAX、EDX、ECX三个寄存器的值相加,并赋值给Result
//register调用约定下,函数参数(三个以内),优先使用上述三个寄存器传递
//因为你只是使用了GetMax的入口地址,所以上面的stdcall约定无效
asm
ADD EAX, EDX
ADD EAX, ECX
end;
//调用
procedure TForm1.Button1Click(Sender: TObject);
var
a: function(var1,var2,var3 : integer): Integer;//注意此处,默认是register方式
max: integer;
LibHandle: THandle;
begin
LibHandle := LoadLibrary( 'Test.dll ');
try
if LibHandle <> 0 then
begin
@a := GetProcAddress(LibHandle, 'GetMax ');
max := a(4, 2, 3); //三个参数
Showmessage(inttostr(max)); // 9
end;
finally
FreeLibrary(LibHandle);
end;
end;
如果在函数a声明时,改为:
a: function(var1,var2,var3 : integer): Integer; stdcall;
则会出错。
------解决方案--------------------
无论什么编程语言,最终都是会翻译成机器语言执行,因为机器只能读懂机器语言
在机器语言这个级别:函数、过程、类、类型、事件、方法等等都不存在
只有机器指令和操作数
a( ' ', true);
被翻译成机器指令大概是:
mov eax,$00xx0000
mov dl,$01
call a
就是向寄存器填写参数
在dll中的函数也没有访问这两寄存器(eax、dl)所以相安无事
如果参数多余3个,则第四个参数器会做入栈操作,如果dll函数没有出栈操作这会导致异常
var
a: function(a, b, c, d: Integer): Integer; //可以这样测试
通过IDE的CPU查看器可以看到执行的指令
能执行通只能说是纯属巧合而已
情况如下:
用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中定义的函数名称相同,参数不一样都可以调用啊。
------解决方案--------------------
实际上,对于函数GetMax来说,你只是使用了它的一个入口地址而已。并且DLL定义的函数GetMax,其调用约定是stdcall,而你在实际调用时,却没有遵守这个约定,而是使用了Delphi默认的register约定。所以,参数仍旧按register方式传进了函数a。我把你的函数和调用过程改了一下,你看一下:
//DLL函数:
function GetMax : integer; stdcall; export;
//下面的句子, 是把EAX、EDX、ECX三个寄存器的值相加,并赋值给Result
//register调用约定下,函数参数(三个以内),优先使用上述三个寄存器传递
//因为你只是使用了GetMax的入口地址,所以上面的stdcall约定无效
asm
ADD EAX, EDX
ADD EAX, ECX
end;
//调用
procedure TForm1.Button1Click(Sender: TObject);
var
a: function(var1,var2,var3 : integer): Integer;//注意此处,默认是register方式
max: integer;
LibHandle: THandle;
begin
LibHandle := LoadLibrary( 'Test.dll ');
try
if LibHandle <> 0 then
begin
@a := GetProcAddress(LibHandle, 'GetMax ');
max := a(4, 2, 3); //三个参数
Showmessage(inttostr(max)); // 9
end;
finally
FreeLibrary(LibHandle);
end;
end;
如果在函数a声明时,改为:
a: function(var1,var2,var3 : integer): Integer; stdcall;
则会出错。
------解决方案--------------------
无论什么编程语言,最终都是会翻译成机器语言执行,因为机器只能读懂机器语言
在机器语言这个级别:函数、过程、类、类型、事件、方法等等都不存在
只有机器指令和操作数
a( ' ', true);
被翻译成机器指令大概是:
mov eax,$00xx0000
mov dl,$01
call a
就是向寄存器填写参数
在dll中的函数也没有访问这两寄存器(eax、dl)所以相安无事
如果参数多余3个,则第四个参数器会做入栈操作,如果dll函数没有出栈操作这会导致异常
var
a: function(a, b, c, d: Integer): Integer; //可以这样测试
通过IDE的CPU查看器可以看到执行的指令
能执行通只能说是纯属巧合而已