DLL调用问题(Access Voilation....),涉及string类型及Interface. ( 积分: 300 )

  • 主题发起人 主题发起人 gear1023
  • 开始时间 开始时间
G

gear1023

Unregistered / Unconfirmed
GUEST, unregistred user!
情况:
1.定义一接口
2.DLL中实现此接口,然后EXPORT出此接口
3.另写一程序调用此DLL,得到相应接口.
结果:
调用结果可以正常得到, 但在执行完毕后出现"Access Violation at ...."

测试程序如下
http://www.xznk.cn/DLL.String.Call.rar
 
情况:
1.定义一接口
2.DLL中实现此接口,然后EXPORT出此接口
3.另写一程序调用此DLL,得到相应接口.
结果:
调用结果可以正常得到, 但在执行完毕后出现"Access Violation at ...."

测试程序如下
http://www.xznk.cn/DLL.String.Call.rar
 
自然不是楼上所说的问题了,工程中已经引用了ShareMem单元.
而且问题是出在FreeLibrary(h);
 
不太清楚原因,执行到释放时报错。
使用静态调用吧,呵呵
 
不好意思,没有仔细看代码。

我对代码修正了一下,现在可以了,修正的全部代码如下:

unit callMainU;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, InterfaceU;

type
TPc = function: IFun
stdcall;

type
TForm1 = class(TForm)
edt1: TEdit;
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function CallProc(p: TPc): String;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btn1Click(Sender: TObject);
var
h: THandle;
// hello: function (sName: string): string;
PubFun : function: IFun
stdcall;
begin
h := LoadLibrary('stringDll.dll');
if h <> 0 then
try
PubFun := GetProcAddress(h, 'PubFun');
Caption := CallProc(PubFun);

//if PubFun <> nil then
// Caption := PubFun.SayHelloTo(PChar(edt1.Text));

// ShowMessage(StrPas(PubFun.SayHelloTo(PChar(edt1.Text))));
finally
FreeLibrary(h);
end;
end;

function TForm1.CallProc(p: TPc): String;
begin
Result := p.SayHelloTo(edt1.Text);
end;

end.
 
是释放的问题,不过这种比较简单的参数,你可以使用PCHAR类型的参数,在释放DLL文件之前把传出的数据保存到另一个变量里就可以了。
 
说明一下造成此现象的原因:

对函数 PubFun 的调用,会返回一个基于引用计数的接口变量,
程序中没有定义一个局部变量来保存此接口变量,
Delphi自动生成了一个局部变量来保存它,这个变量直到当前过程退出才会被释放
因为你自己没有定义它,所以,你也无法去访问它,
但是它至少引起了接口引用计数的变化

那么,当这个接口被释放之前,你不能直接释放DLL,
因为释放这个接口的代码是在DLL内部的,而过程退出前,
你又无法释放这个没有定义的局部变量,
也就是说,在过程退出之前,你不能释放这个DLL,
否则,就会执行已被释放的代码(代码所在的内存无效)

而用一个嵌套的函数调用,就可以巧妙的解决这个问题了
 
对的,关于这种内存的问题已经很多人提到过了,你必须要注意的就是把牵涉到输入输出的string类型换成PChar或者是shortstring的类型,最好不要用String类型,因为那个必须跟borlandmm.dll搭配使用,还必须在uses的最前面加上sharemem,很麻烦,而且同样很容易出问题,所以不推荐使用。
 
事实上,即使你显式定义了一个接口的局部变量,
那个你没有定义的接口变量(实际上是函数的返回值)
依然是存在的,所以,也无法显式地释放它

我们来验证一下:

var
h: THandle;
// hello: function (sName: string): string;
PubFun : function: IFun
stdcall;
m: IFun;
begin
h := LoadLibrary('stringDll.dll');
if h <> 0 then
try
PubFun := GetProcAddress(h, 'PubFun');
//Caption := CallProc(PubFun);

if PubFun <> nil then
begin
m := PubFun;
ShowMessage(IntToStr(m._AddRef));
Caption := m.SayHelloTo(PChar(edt1.Text));
end;

// ShowMessage(StrPas(PubFun.SayHelloTo(PChar(edt1.Text))));
finally
FreeLibrary(h);
end;

上面的 ShowMessage(IntToStr(m._AddRef));
显示的是 4, 说明此接口变量被引用了4次,
减去我们手工添加的这一次,那么还有3次,
这三个分别是:
1.DLL内部对接口的引用
2.那个没有定义的局部变量的引用(函数返回值)
3.变量m 对接口的引用
 
有个ShareMemRep 的版本,可以避免引用 borlandmm.dll
http://delphimaster.ru/cgi-bin/news.pl?showone=1078389493
http://cc.borland.com/Author.aspx?ID=84414
是周爱民先生的改进版本

但是,楼主的问题,显然不是因为这个造成的

具体原因,我在上面已经说的很明白了
 
类型用PChar或者 ShortString !!
 
多谢各位参与.

送各位 .NET 2005 下载(BT)

http://www.xznk.cn/Microsoft.Visual.Studio.2005.Professional.Edition.DVD-ZWTiSO.torrent
 
function TFun.SayHelloTo(sName: string): string;
begin
Result := 'Hello ' + sName;
end;

function TFun.ServerTime: TDateTime;
begin
Result := Now - 6 / 24 ;
end;

initialization
fun := TFun.Create;
Coinitialize(nil)
//主要是这里
finalization
fun := nil;
CoUninitialize;//主要是这里
end.
 
第一步
你定义一个全局的基类,这个基类中的所有函数为“抽象类”
第二步:
你在DLL继承这个基类,然后公布
第三步(调用)
定义一个基类,然后得到DLL中类的句柄,就可以调用了
 
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部