回答一位朋友的提问:在Delphi7下如何与DLL共享数据库连接

来源:互联网 发布:淘宝top20万词表 编辑:程序博客网 时间:2024/05/07 04:53

朋友,你好!
   
    因为有疑问,所以要思考;因为找不到答案,所以要询问。文明,就在疑惑与不懈的探索中演进。

    我的一些文章赢得部分人的关注,我因此而荣幸。于是,QQ上朋友顿然多了起来。很多问题在QQ上无法简单的答复,还有一些问题是提问者自己也不知道如何表达,更谈不上解决问题了。

    时间和精力对于我,太匮乏。我无法一次性回答所有的问题,这里权且回答一个“如何与dll共享数据库连接”的问题吧。非常抱歉,你的QQ号被我的太太删除了,她以为你是一位女士:)。

(1)正确认识dll
    
    Dll本质上一个独立的应用程序,与exe一样,有自己的地址空间。 因此,dll是一个可装载执行的程序。其他的,不多说了吧。  

(2)调用DLL的方法

    我用过的有三种。

    第一种:用API

    因为Delphi支持几乎所有的API,你可以在Delphi中直接调用API装载一个外部DLL。但是我不喜欢这样做,因为我讨厌微软的那套路。

    第二种:外部函数引用

    做一个单元(unit),引用外部函数。例如:

function myDllCall(var i:integer): Integer; stdcall; external 'mydll.dll' name 'mydllpro';
依赖于DLL接口输出方式,或者写成:
function myDllCall(var i:integer): Integer; stdcall; external 'mydll.dll' index my_index;

注意:stdcall,cdecl,safecall遵循从右至左的参数调用规则;register,pascal遵循从左至右的参数调用规则。但是cdecl释放参数的时机与其他几种方式还不一样。建议大家采用stdcall调用,因为无论是Delphi还是C,都可用。

    第三种:推荐方式---地址引用

    如果在Delphi中调用由Delphi生成的Dll,这种方式最好。
    你可以把Dll名称与其输出调用接口作为参数传入,写一个通用的过程或者函数,调用所有的dll.

    这是我的一个例子:

  procedure  tcommon_base.run_dlls_parameter(filename,modulename:string;
          var parameter:tmy_object);
          //dll中要使用对象parameter;并且可能对parameter作修改,必须返回修改。
    var
      handle:thandle;
      p     :procedure(var parameter:tobject);
      a,b   :array[0..255]of char;
    begin
      filename:='./runtime/'+filename; //dll的路径和名字:自己定义。
      strpcopy(a,filename);          //转换为Pchar.
      handle:=loadlibrary(a);          //装入dll.并返回句柄。
      if handle<>0 then          //装入成功
        begin
          strpcopy(a,modulename);      //转换调用接口为pchar.
          @p:=getprocaddress(handle,a);//得到调用接口的进程地址。
          if @p<>nil then          
          p(parameter)          //调用Dll.含参数。
          else
          begin
          strpcopy(b,'Method Not Defined: '+modulename+'!');
          messagebox(message_parameter.mainhandle,b,
          'Error...',mb_ok or mb_iconexclamation);
          end;
        end
      else
        begin
          strpcopy(b,'Component Not Found: '+filename+'!');
          messagebox(message_parameter.mainhandle,
          b,'Error...',mb_ok or mb_iconexclamation);
        end;
    end;

  说明:如果你有一个dll是这么写的,
library cancel_service;
  uses
  sharemem,
  SysUtils,
  Classes,
  forms,
  process_cancel_service,//你的dll应用模块,一个窗体。
  my_global;

(******************************)
  procedure  run_cancel_service(var param:tmy_object);
    begin
      my_global.global_parameter:=param;  
     //把传入的参数对象地址赋给dll的同一个参数类.
     //使得主程序的参数对象和dll中的参数对象指向同一个实例空间.
     //这样,在process_cancel_service窗体中就可以
     //通过my_global.global_parameter来访问主程序的组件了.
     //因为他们指向同一个地址.

      process_cancel_service_form:=tprocess_cancel_service_form.create(nil);
      process_cancel_service_form.showmodal;
      process_cancel_service_form.free;
    end;
(******************************)
  exports
    run_cancel_service;
  begin
  end.

