效果图
显式跟隐式调用差不多的,就重新画了窗体,画的有点粗糙。
DLL文件
DLL文件是一种包含了可执行代码的库文件,但它不能独立运行,必须由其他程序(如EXE文件)显式或隐式地加载并调用。DLL文件通常用于实现代码的复用、模块化以及跨语言调用等功能。
如何理解跨语言调用?
在不同编程语言之间调用彼此的函数、过程或对象等代码单元的能力。这种机制允许开发者在混合编程环境中充分利用各种编程语言的优势,实现功能的整合与扩展。
开发者可以将特定功能的代码编译成DLL文件,然后在其他语言编写的程序中通过特定的调用机制(如LoadLibrary和GetProcAddress在Windows上)来加载和调用DLL中的函数。
DLL文件的创建
-
新建DLL项目:在Delphi5的IDE中,选择“File”->“New”->“Others…”,然后在弹出的对话框中选择“DLL Wizard”或直接在“Project”菜单下选择“New”->“DLL”,点击“OK”按钮创建一个新的DLL项目。
-
编写代码:在DLL项目中,可以编写函数、过程等代码,这些代码将被编译到DLL文件中。需要注意的是,DLL中的函数和过程需要使用特定的调用约定(如stdcall、cdecl等),以确保它们能被其他程序正确调用。
library Project2;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes;
{$R *.RES}
{定义函数时使用的调用约定,如果程序员希望自己的DLL库函数能够被其他程序设计语言的程序调用,应使用stdcall调用约定。}
function Max(x,y,z:Integer):Integer;stdcall;
var
t:Integer;
begin
if x>y then
t:=x
else
t:=y;
if t<z then
t:=z;
Result:=t;
end;
function Min(x,y,z:Integer):Integer;stdcall;
var
t:Integer;
begin
if x<y then
t:=x
else
t:=y;
if t>z then
t:=z;
Result:=t;
end;
{DLL的过程和函数想要在外部被使用,要用exports语句声明供其他应用程序调用的函数和过程名}
exports
Max,Min;
begin
end.
-
导出函数和过程:在DLL项目的源代码中,需要使用exports语句来导出希望被其他程序调用的函数和过程。只有被导出的函数和过程才能在DLL被加载后,通过GetProcAddress等函数被其他程序获取其地址并调用。
-
编译DLL:编写并导出完所需的函数和过程后,就可以编译DLL项目了。编译成功后,将生成一个DLL文件,该文件可以被其他程序加载并调用其中的函数和过程。
DLL文件的调用
隐式调用DLL
静态调用:在Delphi5的源代码中,使用external指示字列出要从DLL中调用的例程。这种方式需要在应用程序开始执行前就将DLL装入内存,并在单元的interface部分用external指示字列出要从DLL中调用的例程。但是,静态调用的缺点是程序在启动时如果找不到DLL将无法运行。
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Dialogs, Forms,Form, Formprpt, StdCtrls;
type
TForm1 = class(MForm)
grp1: TGroupBox;
edt1: TEdit;
Edit1: TEdit;
edt2: TEdit;
lbl1: TLabel;
lbl2: TLabel;
lbl3: TLabel;
Label1: TLabel;
lbl4: TLabel;
edt3: TEdit;
btn1: TButton;
btn2: TButton;
procedure btn1Click(Sender: TObject);
procedure btn2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
{静态载入在这声明
隐式调用又称静态调用或装载时调用,对应于DLL的静态载入。
要在应用程序中隐式调用某个动态链接库中的函数,
一般要用external语句声明要调用的过程或函数及其所在的DLL文件名。}
function Max(x,y,z:integer):integer;stdcall;external 'Project2.dll';
function Min(x,y,z:integer):integer;stdcall;external 'Project2.dll';
implementation
{$R *.DFM}
procedure TForm1.btn1Click(Sender: TObject);
var
x,y,z,max_v:Integer;
begin
x:=StrToInt(edt1.text);
y:=StrToInt(edit1.text);
z:=StrToInt(edt2.text);
max_v:=Max(x,y,z);
edt3.Text:=IntToStr(max_v);
end;
procedure TForm1.btn2Click(Sender: TObject);
var
x,y,z,min_v:Integer;
begin
x:=StrToInt(edt1.text);
y:=StrToInt(edit1.text);
z:=StrToInt(edt2.text);
min_v:=Min(x,y,z);
edt3.Text:=IntToStr(min_v);
end;
end.
显式调用DLL
动态调用:使用Windows API函数LoadLibrary和GetProcAddress来实现在运行时间里的动态装载DLL,并调用其中的过程。这种方式可以在需要时才加载DLL,避免了静态调用中可能出现的问题。同时,动态调用还能处理找不到DLL或在装入过程中出错的情况,提高了程序的健壮性。
unit Unit3;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Dialogs, Forms,Form, Formprpt, StdCtrls;
type
TForm1 = class(MForm)
grp1: TGroupBox;
edt1: TEdit;
edt2: TEdit;
edt3: TEdit;
edt4: TEdit;
btn1: TButton;
btn2: TButton;
procedure btn1Click(Sender: TObject);
procedure btn2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{动态调用DLL}
type
TintFunction = Function(x,y,z:Integer):Integer;stdcall;
var
x,y,z:Integer;
{$R *.DFM}
{定义一个模板,这样只需要把函数名传给这里,就能调用函数了}
Function Calculate(FunName:PChar):Integer;
var
DllName:string;
Hinst:THandle;
ProcName:PChar;
Fpointer:TFarProc;
Myfunc:TintFunction;
begin
GetDir(0,DllName);//当前目录
DllName:=DllName+'\Project2.dll'; //获取DLL文件名
ProcName:=FunName; //函数名
Hinst:=SafeLoadLibrary(DllName); //加载DLL文件
if Hinst>0 then
try
Fpointer:=GetProcAddress(Hinst,ProcName); //获得API函数的地址,放到指针那
if Fpointer<>nil then
begin
Myfunc:=TintFunction(Fpointer); //获取该地址的函数名
Result:=Myfunc(x,y,z); //运行函数获得结果
end
else
ShowMessage('需要的函数不存在');
finally
FreeLibrary(Hinst);
end
else
ShowMessage(DllName+'文件不存在');
end;
//点击按钮调用DLL库函数
procedure TForm1.btn1Click(Sender: TObject);
var
Funname:PChar;
Res:Integer;
begin
Funname:='Max';
x:=StrToInt(edt1.text);
y:=StrToInt(edt2.text);
z:=StrToInt(edt3.text);
Res:=Calculate(Funname);
edt4.Text:=IntToStr(Res);
end;
procedure TForm1.btn2Click(Sender: TObject);
var
Funname:PChar;
Res:Integer;
begin
Funname:='Min';
x:=StrToInt(edt1.text);
y:=StrToInt(edt2.text);
z:=StrToInt(edt3.text);
Res:=Calculate(Funname);
edt4.Text:=IntToStr(Res);
end;
end.