效果图
显式跟隐式调用差不多的,就重新画了窗体,画的有点粗糙。
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 thefirst unit in your library's USES clause AND your project's (selectProject-View Source) USES clause if your DLL exports any procedures orfunctions that pass strings as parameters or function results. Thisapplies to all strings passed to and from your DLL--even those thatare nested in records and classes. ShareMem is the interface unit tothe BORLNDMM.DLL shared memory manager, which must be deployed alongwith your DLL. To avoid using BORLNDMM.DLL, pass string informationusing PChar or ShortString parameters. }usesSysUtils,Classes;{$R *.RES}{定义函数时使用的调用约定,如果程序员希望自己的DLL库函数能够被其他程序设计语言的程序调用,应使用stdcall调用约定。}
function Max(x,y,z:Integer):Integer;stdcall;
vart:Integer;
beginif x>y thent:=xelset:=y;if t<z thent:=z;Result:=t;
end;function Min(x,y,z:Integer):Integer;stdcall;
vart:Integer;
beginif x<y thent:=xelset:=y;if t>z thent:=z;Result:=t;
end;{DLL的过程和函数想要在外部被使用,要用exports语句声明供其他应用程序调用的函数和过程名}
exportsMax,Min;begin
end.
-
导出函数和过程:在DLL项目的源代码中,需要使用exports语句来导出希望被其他程序调用的函数和过程。只有被导出的函数和过程才能在DLL被加载后,通过GetProcAddress等函数被其他程序获取其地址并调用。
-
编译DLL:编写并导出完所需的函数和过程后,就可以编译DLL项目了。编译成功后,将生成一个DLL文件,该文件可以被其他程序加载并调用其中的函数和过程。
DLL文件的调用
隐式调用DLL
静态调用:在Delphi5的源代码中,使用external指示字列出要从DLL中调用的例程。这种方式需要在应用程序开始执行前就将DLL装入内存,并在单元的interface部分用external指示字列出要从DLL中调用的例程。但是,静态调用的缺点是程序在启动时如果找不到DLL将无法运行。
unit Unit1;interfaceusesSysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Dialogs, Forms,Form, Formprpt, StdCtrls;typeTForm1 = 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;varForm1: 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);
varx,y,z,max_v:Integer;
beginx:=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);
varx,y,z,min_v:Integer;
beginx:=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;interfaceusesSysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Dialogs, Forms,Form, Formprpt, StdCtrls;typeTForm1 = 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;varForm1: TForm1;implementation{动态调用DLL}
typeTintFunction = Function(x,y,z:Integer):Integer;stdcall;varx,y,z:Integer;{$R *.DFM}{定义一个模板,这样只需要把函数名传给这里,就能调用函数了}
Function Calculate(FunName:PChar):Integer;
varDllName:string;Hinst:THandle;ProcName:PChar;Fpointer:TFarProc;Myfunc:TintFunction;
beginGetDir(0,DllName);//当前目录DllName:=DllName+'\Project2.dll'; //获取DLL文件名ProcName:=FunName; //函数名Hinst:=SafeLoadLibrary(DllName); //加载DLL文件if Hinst>0 thentryFpointer:=GetProcAddress(Hinst,ProcName); //获得API函数的地址,放到指针那if Fpointer<>nil thenbeginMyfunc:=TintFunction(Fpointer); //获取该地址的函数名Result:=Myfunc(x,y,z); //运行函数获得结果endelseShowMessage('需要的函数不存在');finallyFreeLibrary(Hinst);endelseShowMessage(DllName+'文件不存在');
end;//点击按钮调用DLL库函数
procedure TForm1.btn1Click(Sender: TObject);
varFunname:PChar;Res:Integer;
beginFunname:='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);
varFunname:PChar;Res:Integer;
beginFunname:='Min';x:=StrToInt(edt1.text);y:=StrToInt(edt2.text);z:=StrToInt(edt3.text);Res:=Calculate(Funname);edt4.Text:=IntToStr(Res);
end;end.