那么,调用方式:
    run_dlls_parameter('cancel_service.dll',
          'run_cancel_service',myglobal.global_parameter);

    注意:global_parameter对象定义在my_global.pas中,它定义了一个公用的对象Tglobal_parameter,用来传递参数.这个unit在你的主程序和dll中都要用到.从逻辑上讲,这是两个完全独立的unit,只不过他们的结构和内容完全一样.因此只要在主程序中初始化这个实例,dll中就可以通过对象地址直接引用了.切莫在dll中再次初始化这个实例,那样就浪费空间了,会形成内存泄露.

(3)是不是看不懂呢? 继续听我讲.

   调用dll很简单,问题是如何与dll传递参数?比如,我要与dll共享数据库组件,我要在主程序中打开一个表,然后在dll中操作这个表,怎么办?

   其实很简单,我们可以分别在主程序和dll中定义一个相同的对象或者结构(记录类型):global_parameter,这个对象或者结构中包含了我们需要共享或者传递的参数,包括数据库连接组件等等.
  
   但是,我们知道,exe和dll完全是两个进程,他们之间如何传递这些参数呢? 很简单,我们可以在主程序中把这个参数对象生成好了,然后把这个对象的地址作为参数传给dll, 使得他们访问同一个空间,不就可以了么?

   事实上,前面的例子就是这么做的.他们之间传递了一个tmy_object类型的对象,因为对象的传递过程,本身就是对象地址的传递,而不是空间的克隆.所以,他们之间就共享了同一个对象,可以直接访问对象中的其他组件......并且在dll中对参数对象的任何修改,对于主程序都是可见的.

   中午没有休息,有点晕乎乎的感觉,可能没有表达清楚,请原谅!




朋友,你好!
   
    因为有疑问,所以要思考;因为找不到答案,所以要询问。文明,就在疑惑与不懈的探索中演进。

    我的一些文章赢得部分人的关注,我因此而荣幸。于是,QQ上朋友顿然多了起来。很多问题在QQ上无法简单的答复,还有一些问题是提问者自己也不知道如何表达,更谈不上解决问题了。

    时间和精力对于我,太匮乏。我无法一次性回答所有的问题,这里权且回答一个“如何与dll共享数据库连接”的问题吧。非常抱歉,你的QQ号被我的太太删除了,她以为你是一位女士:)。

(1)正确认识dll
    
    Dll本质上一个独立的应用程序,与exe一样,有自己的地址空间。 因此,dll是一个可装载执行的程序。其他的,不多说了吧。  

(2)调用DLL的方法

    我用过的有三种。

    第一种:用API

    因为Delphi支持几乎所有的API,你可以在Delphi中直接调用API装载一个外部DLL。但是我不喜欢这样做,因为我讨厌微软的那套路。

    第二种:外部函数引用

    做一个单元(unit),引用外部函数。例如:

function myDllCall(var i:integer): Integer; stdcall; external 'mydll.dll' name 'mydllpro';
依赖于DLL接口输出方式,或者写成:
function myDllCall(var i:integer): Integer; stdcall; external 'mydll.dll' index my_index;

注意:stdcall,cdecl,safecall遵循从右至左的参数调用规则;register,pascal遵循从左至右的参数调用规则。但是cdecl释放参数的时机与其他几种方式还不一样。建议大家采用stdcall调用,因为无论是Delphi还是C,都可用。

    第三种:推荐方式---地址引用

    如果在Delphi中调用由Delphi生成的Dll,这种方式最好。
    你可以把Dll名称与其输出调用接口作为参数传入,写一个通用的过程或者函数,调用所有的dll.

    这是我的一个例子:

  procedure  tcommon_base.run_dlls_parameter(filename,modulename:string;
          var parameter:tmy_object);
          //dll中要使用对象parameter;并且可能对parameter作修改,必须返回修改。
    var
      handle:thandle;
      p     :procedure(var parameter:tobject);
      a,b   :array[0..255]of char;
    begin
      filename:='./runtime/'+filename; //dll的路径和名字:自己定义。
      strpcopy(a,filename);          //转换为Pchar.
      handle:=loadlibrary(a);          //装入dll.并返回句柄。
      if handle<>0 then          //装入成功
        begin
          strpcopy(a,modulename);      //转换调用接口为pchar.
          @p:=getprocaddress(handle,a);//得到调用接口的进程地址。
          if @p<>nil then          
          p(parameter)          //调用Dll.含参数。
          else
          begin
          strpcopy(b,'Method Not Defined: '+modulename+'!');
          messagebox(message_parameter.mainhandle,b,
          'Error...',mb_ok or mb_iconexclamation);
          end;
        end
      else
        begin
          strpcopy(b,'Component Not Found: '+filename+'!');
          messagebox(message_parameter.mainhandle,
          b,'Error...',mb_ok or mb_iconexclamation);
        end;
    end;

  说明:如果你有一个dll是这么写的,
