• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Delphi之DLL知识学习5---在Delphi应用程序中使用DLL

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

  首先说明一下:同一个动态库(DLL)被多个的程序加载的话,那么将会在每次加载的时候都会重新分配新的独立的内存空间,绝对不是共用一个,所以当一个DLL被多次加载的时候,其会在内存中“复制”多份,不会互相之间 产生影响。

 

  加载DLL有两种方式:隐式和显式。下面就以刚创建的DLL为例,来介绍两种方式

一、隐式

  本章创建的第一个 D L L包含一个接口单元。下面就用该接口单元来隐式链接 DLL。这个工程的主窗体上有一个TMadkEdit、一个TButton和九个TLabel。

  在这个应用程序里,用户输入美分的数目,然后单击按钮,标签上将按条目显示其结果。这结果是来源于PenniesLib.dll中引出的例程 PenniesToCoins()。

  主窗体是在MainFrm.pas单元里定义的,代码

unit MainRrm;

interface
uses
    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
    Forms, Dialogs, StdCtrls, Mask;
type
    TMainForm = class(TForm)
        lblTotal: TLabel;
        lblQlbl: TLabel;
        lblDlbl: TLabel;
        lblNlbl: TLabel;
        lblPlbl: TLabel;
        lblQuarters: TLabel;
        lblDimes: TLabel;
        lblNickels: TLabel;
        lblPennies: TLabel;
        btnMakeChange: TButton;
        meTotalPennies: TMaskEdit;
        procedure btnMakeChangeClick(sender: TObject);
    end;

var MainForm: TMainForm;

implementation
uses PenniesInt;
{R *.DFM}

procedure TMainForm.btnMakeChangeClick(Sender: TObject);
var
    CoinsRec: TCoinsRec;
    TotPennies: word;
begin
    {call the DLL function to determine the minimum coins required
    for the amount of pennies specified}
    TotPennies:= PenniesToCoins(StrToInt(meTotalPennies.Text), @CoinsRec);
    with CoinsRec do
    begin
        {Now display the coin information}
        lblQuarters.Caption:= IntToStr(Quarters);
        lblDimes.Caption:= IntToStr(Dimes);
        lblNickels.Caption:= IntToStr(Nickels);
        lblPennies.Caption:= IntToStr(Pennies);
    end;
end;

end.

  注意MainFrm.pas使用了接口单元PenniesInt。PenniesInt.pas包括了PenniesLib.dpr中的函数的外部声明。当应用程序运行时,Win 32系统会自动调入 PenniesLin.dll,并将其映射到该应用程序的进程地址空间

  import 接口单元其实是可选的,可以不用 PenniesInt单元,而在 MainFrm.pas 的 implementation部分外部声明 PenniesToCoins() 函数即可,代码如下

implementation

function PenniesToCoins(TotPennies: word; ChangeRec: PChangeRec): Word; stdcall; external 'PENNIESLIB.DLL';

  不过,在MainFrm.pas 还要再次定义 PChangeRec 和TChangeRec,要不然也可以使用编译指令 PENNIESLIB 编译你的应用程序。在只需要访问DLL中的少数例程时,这种技术比较合适。不过,大多数情况下是既要访问DLL中的例程,又要引用 DLL中定义的数据类型,这时候最好使用接口单元

  注意:有些时候,需要调用其他软件厂商的DLL,这时候就不会有 Pascal的接口单元,不过,也许有 C/C++的引入库。如果是这样的话,就只要显将该库转换为等效的Pascal接口单元。

 

二、显式调用

  尽管隐式调用很方便,但不是最理想的方法。假使有一个包含多个例程的DLL,可能这些例程却用不着,这样的话,调入该DLL显然浪费了内存。尤其是一个应用程序需要使用多个DLL时,这就更浪费了。另一种情况是,一个多版本的标准函数,如一组打印驱动程序,分别是由多个DLL来实现的。这种情况下,如果能需要哪一个版本的就调用其DLL,这最好不过了。这种方式就是显示调用。

unit MainFrm;

interface
uses
    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
    Forms, Dialogs, StdCtrls;

type
    TShowCalendar = function(AHandle: THandle; ACaption: String): TDateTime; Stdcall;

    EDLLLoadError = class(Exception);

    TMainForm = class(TForm)
        lblDate: TLabel;
        btnGetCalendar: TButton;
        procedure btnGetCalendarClick(Sender: TOnject);
    end;

var
    MainForm: TMainForm;

implementation
{$R *.DFM}

procedure TMainForm.btnGetCalendarClick(Sender: TObject);
var
    LibHandle: THandle;    //!!!!!
    ShowCalendar: TShowCalendar;    //!!!!!!
begin
    LibHandle:= LoadLibrary('CALENDARLIB.DLL');    //!!!!!!
    try
        if LibHandle = 0 then
            raise EDLLLoadError.Create('Unable to load DLL');

        @ShowCalendar:= GetProcAddress(LibHandle, 'ShowCalendar');

        if not(@ShowCalendar = nil) then
            lblData.Caption:= DateToStr(ShowCalendar(Application.Handle, Caption))
        else
            RaiseLastWin32Error;
    finally
        FreeLibrary(LibHandle);
    end;
end;

end.

  上面这个单元首先定义了一个过程数据类型 TShowCalendar,该类型就是 CalendarLib.dll 中的函数类型。接着,又声明一个特殊的异常类,当调用失败时,就会引发这个异常。在 btnGetCalendarClick() 事件处理过程中,使用了三个Win32 API函数:LoadLibrary()、FreeLibrary()、GetProcAddress()。

  LoadLibrary()声明如下

function LoadLibrary(lpLibFileName: PChar): HMODULE; stdcall;

  上述函数调入有 lpLibFileName参数指定的DLL模块,并将其映射到调用进程的地址空间。如果调用成功,函数将返回该模块的句柄;若失败,返回值为0,并触发一异常。你可以查阅在线帮助中 LoadLibrry() 函数的详细说明以及可能返回的错误值

  FreeLibrary()声明如下

function FreeLibrary(hLibModule: HMODULE): BOOl; stdcall;

  FreeLibrary()函数减少LibModule指定的库的实例计数。当该DLL的实例计数是零时,调用的DLL就会被释放。实例计数记录使用这个DLL的任务数

  GetProcAddress()是这样定义的

function GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall;

  GetProcAddress() 返回的是一个函数在模块中的地址,其中由 hModule参数指定模块。hModule是从 LoadLibrary() 函数返回的结果THandle。如果 GetProcAddress()调用失败,则返回 nil。你只有调用GetLastError()才能获得详细的错误信息。

  在处理 Button1 的OnClick事件处理过程中,首先调用LoadLibrary()去调用CALDLL。如果失败,则触发异常。如果成功,调用 GetProcAddress()来获得函数 ShowCalendar()的地址。在变量ShowCalendar 前加上取址运算符@,这样就不会在编译时出现类型转换错误。得到函数 ShowCalendar()的地址后,就可以按 TShowCalendar定义的使用它了。最后不再使用时,一定要在 finally 块中调用FreeLibrary() 来释放它。

  你也许明白,每次调用该函数时, DLL既要调入,又要释放。如果在应用程序运行时,只需要调用一次,显式调用的优势就很明显,它能够有效地减少内存的消耗。但是,如果该函数要被频繁地调用,那么频繁地调入和释放会增加系统负担。


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Matlab7.0利用mcc与VC++编程实例发布时间:2022-07-18
下一篇:
Stockwelltransforminmatlab发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap