小弟想用DLL来查询数据库数据,把查到的数据集 返回到调用DLL的EXE程序中(DLL中函数类似:function AAA:TDataSet)。我搞了好久都不成功。那位大侠 有例子 可以贴一下,或发到我邮箱([email protected])。DLL中的函数 和 EXE中调用的函数都要啊。我的代码是这样的:
DLL中:
library Project1;uses
  SysUtils,
  Classes,
  Unit1 in 'Unit1.pas' {Form1};{$R *.res}exports
  getDataSet;begin
end.
================分割线===============
unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DB, ADODB, Grids, DBGrids;function getDataSet:TDataSet;stdcall;export;type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  Form1: TForm1;implementation{$R *.dfm}
function getDataSet:TDataSet;stdcall;export;
begin
  form1.ADOQuery1.Active:=true;  //ADOQuery1的SQL属性已填写
  Result:=form1.ADOQuery1.DataSource.DataSet
end;end.
================分割线===============
EXE中:
unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Grids, DBGrids, DB, ADODB, StdCtrls;type
  TForm1 = class(TForm)
    Button1: TButton;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  Form1: TForm1;  function getDataSet:TDataSet;stdcall;external 'project1.dll' name 'getDataSet';implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
var DS:TDataSet;
begin
  DS:=TdataSet.Create(self);
  DS:=getdataSet;
end;end.

解决方案 »

  1.   

    DLL中返回对象,最好是返回对象的指针,即地址,这样才能调用得到值
      

  2.   

    要加ShareMem的,不然问题多呀
    dlllibrary Dll;uses
      ShareMem,//用在第一个单元
      SysUtils,
      Classes,
      DllForm in 'DllForm.pas' {frmDLL};exports
      GetDataSet, FreeData;
    begin
    end.unit DllForm;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, DB, ADODB;type
      TfrmDLL = class(TForm)
        ADOConnection1: TADOConnection;
        ADOQuery1: TADOQuery;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;function GetDataSet: TDataSet; export;
    procedure FreeData;implementation{$R *.dfm}var
      frmDLL: TfrmDLL = nil;function GetDataSet: TDataSet;
    begin
      if frmDLL = nil then
        frmDLL := TfrmDLL.Create(nil);
      Result := frmDLL.ADOQuery1;
    end;procedure FreeData;
    begin
      FreeAndNil(frmDll);
    end;procedure TfrmDLL.FormCreate(Sender: TObject);
    const
      CNN_MDB_FMT = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s; Jet OLEDB:Database Password=%s';
    begin
      ADOConnection1.Close;
      ADOConnection1.ConnectionString := Format(CNN_MDB_FMT,
        [ExtractFilePath(ParamStr(0)) + 'test.mdb', '']);
      ADOConnection1.Open;
      ADOQuery1.Open;
    end;procedure TfrmDLL.FormDestroy(Sender: TObject);
    begin
      ADOConnection1.Close;
    end;end.
    exeprogram Host;uses
      ShareMem,
      Forms,
      HostForm in 'HostForm.pas' {frmHost};{$R *.res}begin
      Application.Initialize;
      Application.CreateForm(TfrmHost, frmHost);
      Application.Run;
    end.
    unit HostForm;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, Grids, DBGrids, DB;type
      TfrmHost = class(TForm)
        DataSource1: TDataSource;
        DBGrid1: TDBGrid;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;function GetDataSet: TDataSet; external 'Dll.dll';var
      frmHost: TfrmHost;implementation{$R *.dfm}procedure TfrmHost.Button1Click(Sender: TObject);
    var
      DataSet: TDataSet;
    begin
      DataSet := GetDataSet;
      DataSource1.DataSet := DataSet;
    end;end.
      

  3.   

    感谢 blazingfire 的帮助,这种方式,我已成功。有没有那位大侠,有用 指针 传 结果集 的 例子或经验,能教下我?
      

  4.   


    blazingfire 的不就是用对象指针传的吗?
      

  5.   

    如果一定要用指针的话,那可能就麻烦得多了。思路就是把DataSet里的数据写到连续结构化的一块内存里,
    然后再把指向这块内存的首地址指针传给exe。如果是我,我会选择用DataSet的方法!
      

  6.   

    to blazingfire 再问个问题 'ShareMem'是干嘛用的?还必须放第一个单元?
      

  7.   

    这个说来就话长了。大体上说一下吧,有些细节我没有时间去研究:
    在Delphi程序设计时,在堆中开辟内存空间时候,Delphi会用一个容器来管理堆里面的内存(其实就是一个链表加上相关的内存操作函数),而这个容器指针
    是一个全局变量来存储的。所以你在写dll与exe程序时,所分配的内存并不是由一个内存管理器来(因为在exe、dll分别都有一个
    自己全局指针);这样就导致在exe分配的内存,不能在dll里释放,反之亦然。怎么办呢? 所以Delphi就提供了一个方案
    就是ShareMem。他的思路就是这样,让你的dll、exe所分配的内存都交全另外一个dll来管理,这样dll与exe的分配内存就可以
    共用了。这个dll名字叫borlndmm.dll。至于为什么ShareMem必须放第一个单元,那是因为Delphi的单元文件的initialization
    的执行顺序,与dpr中引用这个单元的顺序有关;dpr中某单元引用越靠前,则某单元的initialization就越先执行!而我们程序的
    内存管理器的"替换"过程就是在initialization块里实现的
    看看Delphi的ShareMem里的一点代码就知道了:procedure InitMemoryManager;
    var
      SharedMemoryManager: TMemoryManager;
      MM: Integer;
    begin
      // force a static reference to borlndmm.dll, so we don't have to LoadLibrary
      SharedMemoryManager.GetMem := SysGetMem;  MM := GetModuleHandle(DelphiMM);
    {$IFDEF GLOBALALLOC}
      SharedMemoryManager.GetMem := xSysGetMem;
      SharedMemoryManager.FreeMem := xSysFreeMem;
      SharedMemoryManager.ReallocMem := xSysReallocMem;
    {$ELSE}
      SharedMemoryManager.GetMem := GetProcAddress(MM,'@Borlndmm@SysGetMem$qqri');
      SharedMemoryManager.FreeMem := GetProcAddress(MM,'@Borlndmm@SysFreeMem$qqrpv');
      SharedMemoryManager.ReallocMem := GetProcAddress(MM, '@Borlndmm@SysReallocMem$qqrpvi');
    {$ENDIF}
      SetMemoryManager(SharedMemoryManager);
    end;initialization
      if not IsMemoryManagerSet then
        InitMemoryManager;//!!!!如果我们不放到第一个单元,就很可能会出现这样的情况,有些单元分配的内存就是通过ShareMem(borlndmm.dll),有些又不是,这样会出现的问题就不说了。注:这里说的分配内存,并不等于程序中直接GetMem,New操作,象一些String、动态数组的操作也会涉及到内存操作,只是它们是Delphi封装起来了而已。说到
    你这个DataSet问题,它也涉及到内存操作,不过要说清楚这一点,可就更复杂了!
      

  8.   

    有个问题想问一下,楼主这样做不会是单单显示一下数据吧?如果不只是这样为何不从DLL中返回OLEVariant型?
    其实我也是想研究一下的,以下是我的代码,请高手们指正:
    1.DLL部分:library userdll;{ 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
      ShareMem,
      SysUtils,
      Classes,
      Udm in 'Udm.pas' {dm: TDataModule};{$R *.res}
    exports
    GetUserList;
    begin
    end.
    2:DLL引用的数据模块单元:unit Udm;interfaceuses
      SysUtils, Classes, DB, ADODB, Provider;type
      Tdm = class(TDataModule)
        ADOConnection1: TADOConnection;
        ADOQuery1: TADOQuery;
        DataSetProvider1: TDataSetProvider;
        procedure DataModuleCreate(Sender: TObject);
        procedure DataModuleDestroy(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
     function GetUserList(strName:String): OLEVariant;export;
    var
      dm: Tdm=nil;implementation{$R *.dfm}
     function GetUserList(strName:String): OLEVariant;
     begin
      if dm = nil then
      dm := Tdm.Create(nil);
      with dm.ADOQuery1 do begin
       Close;
       SQL.Text:=strname;
       Open;
      end;
      Result := dm.DataSetProvider1.Data;
     end;procedure Tdm.DataModuleCreate(Sender: TObject);
    var
    str:string;
    begin
     str:='Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=G_CBBTOP;Data Source=127.0.0.1';
     with ADOConnection1 do begin
        Connected:=false;
        ConnectionString:=str;
        Connected:=true;
     end;
    end;procedure Tdm.DataModuleDestroy(Sender: TObject);
    begin
      ADOConnection1.Connected:=false;
    end;end.
    3:调用DLL:unit Ulookdll;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, DB, DBClient, StdCtrls, Grids, DBGrids;type
      TFlookdll = class(TForm)
        Button1: TButton;
        ClientDataSet1: TClientDataSet;
        DataSource1: TDataSource;
        DBGrid1: TDBGrid;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Flookdll: TFlookdll;implementation{$R *.dfm}
    function GetUserList(strName:String): OLEVariant; external 'userdll.dll';procedure TFlookdll.Button1Click(Sender: TObject);
    begin
     ClientDataSet1.Data:=GetUserList('select x_a,x_b from x_eng');
    end;end.
      

  9.   


    '把DataSet里的数据写到连续结构化的一块内存里, 然后再把指向这块内存的首地址指针传给exe'这个能给个例子吗?或写些关键代码段
      

  10.   

    说简单点是,就是定义一个结构体,分配空间,在Dll里把数据填进去此空间里,再把这个空间的地址(指针)返回就可以了