library cancel_service;
  uses
  sharemem,
  SysUtils,
  Classes,
  forms,
  process_cancel_service,//你的dll应用模块,一个窗体。
  my_global;

(******************************)
  procedure  run_cancel_service(var param:tmy_object);
    begin
      my_global.global_parameter:=param;  
     //把传入的参数对象地址赋给dll的同一个参数类.
     //使得主程序的参数对象和dll中的参数对象指向同一个实例空间.
     //这样,在process_cancel_service窗体中就可以
     //通过my_global.global_parameter来访问主程序的组件了.
     //因为他们指向同一个地址.

      process_cancel_service_form:=tprocess_cancel_service_form.create(nil);
      process_cancel_service_form.showmodal;
      process_cancel_service_form.free;
    end;
(******************************)
  exports
    run_cancel_service;
  begin
  end.

那么,调用方式:
    run_dlls_parameter('cancel_service.dll',
          'run_cancel_service',myglobal.global_parameter);

    注意:global_parameter对象定义在my_global.pas中,它定义了一个公用的对象Tglobal_parameter,用来传递参数.这个unit在你的主程序和dll中都要用到.从逻辑上讲,这是两个完全独立的unit,只不过他们的结构和内容完全一样.因此只要在主程序中初始化这个实例,dll中就可以通过对象地址直接引用了.切莫在dll中再次初始化这个实例,那样就浪费空间了,会形成内存泄露.

(3)是不是看不懂呢? 继续听我讲.

   调用dll很简单,问题是如何与dll传递参数?比如,我要与dll共享数据库组件,我要在主程序中打开一个表,然后在dll中操作这个表,怎么办?

   其实很简单,我们可以分别在主程序和dll中定义一个相同的对象或者结构(记录类型):global_parameter,这个对象或者结构中包含了我们需要共享或者传递的参数,包括数据库连接组件等等.
  
   但是,我们知道,exe和dll完全是两个进程,他们之间如何传递这些参数呢? 很简单,我们可以在主程序中把这个参数对象生成好了,然后把这个对象的地址作为参数传给dll, 使得他们访问同一个空间,不就可以了么?

   事实上,前面的例子就是这么做的.他们之间传递了一个tmy_object类型的对象,因为对象的传递过程,本身就是对象地址的传递,而不是空间的克隆.所以,他们之间就共享了同一个对象,可以直接访问对象中的其他组件......并且在dll中对参数对象的任何修改,对于主程序都是可见的.

   中午没有休息,有点晕乎乎的感觉,可能没有表达清楚,请原谅!




朋友,你好!
   
    因为有疑问,所以要思考;因为找不到答案,所以要询问。文明,就在疑惑与不懈的探索中演进。

    我的一些文章赢得部分人的关注,我因此而荣幸。于是,QQ上朋友顿然多了起来。很多问题在QQ上无法简单的答复,还有一些问题是提问者自己也不知道如何表达,更谈不上解决问题了。

    时间和精力对于我,太匮乏。我无法一次性回答所有的问题,这里权且回答一个“如何与dll共享数据库连接”的问题吧。非常抱歉,你的QQ号被我的太太删除了,她以为你是一位女士:)。

(1)正确认识dll
    
    Dll本质上一个独立的应用程序,与exe一样,有自己的地址空间。 因此,dll是一个可装载执行的程序。其他的,不多说了吧。  

(2)调用DLL的方法

    我用过的有三种。

    第一种:用API

    因为Delphi支持几乎所有的API,你可以在Delphi中直接调用API装载一个外部DLL。但是我不喜欢这样做,因为我讨厌微软的那套路。

    第二种:外部函数引用

    做一个单元(unit),引用外部函数。例如:

function myDllCall(var i:integer): Integer; stdcall; external 'mydll.dll' name 'mydllpro';
依赖于DLL接口输出方式,或者写成:
function myDllCall(var i:integer): Integer; stdcall; external 'mydll.dll' index my_index;

