各位仁兄:小弟刚刚接触dll编写,请问:如何编写一个dll里的函数,返回值为string数组,并在前台调用;前台如何调用!(调用方和被调用方均为Delphi程序)谢谢

解决方案 »

  1.   

    不可以
    ,得用pchar 型
      

  2.   

    开发软件时,需要客户端将采用核心与外围剥离的方式开发,核心层负责提供直接的用户界面,负责使用WinAPI打开网络套接字,并对外围的DLL进行各种调用。外围的DLL实现具体功能。  
    我知道,典型的调用DLL中的某个过程,可以这样实现(Delphi语句):  
     
    begin  
    FDLLHandle:=LoadLibrary('AddFunction.dll');//获得句柄  
    if  FDLLHandle>0  then  
       try  
           PAddFunction:=GetProcAddress(FDLLHandle,  'AddFunc');    
           //获得DLL中AddFunc过程的入口地址  
           if  PAddFunction<>nil  then  
               //  do  something  
           else  
               showmessage('dll  没有找到。');  
       finally  
           FreeLibrary(FDLLHandle);  
       end  
    else  
       showmessage('dll  没有找到。');  
    end  
     
    现在核心程序需要多次使用同一DLL中的过程。而且又不能使用多线程编程。例如,按下某个按钮后执行DLL中  
     
    的AddFunc过程。按多次该按钮就执行多次该DLL中的AddFunc过程。因此,上面的程序不需要这样写。只需要在  
     
    主程序中获得DLL句柄,在按钮事件中调用同一个DLL过程:  
     
    //主程序  
    begin  
       FDLLHandle:=LoadLibrary('AddFunction.dll');  
       //该变量设置为全局  传递给单击按钮事件的过程  
       if  FDLLHandle<=0  then  
       begin  
           showmessage('dll  没有找到。');  
           exit();  
       end;//End  for  if  FDLLHandle<=0  
       //开始主程序消息循环  
       //do  something  
    end.  //end  for  main  
     
    按钮的单击事件映射到下面的过程  
    procedure  TForm.Button_Click(Sender:  TObject);  
    begin  
       try  
           PAddFunction:=GetProcAddress(FDLLHandle,  'AddFunc');  
           if  PAddFunction<>nil  then  
               //  do  something  
           else  
               showmessage('AddFunction.dll  没有找到。');  
       finally  
           FreeLibrary(FDLLHandle);  
       end//End  for  try  
    end;//end  for  Proc  Button_Click  
     
    这样就不需要多次载入DLL。因为只需要载入一次,每次传递句柄即可。  
    我也已经实现了这种方式的调用。问题是现在我的主程序在关闭的时候出现了问题:主程序关闭的时候,DLL中  
     
    的过程可能还没有运行完。如果直接使用:  
    if  FDLLHandle>0  then    FreeLibrary(FDLLHandle);  
    那么正在运行的结果是进程死了。无论如何也无法关闭,除非直接杀死进程。请问有什么办法知道该DLL中的过  
     
    程仍然在运行。从而阻止直接释放DLL?  
    例如改写成为:  
    if  FDLLHandle>0  then      
       if  该DLL所以过程都不在运行了  then  
               FreeLibrary(FDLLHandle);  
       else  
       begin  
               showmessage('请先结束所以任务后再退出主程序');  
               //不再结束主程序,返回主程序消息循环  
               //  Do  something  
       end;//end  for  else  of  
     
    请教如何得知DLL是否可以释放了?              
     
     
     
    ---------------------------------------------------------------  
     
    我的建议是将上面的DLL的调用封装成一个类,提供方法供程序调用,在类的构造方法中调用LoadLibrary,析构函数中调用FreeLibrary,提供GetProcAddress的接口函数,这样可以适用同一DLL的多个函数,记数大于零,表明DLL仍在使用。基本框架我写了:  
    TDLLObject    
    private  
       FDLLHandle:  HINSTANCE;//  dll的句柄  
       FReferenceCount:  Integer;  //  已调用函数个数  
    public  
       constructor  Create(sDLLName:  string);  
       destructor  Destroy();  override;  
       function  Reference():  boolean;  //  返回已调用函数个数  
       function  GetProcAddr(sProcName:  string):  FARPROC;  //  获得函数地址  
       procedure  FreeProcReference();  //  函数适用完,将调用记数减1  
    end;  
    函数你自己实现吧,应该比较简单。  
    ---------------------------------------------------------------  
     
    你只调用过一次LoadLibrary,那么DLL的引用计数便只有一次,所以FreeLibrary也仅需调用一次。DLL中的函数调用多少次都没有关系。  
         我怀疑你的DLL中的函数是不是创建了窗体或用到外部控件,这样的话需要先释放或关闭那些东西。这是别人写的  看看
      

  3.   

    Delphi中动态链接库两种调用方式的比较摘要:本文阐述了Windows环境下动态链接库的概念和特点,对静态调用和动态调用两种调用方式作出了比较,并给出了Delphi中应用动态链接库的实例。  一、动态链接库的概念  动态链接库(Dynamic Link Library,缩写为DLL)是一个可以被其它应用程序共享的程序模块,其中封装了一些可以被共享的例程和资源。动态链接库文件的扩展名一般是dll,也有可能是drv、sys和fon,它和可执行文件(exe)非常类似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由Windows应用程序直接或间接调用。  动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的全部代码。当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。  一般情况下,如果一个应用程序使用了动态链接库,Win32系统保证内存中只有DLL的一份复制品,这是通过内存映射文件实现的。DLL首先被调入Win32系统的全局堆栈,然后映射到调用这个DLL的进程地址空间。在Win32系统中,每个进程拥有自己的32位线性地址空间,如果一个DLL被多个进程调用,每个进程都会收到该DLL的一份映像。与16位Windows不同,在Win32中DLL可以看作是每个进程自己的代码。  二、动态链接库的优点  1. 共享代码、资源和数据   使用DLL的主要目的就是为了共享代码,DLL的代码可以被所有的Windows应用程序共享。  2. 隐藏实现的细节   DLL中的例程可以被应用程序访问,而应用程序并不知道这些例程的细节。  3. 拓展开发工具如Delphi的功能  由于DLL是与语言无关的,因此可以创建一个DLL,被C++、VB或任何支持动态链接库的语言调用。这样如果一种语言存在不足,就可以通过访问另一种语言创建的DLL来弥补。  三、动态链接库的实现方法  1. Load-time Dynamic Linking  这种用法的前提是在编译之前已经明确知道要调用DLL中的哪几个函数,编译时在目标文件中只保留必要的链接信息,而不含DLL函数的代码;当程序执行时,利用链接信息加载DLL函数代码并在内存中将其链接入调用程序的执行空间中,其主要目的是便于代码共享。  2. Run-time Dynamic Linking   这种方式是指在编译之前并不知道将会调用哪些DLL函数,完全是在运行过程中根据需要决定应调用哪个函数,并用LoadLibrary和GetProcAddress动态获得DLL函数的入口地址。四、DLL的两种调用方式在Delphi中的比较  编写DLL的目的是为了输出例程供其他程序调用,因此在DLL的工程文件中要把输出的例程用Exports关键字引出。在调用DLL的应用程序中,需要声明用到的DLL中的方法,声明格式要和DLL中的声明一样。访问DLL中的例程有静态调用和动态调用两种方式。静态调用方式就是在单元的Interface部分用External指示字列出要从DLL中引入的例程;动态调用方式就是通过调用Windows的API包括LoadLibrary函数、GetProcAddress函数以及FreeLibrary函数动态的引入DLL中的例程。  静态调用方式所需的代码较动态调用方式所需的少,但存在着一些不足,一是如果要加载的DLL不存在或者DLL中没有要引入的例程,这时候程序就自动终止运行;二是DLL一旦加载就一直驻留在应用程序的地址空间,即使DLL已不再需要了。动态调用方式就可解决以上问题,它在需要用到DLL的时候才通过LoadLibrary函数引入,用完后通过FreeLibrary函数从内存中卸载,而且通过调GetProcAddress函数可以指定不同的例程。最重要的是,如果指定的DLL出错,至多是API调用失败,不会导致程序终止。以下将通过具体的实例说明说明这调用方式的使用方法。  1. 静态调用方式  示例程序创建了一个DLL,其中仅包含一个求两个整数的和的函数,在主程序中输入两个整数,通过调用该DLL,即可求出两个整数的和,如图1所示。
         图1 DLL的静态调用 
     
      该DLL的程序代码如下:library AddNum;
    uses
    SysUtils,
    Classes;{$R *.res}function AddNumber(Num1,Num2:integer):integer;stdcall; //定义求和函数
     begin
      result:=Num1+Num2;
     end;
      exports
      AddNumber; //引出求和函数
     begin
    end.
     
      主程序在调用该DLL时,首先在interface部分声明要调用的函数:function AddNum(Num1,Num2:integer):integer;stdcall;external 'AddNum.dll'
    name 'AddNumber'; 
     
      然后在按钮控件的事件中写入如下代码:procedure TForm1.Button1Click(Sender: TObject);
    var
     Number1,Number2:integer; 
     Sum:integer;
    begin
     Number1:=strtoint(Edit1.Text);
     Number2:=strtoint(Edit2.Text);
     Sum:=AddNum(Number1,Number2); //调用求和函数计算结果
     Edit3.Text:=inttostr(Sum);
    end;
      2.动态调用方式  这个示例程序创建了一个显示日期的DLL,其中包含一个窗体,如图2所示。
         图2 DLL的动态调用 
     
      程序中定义了一个ShowCalendar函数,返回在这个窗体中设定的日期。函数定义如下:function ShowCalendar(AHandle: THandle; ACaption: String): TDateTime;
    var
     DLLForm: TDLLForm;
    begin
     Application.Handle := AHandle;
     DLLForm := TDLLForm.Create(Application); //创建并显示窗体
     try
      DLLForm.Caption := ACaption;
      DLLForm.ShowModal; //显示方式为模式化
      Result := DLLForm.calDLLCalendar.CalendarDate; //返回设定日期
     finally
      DLLForm.Free; //用完后卸载该窗体
     end;
    end;
     
      在DLL的工程文件中用exports ShowCalendar; 语句引出该函数。下面通过一个简单的应用程序测试一下该DLL文件。新建一个工程文件,在窗体中放置一个Label控件和一个按钮控件,在按钮控件的OnClick事件中编写如下代码:procedure TMainForm.Button1Click(Sender: TObject);
    var
     OneHandle : THandle; //定义一个句柄变量
    begin
     OneHandle := LoadLibrary('Clendar.dll'); //动态载入DLL,并返回其句柄
     try
      if OneHandle <> 0 then //如果载入成功则获取ShowCalendar函数的地址
       @ShowCalendar := GetProcAddress(OneHandle, 'ShowCalendar');
       if not (@ShowCalendar = nil) then
        //如果找到该函数则在主窗体的Label1中显示DLL窗体中设定的日期
        Label1.Caption := DateToStr(ShowCalendar(Application.Handle, Caption))
       else
        RaiseLastWin32Error;
     finally
      FreeLibrary(OneHandle); //调用完毕收回DLL占用的资源
     end;
    end;
     
      从以上程序中可以看到DLL的动态调用方式比静态调用方式的优越之处。DLL例程在用到时才被调入,用完后就被卸载,大大减少了系统资源的占用。在调用LoadLibrary函数时可以明确指定DLL的完整路径,如果没有指定路径,运行时首先查找应用程序载入的目录,然后是Windows系统的System目录和环境变量Path设定的路径。
      

  4.   

    五、结束语  由于动态链接库可以实现代码和资源的共享,大大减少系统资源的占用,因此在当今的应用程序开发中起着非常重要的作用。Delphi是现今流行的应用软件开发工具,本文就如何在Delphi中使用动态链接库给出了一定程度上的阐述。
    Delphi中动态链接库(DLL)的建立和使用1 
     
    中国风网 2004-1-12 16:07:42 
     --------------------------------------------------------------------------------
       动态链接库是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源。由于DLL代码使用了内存共享技术,在某些地方windows也给了DLL一些更高的权限,因而DLL中可以实现一些一般程序所不能实现的功能,如实现windows的HOOK、ISAPI等。  同时,DLL还为不同语言间代码共享提供了一条方便的途径。因而DLL在编程时应用较为广泛,本文将介绍如何在 Delphi 中建立和使用DLL。  一.DLL 库内存共享机制  从使用效果看,DLL和unit 很像,它们都可以被别的工程模块所调用,但二者在内部的实现机制上确存在着差别。如果一个程序模块中用uses语句引用了某个unit,编译程序在编译该模块时,便会连同unit一起编译,并把编译后的可执行代码链接到本程序模块中,这就是一个程序模块能够调用所引用unit中过程和函数的原因。  当同一个unit被多个工程所引用时,则每个工程中都含有该unit的可执行代码,当含有该unit的多个工程同时执行时,unit的可执行代码会随不同工程而多次被调入内存,造成内存资源的浪费。DLL则不同,它即使被某个工程调用,编译后仍是独立的。  也就是说编译后,一个DLL库形成一个单独的可执行文件,而不与任何其它的可执行文件连接在一起,因而DLL库并不从属于某个特定的工程,当多个工程调用同一个DLL库时只有第一个工程把DLL库调入内存,其余工程并不重复调入同一个DLL库到内存,而是到同一个共享内存区读取。并且,DLL的执行代码是在程序运行期间动态调入的,而不是如unit在程序运行时就与整个工程一起调入内存。这样便可消除unit带来的相同代码多处占用内存的弊病。  二 Delphi中DLL库的建立 在Delphi环境中,编写一个DLL同编写一个一般的应用程序并没有太大的区别。事实上作为DLL主体的DLL函数的编写,除了在内存、资源的管理上有所不同外,并不需要其它特别的手段。    一般工程文件的格式为:
      program  工程标题; 
      uses    子句; 
      程序体
      而DLLs工程文件的格式为:
      library 工程标题; 
      uses 子句; 
      exprots 子句; 
      程序体 
    它们主要的区别有两点:  1.一般工程文件的头标用program关键字,而DLL工程文件头标用library 关键字。不同的关键字通知编译器生成不同的可执行文件。用program关键字生成的是.exe文件,而用library关键字生成的是.dll文件;  2.假如DLL要输出供其它应用程序使用的函数或过程,则必须将这些函数或过程列在exports子句中。而这些函数或过程本身必须用export编译指令进行编译。  在Delphi主菜单file 中选new...项,在弹出的窗口中双击DLL图标,便会自动给出DLL源模块框架,如下:  Library project1;
      {...注释...}
      uses
      SysUtils, Classes;
      begin
      end.   接下来便可在USES和begin之间加入想在该DLL中实现的过程和函数的定义,并用export和exprots保字把它们引出,以便别的模块引用,在begin和end之间加入初始化代码,初始化代码是用来对DLL变量初始化的。应注意,即便无初始化代码begin与end也不可省略,如下例:  library minmax;
      function Min(X, Y: Integer): Integer; export;
      begin
      if X < Y then Min := X else Min := Y;
      end;
      function Max(X, Y: Integer): Integer; export;
      begin
      if X > Y then Max := X else Max := Y;
      end;
      exports
      Min index 1,
      Max index 2;
      begin
      end.   经编译后,并以minmax.DLL存盘后,一个DLL库文件便形成了 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>你的问题是:  把 string  数组   变成  tstringlist型的 然后墙制转换成  pchar 型  回头 主程序中调用时,强制转化回来,就可以了 是  stirng 的数组了  
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      

  5.   

    返回这个字符串数组的 指针
    同时 在主程序中定义一个 和 dll的 string 数组类型的一样的数据 用于读取指针指向的数组
      

  6.   

    如果都是Delphi程序,为什么不用TStringList,用类型强制转换成Integer传过去,那边再转换回来不就行了
      

  7.   

    都在D中就简单了:
    Dll中:
    library Project1;uses
      SysUtils,
      Classes,
      windows;{$R *.res}
    procedure getp(var sl:TStringList);stdcall;
    begin
        if sl<>nil then
        begin
            sl.Add('test1');
            sl.Add('test2');
            sl.Add('test3');
        end;
    end;exports
      getp;begin
    end.使用dll部分:
    procedure getp(var sl:TStringlist);stdcall;external 'project1.dll';implementation{$R *.dfm}procedure TForm1.BitBtn1Click(Sender: TObject);
    var
        s:TStringList;
        i:integer;
    begin
        s:=TStringList.Create;
        getp(s);
        for i:=0 to s.Count-1 do
            ListBox1.Items.Add(s[i]);
        s.Free;
    end;end.