DLL工程代码:library ConnFunc;
uses
  SysUtils,
  Classes,
  UconnDbfunc in 'UconnDbfunc.pas',
  IconnDbfunc in 'IconnDbfunc.pas'; //IconnDbfunc单元文件的接口名字是Icdf{$R *.res}
function connDbFuncObj:Icdf;stdcall;
begin
  result := TconnDbfunc.create;
end;exports     //导出connDbfunc函数
  connDbFuncObj;begin
end.
定义接口Icdf,单元名:IconnDbfunc:unit IconnDbfunc;interface
type
  Icdf = interface
  ['{623008B1-5E8C-463C-9048-821C14FB20C1}']  //读取ini参数配置文件
 function ReadIniFile(const FileName, Section, ident: string;Default:string): string;  //写入参数配置信息到ini中
 procedure WriteIniFile(const FileName, Section,ident: string;Default:string);
end;
implementation
end.
实现Icdf接口的类TconnDbfunc,单元文件名:UconnDbfunc 
unit UconnDbfunc;interface
uses
  SysUtils,
  forms,
  inifiles, //使用ini文件操作类
  IconnDbfunc; //类的单元文件引用接口单元,并在类中进行实现
type
  TconnDbfunc=class(TinterfacedObject,ICdf)
  public
  //section(区段)、ident(关键字)、default(默认值)}
  function ReadIniFile(const FileName, Section, ident: string;Default:string): string;  procedure WriteIniFile(const FileName, Section,ident: string;value:string);end;
implementationfunction TconnDbfunc.ReadIniFile(const FileName, Section, ident: string;Default:string): string;
var
  Myinifile:Tinifile;
begin
  myIniFile := TiniFile.Create(FileName);
  result:=myIniFile.ReadString(Section, ident, default);
  myIniFile.FreeInstance;
end;procedure TconnDbfunc.WriteIniFile(const FileName, Section, Ident: string; Value: string);
var
      MyiniFile:Tinifile;
begin
      myIniFile := TiniFile.Create(FileName);
      myIniFile.WriteString(Section, Ident, Value);
      myIniFile.FreeInstance;
end;end.
我在程序中动态载入dll时候,老是提示AV错误,动态载入的主要代码:
unit ConnectToDB;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, DB, ADODB,inifiles,IconnDbfunc;type
  TconnDbfunc=function:Icdf;stdcall;  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    BtnOK: TBitBtn;
    BtnTest: TButton;
    ADOConnection1: TADOConnection;
    procedure BtnOKClick(Sender: TObject);
    procedure BtnTestClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    
    connDbfunc: TconnDbfunc;
    myHandle: THandle;
  public
    { Public declarations }
  end;var
  Form1: TForm1;
implementation{$R *.dfm}procedure TForm1.BtnOKClick(Sender: TObject);
begin
        showmessage('保存配置信息');
end;procedure TForm1.BtnTestClick(Sender: TObject);
begin
        showmessage('测试连接到数据库');
end;procedure TForm1.FormCreate(Sender: TObject);
begin
   myHandle:=loadlibrary('ConnFunc.dll'); //载入dll函数,获得句柄
   if myHandle>0 then
      try
          @connDbfunc:= GetProcAddress(myHandle, 'connDbFuncObj');
          if @connDbfunc<>nil then
              begin
               edit1.Text:=connDbfunc.ReadIniFile(extractFilePath(application.ExeName)+ 'conntoDB.ini','connDB','data source','');
               edit2.Text:=connDbfunc.ReadIniFile(extractFilePath(application.ExeName)+ 'conntoDB.ini','connDB','Initial Catalog','');
              end
          else
               application.MessageBox('在Dll动态链接库中加载方法失败!','提示',mb_ok);
      finally
          freeLibrary(myHandle);
      end
   else
      application.MessageBox('can''t find the connFunc.dll !','system information',mb_ok);
end;end.