注意:stdcall,cdecl,safecall遵循从右至左的参数调用规则;register,pascal遵循从左至右的参数调用规则。但是cdecl释放参数的时机与其他几种方式还不一样。建议大家采用stdcall调用,因为无论是Delphi还是C,都可用。

    第三种:推荐方式---地址引用

    如果在Delphi中调用由Delphi生成的Dll,这种方式最好。
    你可以把Dll名称与其输出调用接口作为参数传入,写一个通用的过程或者函数,调用所有的dll.

    这是我的一个例子:

  procedure  tcommon_base.run_dlls_parameter(filename,modulename:string;
          var parameter:tmy_object);
          //dll中要使用对象parameter;并且可能对parameter作修改,必须返回修改。
    var
      handle:thandle;
      p     :procedure(var parameter:tobject);
      a,b   :array[0..255]of char;
    begin
      filename:='./runtime/'+filename; //dll的路径和名字:自己定义。
      strpcopy(a,filename);          //转换为Pchar.
      handle:=loadlibrary(a);          //装入dll.并返回句柄。
      if handle<>0 then          //装入成功
        begin
          strpcopy(a,modulename);      //转换调用接口为pchar.
          @p:=getprocaddress(handle,a);//得到调用接口的进程地址。
          if @p<>nil then          
          p(parameter)          //调用Dll.含参数。
          else
          begin
          strpcopy(b,'Method Not Defined: '+modulename+'!');
          messagebox(message_parameter.mainhandle,b,
          'Error...',mb_ok or mb_iconexclamation);
          end;
        end
      else
        begin
          strpcopy(b,'Component Not Found: '+filename+'!');
          messagebox(message_parameter.mainhandle,
          b,'Error...',mb_ok or mb_iconexclamation);
        end;
    end;

  说明:如果你有一个dll是这么写的,
library cancel_service;
  uses
  sharemem,
  SysUtils,
  Classes,
  forms,
  process_cancel_service,//你的dll应用模块,一个窗体。
  my_global;

(******************************)
  procedure  run_cancel_service(var param:tmy_object);
    begin
      my_global.global_parameter:=param;  
     //把传入的参数对象地址赋给dll的同一个参数类.
     //使得主程序的参数对象和dll中的参数对象指向同一个实例空间.
     //这样,在process_cancel_service窗体中就可以
     //通过my_global.global_parameter来访问主程序的组件了.
     //因为他们指向同一个地址.

      process_cancel_service_form:=tprocess_cancel_service_form.create(nil);
      process_cancel_service_form.showmodal;
      process_cancel_service_form.free;
    end;
(******************************)
  exports
    run_cancel_service;
  begin
  end.

那么,调用方式:
    run_dlls_parameter('cancel_service.dll',
          'run_cancel_service',myglobal.global_parameter);

    注意:global_parameter对象定义在my_global.pas中,它定义了一个公用的对象Tglobal_parameter,用来传递参数.这个unit在你的主程序和dll中都要用到.从逻辑上讲,这是两个完全独立的unit,只不过他们的结构和内容完全一样.因此只要在主程序中初始化这个实例,dll中就可以通过对象地址直接引用了.切莫在dll中再次初始化这个实例,那样就浪费空间了,会形成内存泄露.

(3)是不是看不懂呢? 继续听我讲.

   调用dll很简单,问题是如何与dll传递参数?比如,我要与dll共享数据库组件,我要在主程序中打开一个表,然后在dll中操作这个表,怎么办?

   其实很简单,我们可以分别在主程序和dll中定义一个相同的对象或者结构(记录类型):global_parameter,这个对象或者结构中包含了我们需要共享或者传递的参数,包括数据库连接组件等等.
  
   但是,我们知道,exe和dll完全是两个进程,他们之间如何传递这些参数呢? 很简单,我们可以在主程序中把这个参数对象生成好了,然后把这个对象的地址作为参数传给dll, 使得他们访问同一个空间,不就可以了么?

   事实上,前面的例子就是这么做的.他们之间传递了一个tmy_object类型的对象,因为对象的传递过程,本身就是对象地址的传递,而不是空间的克隆.所以,他们之间就共享了同一个对象,可以直接访问对象中的其他组件......并且在dll中对参数对象的任何修改,对于主程序都是可见的.

   中午没有休息,有点晕乎乎的感觉,可能没有表达清楚,请原谅!