程序在运行时就载入了调动态连接库,我想使其动态的载入,即在运行时不载入,再用时在动态载入dll,
如何实现?

解决方案 »

  1.   

    在Delphi中动态调用DLL
        动态调用DLL相对复杂很多,但非常灵活。为了全面的说明该问题,这次我们举一个调用由C++编写的DLL的例子。首先在C++中编译下面的DLL源程序。
    #include
    extern "C" _declspec(dllexport)
    int WINAPI TestC(int i)
    {
    return i;
    }
        编译后生成一个DLL文件,在这里我们称该文件为Cpp.dll,该DLL中只有一个返回整数类型的函数TestC。为了方便说明,我们仍然引用上面的调用程序,只是将原来的Button1Click过程中的语句用下面的代码替换掉了。
    procedure TForm1.Button1Click(Sender: TObject);
    type
    TIntFunc=function(i:integer):integer;stdcall;
    var
    Th:Thandle;
    Tf:TIntFunc;
    Tp:TFarProc;
    begin
    Th:=LoadLibrary('Cpp.dll'); {装载DLL}
    if Th>0 then
    try
    Tp:=GetProcAddress(Th,PChar('TestC'));
    if Tp<>nil
    then begin
    Tf:=TIntFunc(Tp);
    Edit1.Text:=IntToStr(Tf(1)); {调用TestC函数}
    end
    else
    ShowMessage('TestC函数没有找到');
    finally
    FreeLibrary(Th); {释放DLL}
    end
    else
    ShowMessage('Cpp.dll没有找到');
    end;
        大家已经看到了,这种动态调用技术很复杂,但只要修改参数,如修改LoadLibrary('Cpp.dll')中的DLL名称为'Delphi.dll'就可动态更改所调用的DLL。
    一、定义所要调用的函数或过程的类型。
        在上面的代码中我们定义了一个TIntFunc类型,这是对应我们将要调用的函数TestC的。在其他调用情况下也要做同样的定义工作。并且也要加上stdcall调用参数。
    二、释放所调用的DLL。
        我们用LoadLibrary动态的调用了一个DLL,但要记住必须在使用完后手动地用FreeLibrary将该DLL释放掉,否则该DLL将一直占用内存直到您退出Windows或关机为止。
        现在我们来评价一下两种调用DLL的方法的优缺点。静态方法实现简单,易于掌握并且一般来说稍微快一点,也更加安全可靠一些;但是静态方法不能灵活地在运行时装卸所需的DLL,而是在主程序开始运行时就装载指定的DLL直到程序结束时才释放该DLL,另外只有基于编译器和链接器的系统(如Delphi)才可以使用该方法。动态方法较好地解决了静态方法中存在的不足,可以方便地访问DLL中的函数和过程,甚至一些老版本DLL中新添加的函数或过程;但动态方法难以完全掌握,使用时因为不同的函数或过程要定义很多很复杂的类型和调用方法。对于初学者,笔者建议您使用静态方法,待熟练后再使用动态调用方法。
    第五章 使用DLL的实用技巧
    一、编写技巧。
    1 、为了保证DLL的正确性,可先编写成普通的应用程序的一部分,调试无误后再从主程序中分离出来,编译成DLL。
    2 、为了保证DLL的通用性,应该在自己编写的DLL中杜绝出现可视化控件的名称,如:Edit1.Text中的Edit1名称;或者自定义非Windows定义的类型,如某种记录。
    3 、为便于调试,每个函数和过程应该尽可能短小精悍,并配合具体详细的注释。
    4 、应多利用try-finally来处理可能出现的错误和异常,注意这时要引用SysUtils单元。
    5 、尽可能少引用单元以减小DLL的大小,特别是不要引用可视化单元,如Dialogs单元。例如一般情况下,我们可以不引用Classes单元,这样可使编译后的DLL减小大约16Kb。
    二、调用技巧。
    1 、在用静态方法时,可以给被调用的函数或过程更名。在前面提到的C++编写的DLL例子中,如果去掉extern "C"语句,C++会编译出一些奇怪的函数名,原来的TestC函数会被命名为@TestC$s等等可笑的怪名字,这是由于C++采用了C++ name mangling技术。这个函数名在Delphi中是非法的,我们可以这样解决这个问题:
    改写引用函数为
    function TestC(i:integer):integer;stdcall;
    external 'Cpp.dll';name '@TestC$s';
    其中name的作用就是重命名。
    2 、可把我们编写的DLL放到Windows目录下或者Windows\system目录下。这样做可以在external语句中或LoadLibrary语句中不写路径而只写DLL的名称。但这样做有些不妥,这两个目录下有大量重要的系统DLL,如果您编的DLL与它们重名的话其后果简直不堪设想,况且您的编程技术还不至于达到将自己编写的DLL放到系统目录中的地步吧!
    三、调试技巧。
    1 、我们知道DLL在编写时是不能运行和单步调试的。有一个办法可以,那就是在Run|parameters菜单中设置一个宿主程序。在Local页的Host Application栏中添上宿主程序的名字就可进行单步调试、断点观察和运行了。
    2 、添加DLL的版本信息。开场白中提到了版本信息对于DLL是很重要的,如果包含了版本信息,DLL的大小会增加2Kb。增加这么一点空间是值得的。很不幸我们如果直接使用Project|options菜单中Version选项是不行的,这一点Delphi的帮助文件中没有提到,经笔者研究发现,只要加一行代码就可以了。如下例:
    library Delphi;
    uses
    SysUtils,
    Classes;
    {$R *.RES}
    //注意,上面这行代码必须加在这个位置
    function TestDll(i:integer):integer;stdcall;
    begin
    Result:=i;
    end;
    exports
    TestDll;
    begin
    end.
    3 、为了避免与别的DLL重名,在给自己编写的DLL起名字的时候最好采用字符数字和下划线混合的方式。如:jl_try16.dll。
    4 、如果您原来在Delphi 1或Delphi 2中已经编译了某些DLL的话,您原来编译的DLL是16位的。只要将源代码在新的Delphi 3或Delphi 4环境下重新编译,就可以得到32位的DLL了。
    [后记]:除了上面介绍的DLL最常用的使用方法外,DLL还可以用于做资源的载体。例如,在Windows中更改图标就是使用的DLL中的资源。另外,熟练掌握了DLL的设计技术,对使用更为高级的OLE、COM以及ActiveX编程都有很多益处。 
    /////////////////////////////
      

  2.   

    1、定义一个过程类型,些类型要与你从DLL中调用的的函数类型一致:
    type
     TMyProc=procedure(i:integer);stdcall;//如果是函数换成Function
    2、使用API函数LoadLibrary、GetProcAddress、FreeLibrary动态装载DLL、查找调用过程的地址、释放DLL
    var
      dllHandle:Thandle;
      MyProc:TMyProc;
    begin
      dllHandle:=LoadLibrary('mydll.dll');//装入
      @MyProc:= GetProcAddress(dllHandle,'MyTestProc');//查找过程地址,@MyProc将MyProc转换为Pointer类型,与GetProcAddress返回类型一致
      MyProc(100);//调用函数
    ....
      FreeLibrary(dllHandle);//释放dll 
    end;
      

  3.   

    要知道要用其中哪些函数,然后在开头申明,在程序中就可直接使用了.在网上下载别人的DLL然后看看他的调用文档,很容易明白的.