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.
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.
{ Private declarations }
//connDbfunc: TconnDbfunc;
myHandle: THandle;
var
Form1: TForm1;
connDbfunc: function() :Icdf;stdcall;
改为TconnDbfunc = function(): Icdf;stdcall;
答案见楼上
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;
像你这种方式,错误的根源在于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在这方面设置不一样,你的程序害我看了半天汇编,也帮我解决解决吧
2.myIniFile.FreeInstance;改为myIniFile.Free;
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创建的对象.
一、把调用接口(这里是那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;
我在回帖中主要提到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,到达我的目的
以上讨论仅基于自己管理接口,而不是使用类厂创建。
(这CSDN到底怎么了?连回贴都不让回了?)
僵哥,我只能说你太牛叉了,面向对象又好好学习了一课。
问题已经解决...thanks!!仰望中...