DELPHI编译的DLL喜欢内存泄漏

用Delphi的过程中难免会遇到很多奇怪的问题,而Delphi的文档也出奇的少,因此只能自己慢慢的总结,所以有了下文(由于只是零散的细节,所以文笔上没有花什么功夫,可能会比较乱,但应该能够理解;-))。
如果:
1. 你有下面问题的更好解决方法,请告诉我,和csdn上的朋友
2. 你有其他的问题,请列出问题,以及你的解答,告诉我,和csdn上的朋友。
 
沟通创造一切!

正文:
 
Q: 在Delphi的DLL中制作的Form,如果在Exe中ShowModal时,会在任务栏上出现两个Icon,为什么?如何解决这个问题?
A: 下面是一种典型的DLL中放Form的方法:
DLL:
function ShowFrm: TModalResult; stdcall;
begin
Form1 := TForm1.Create(Nil);
try
Form1.ShowModal;
finally
Form1.Free;
end;
end;
主EXE:
function ShowFrm: TModalResult; stdcall; external 'TestDLL.dll';
 

 
begin

ShowFrm;

end.
 
以这种方式做出的DLL中的Form,会和主应用程序显示另一个Icon,其原因在于:
Delphi中对于DLL会另外再创建一个Application,而每个Application都会显示一个任务栏的Icon。
解决方法:
在主应用程序中将主EXE的Application传入DLL,如下:
DLL:
function ShowFrm(app: TApplication): TModalResult; stdcall;
var
oldApp: TApplication;
begin
oldApp := Application;
Application := app;
Form1 := TForm1.Create(Nil);
try
Form1.ShowModal;
finally
Form1.Free;
end;
Application := oldApp;
end;
主EXE:
function ShowFrm(app: TApplication): TModalResult; stdcall; external 'TestDLL.dll';
 

 
begin

ShowFrm(Application);

end.
注:DLL中的Application和EXE中的Application还是有些区别的,看Forms.pas中的代码:
constructor TApplication.Create(AOwner: TComponent);
begin

if not IsLibrary then CreateHandle;

end;
可以知道,DLL中的Application没有Handle,因此不会进行消息循环处理,这也是很正确的。
 
Q: Delphi中的DLL,经常出现问题!
A: 至所以出现问题,是因为Delphi本身的内存管理机制。比如:
在DLL中创建一个对象:
x := TClass.Create(Application);
这时,Delphi会在Application Free时自动Free x,但由于x是在DLL的地址空间中,当Application结束时,DLL的地址空间可能已经失效(不同的操作系统会有不一样),因此这时对x的释放操作就会引发异常。
又比如:
在EXE中创建了一个对象,并且传入了DLL作为DLL中的局部变量,这样在DLL销毁时,由于Delphi会将所有超出作用域的变量自动释放,因此如果再在EXE中使用这个对象,就会引发异常。
 
总的说,问题是由于“聪明”的Delphi编译器的内存管理机制,和Windows的DLL加/卸载机制,导致了DLL和EXE中的内存存取冲突。
 
解决方法:(只要遵循以下几个原则就可以避免大多数的问题)
1.在DLL和EXE之间,尽量不要使用Delphi的自动内存管理机制,由程序员自己对对象的生命期负责,比如:
对于上面的x := TClass.Create(Application);把它改成:
x := TClass.Create(nil);
这样,Application就不会再Free它了。当然,程序员必须自己来释放它。
2.尽量避免在DLL和EXE之间存在不同的指针指向的同一个对象。比如,在DLL中有x指向TClass对象,在EXE中又有y指向TClass对象,这样在任何一边的内存释放都会导致另一边的内存无效。
3.其他…
                                                                                                             
Q: 一个做周期性任务的线程,在其中需要暂停片刻,然后继续运行,但如果这时需要让线程停止(比如进程已经结束了),那该怎么办?
A:
解决方法一:
在线程中通过Sleep进行周期循环。(如果在线程中通过Sleep暂停了,通过Resume等方法是无法使得线程重新复活的)
通过KillThread来结束线程。
这是最简单的方法,但也太粗暴,可能会导致问题(KillThread是Windows不推荐使用的API)


2007-11-17 17:17:14   
 2007-11-17 17:19:12   

解决方法二:
在线程中Suspend,在线程外面通过一个定时器,每隔一段时间就Resume。代码如下:
// Thread
procedure Execute;
begin
while not Terminated do
begin
…   // 处理代码
Suspend;
end;
end;
 
// 外面
// 定时器
procedure OnTimer(Sender: Tobject);
begin
thd.Resume;
end;
// 要结束线程的地方

thd.Resume;
thd.Terminate;
thd.WaitFor;                 // 一般在结束线程后得通过WaitFor确认线程已经真的结束了。

 
问题:线程和外部的耦合太强了,甚至线程的操作周期得通过外面的定时器来确定。
 
解决方法三(这是我想到的最好方法):
在线程中通过信号量进行暂停操作。
// Thread
TMyThread = class(TThread)
private
  Event: TEvent;
protected
  procedure Execute; override;
public
    constructor Create(loginInfo: TLoginInfo); overload;
    destructor Destroy; override;
    procedure SetEvent;
end;
 
{ TMyThread }
 
constructor TMyThread.Create(loginInfo: TLoginInfo);
begin
  Event := TEvent.Create(nil, True, True, 'EventName');
end;
 
destructor TMyThread.Destroy;
begin
  Event.Free;
  inherited;
end;
 
procedure TMyThread.Execute;
begin
  inherited;
  while not Terminated do
  begin
    // ...
    Event.ResetEvent;
    Event.WaitFor(10000);
  end;
end;
 
procedure TMyThread.SetEvent;
begin
  Event.SetEvent;
end;
 
对于需要中断线程的程序,只需如下代码即可:
begin
  …
 thd.Terminate;
 thd.SetEvent;
thd.WaitFor;

end;

 
 2007-11-17 17:22:01   

Delphi 可以快速开发桌面程序,用来做dll 封装操作,封装窗体都是很方便的。
在 delphi 做动态库时,会自动提示要 uses ShareMem,这个实际用起来是不方便的,因为 dll 可能要发布,要给其他人用,而别人用什么语言来开发是说不准的,如果不是delphi,就没办法用了。因此在接口上一般是用 pchar来代替string。但是在内部,string 还是可以拿来用的。

这样就会产生一个问题,如果动态库是支持多线程来用的,而在动态库内部并没有显式的创建一个线程时,烦繁使用 string 就说不定什么时候会产生一个内存出错。

在Delphi 内部,定义了一个 isMultiThread 的boolean 型全局变量(好象是在 system 单元)。默认这个变量是 false的。当显示地创建一个线程时(如TThread.Create),会置成true。否则一直是false。
这个变量唯一使用的地方是在 GetMem 和 FreeMem时,如果为 True,会先进入临界区,操作完成后退出。如果为false就没有临界区了。

因此在编写多线程安全的动态库时,一定要记得在动态库初始化的时候手动加上 isMultiThread=true; 这样强制delphi来使用临界区操作。

如果调用程序也是用 delphi 来写的,可能会多线程使用,那最好也在初始化的时候加上这句。值得注意的是,调用程序和动态库的这两个变量是两个互不相干的,也就是两边都得加,只在一方加上是影响不了另一方的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值