解决方案 »

  1.   

    private
        { Private declarations }
        
        //connDbfunc: TconnDbfunc;
        myHandle: THandle;
    var
      Form1: TForm1;
      connDbfunc: function() :Icdf;stdcall;
      

  2.   

    将TconnDbfunc=function:Icdf;stdcall;
    改为TconnDbfunc = function(): Icdf;stdcall;
      

  3.   

    DLL单元引用ShareMem ,调用DLL的工程也引用ShareMem在一种方法就是使用 FastMM单元.你在网上查一下.在就是不要使用String,使用使用PChar
      

  4.   

    晕,我还以为是Adult Video呢!!!
    答案见楼上
      

  5.   

    需要在客户端调用DLL时多增加一次对Icdf 的引用,否则会出现AV错误
      

  6.   


    procedure TForm1.FormCreate(Sender: TObject);
    begin
       myHandle:=loadlibrary('ConnFunc.dll'); //载入dll函数,获得句柄
       if myHandle>0 then
          try
              @connDbfunc:= GetProcAddress(myHandle, 'connDbFuncObj');
              if @connDbfunc<>nil then
                  begin
                   connDbfunc._AddRef;
                   edit1.Text:=connDbfunc.ReadIniFile(extractFilePath(application.ExeName)+ 'conntoDB.ini','connDB','data source','');
                   edit2.Text:=connDbfunc.ReadIniFile(extractFilePath(application.ExeName)+ 'conntoDB.ini','connDB','Initial Catalog','');
                  end
              else
                   application.MessageBox('在Dll动态链接库中加载方法失败!','提示',mb_ok);
          finally
              freeLibrary(myHandle);
          end
       else
          application.MessageBox('can''t find the connFunc.dll !','system information',mb_ok);
    end;
      

  7.   

    楼主原来是返回接口,光去注意AV了,嘿嘿。当然用ShareMem 是肯定的
    像你这种方式,错误的根源在于d的编译器太智能化了,原理我就不鬼扯了,若有兴趣,请参考(《inside vcl架构分析》一书)。先说你的程序问题吧
    connDbfunc.ReadIniFile
    你调用了2次这个函数会产生2个对象,请定义一个接口赋值后调用。关于AV(Not Adult Video)解决方法,
    1,请在使用接口指针前,和释放指针后立即把接口指针赋为nil
    2,这个问题更麻烦,接口指针的定义位置,会造成编译器代码的不同,这个不同也可能会影响你的FRefCount, 但你只需要注意请注意最后FRefCount计数器的值的变化,(调试期间可以看到),保证最后释放的时候的值。
    3,以下是vcl代码里面的一段
    procedure TInterfacedObject.BeforeDestruction;
    begin
      if RefCount <> 0 then
        Error(reInvalidPtr);
    end;
    function TInterfacedObject._Release: Integer;
    begin
      Result := InterlockedDecrement(FRefCount);
      if Result = 0 then
        Destroy;
    end;
    上述代码可以看到销毁对象时候,只能使用接口指针 _Release,或:=nil 来销毁,而不能使用 对象.free 了解决的办法可以重载_Release,来自由控制。  总之,2点,1.看好你的引用计数器,保证对象能销毁,
                 2.接口指针前,和释放指针后立即把接口指针赋为nil(注意赋为nil,同样引起FRefCount的变化)
     多久没用delphi了,下了你的代码调了个吧小时才发现,我想问一下楼主,delphi2007如何跟进vcl源代码,我把source path,search path ,uses debug dcus 都设了,还是不能在vcl代码中调试,难道delphi2007和delphi6在这方面设置不一样,你的程序害我看了半天汇编,也帮我解决解决吧
      

  8.   

    1.增加ShareMem;
    2.myIniFile.FreeInstance;改为myIniFile.Free;
      

  9.   

    function connDbFuncObj:Icdf;stdcall;
    begin
      result := TconnDbfunc.create;
    end;首先是TConnDbfunc.Create,这个会调用TInterfacedObject.NewInstance
    class function TInterfacedObject.NewInstance: TObject;
    begin
      Result := inherited NewInstance;
      TInterfacedObject(Result).FRefCount := 1;
    end;
    此时FRefCount=1接着会调用TInterfacedObject.AfterConstruction;
    procedure TInterfacedObject.AfterConstruction;
    begin
    // Release the constructor's implicit refcount
      InterlockedDecrement(FRefCount);
    end;
    从而FRefCount又会归0然后connDbFuncObj当中会从TconnDbfunc转为Icdf
    从而会执行到
    {System}单元
    procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);在其中会调用IInterface._AddRef,该实现为
    function TInterfacedObject._AddRef: Integer;
    begin
      Result := InterlockedIncrement(FRefCount);
    end;
    从而FRefCount又会回到1所以调用DLL的函数之后会得到一个Icdf接口,接着该接口不会被释放,直到调用完ReadIniFile.之后由于没有引用Icdf接口,接口会被释放掉,从而执行到.
    {system}单元的
    function _IntfClear(var Dest: IInterface): Pointer;
    其中调用IInterface._Release,即实例过程
    function TInterfacedObject._Release: Integer;
    begin
      Result := InterlockedDecrement(FRefCount);
      if Result = 0 then
        Destroy;
    end;
    由于InterlockedDecrement是先FRefCount-1,再返回,该函数的Result = 0,继而调用Destroy销毁由TconnDbfunc.Create创建的对象.
      

  10.   

    问题的关键在于调用过程没有做显示的接口管理,系统会在过程执行完之后,清理堆栈时释放,而程序当中在没有执行完之前就已经FreeLibrary,所以会导致异常。解决的办法有两种:
    一、把调用接口(这里是那DLL的那个返回接口的函数)的所有过程放到子过程/子函数当中,从而达到在子过程/函数执行完之后释放掉接口引用,再回到主调用过程当中FreeLibrary.
    procedure TForm1.TempleteProc;
    begin
      edit1.Text := connDbfunc.ReadIniFile(extractFilePath(application.ExeName) + 'conntoDB.ini', 'connDB', 'data source', '');
      edit2.Text := connDbfunc.ReadIniFile(extractFilePath(application.ExeName) + 'conntoDB.ini', 'connDB', 'Initial Catalog', '');
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
       myHandle:=loadlibrary('ConnFunc.dll'); //载入dll函数,获得句柄
       if myHandle>0 then
          try
              @connDbfunc:= GetProcAddress(myHandle, 'connDbFuncObj');
              if @connDbfunc<>nil then
                  begin
                    TempleteProc;
                  end
              else
                   application.MessageBox('在Dll动态链接库中加载方法失败!','提示',mb_ok);
          finally
              freeLibrary(myHandle);
          end
       else
          application.MessageBox('can''t find the connFunc.dll !','system information',mb_ok);
    end;
    二、使用显示的接口变量来接管接口,然后在FreeLibrary调用之前释放接口
    procedure TForm1.FormCreate(Sender: TObject);
    var
      DLLInterface: Icdf;
    begin
       myHandle:=loadlibrary('ConnFunc.dll'); //载入dll函数,获得句柄
       if myHandle>0 then
          try
              @connDbfunc:= GetProcAddress(myHandle, 'connDbFuncObj');
              if @connDbfunc<>nil then
                  begin
                   DLLInterface := connDbfunc;
                   edit1.Text:=DLLInterface.ReadIniFile(extractFilePath(application.ExeName)+ 'conntoDB.ini','connDB','data source','');
                   edit2.Text:=DLLInterface.ReadIniFile(extractFilePath(application.ExeName)+ 'conntoDB.ini','connDB','Initial Catalog','');
                  end
              else
                   application.MessageBox('在Dll动态链接库中加载方法失败!','提示',mb_ok);
          finally
              if DLLInterface <> Nil then DLLInterface := Nil;
              freeLibrary(myHandle);
          end
       else
          application.MessageBox('can''t find the connFunc.dll !','system information',mb_ok);
    end;
      

  11.   

    看来17楼对我的回复颇有不同意见,于是在18楼贴出了vcl源码来帮助理解,我在这里先表示感谢,但是我还是不明白17楼对我提出的哪一点有意见,还望指教。
    我在回帖中主要提到2点:
    1.看好你的引用计数器,保证对象能销毁, 
    2.接口指针前,和释放指针后立即把接口指针赋为nil(注意赋为nil,同样引起FRefCount的变化) 
    第二点内容在你后面的回帖已经的到印证,就不讨论了,
    关于第一点,我讨论的前提是基于显式接口来获取connDbfunc的返回值,也就是第二种17楼提出的第2种方式,(关于第一种方式创建了2次对象是否适合,不在讨论范围之内)
    我想就关于楼主接口获得函数定义来说,这也是我为什么要要求楼主管理好引用计数器的原因。我们使用com接口获得指针基本都是从参数来传回,如QueryInterface函数,而楼主使用的函数返回值来返回,function connDbFuncObj:Icdf;stdcall;
    现在如果申请了一个显式接口作为类变量或作为函数局部变量,调用connDbFuncObj获得接口后,RefCount的数值是不同的,这就是我为什么要求楼主管理好自己的引用计数器。
    关于我提出重载_Release,这是一个程序方式问题,
    以楼主程序为例:
    function connDbFuncObj:Icdf;stdcall;
    begin
      result := TconnDbfunc.create;
    end;这种方式是我不能容忍的,如果我只写这个dll接口函数,TconnDbfunc对象什么时候释放,释放了没有,我根本不能控制,以我的习惯,我会把TconnDbfunc创建的对象保存,最后使用对象.free来释放,而如果使用 对象.free 来释放就会遇到我上面提出的问题,所以我一般都会重载_Release,到达我的目的
    以上讨论仅基于自己管理接口,而不是使用类厂创建。
      

  12.   

    释放对象的前提是FRefCount = 0,而不是不能使用Free
    (这CSDN到底怎么了?连回贴都不让回了?)
      

  13.   


    僵哥,我只能说你太牛叉了,面向对象又好好学习了一课。
    问题已经解决...thanks!!仰望中...