Delphi点滴
2006-02-04 13:43:32 来源:WEB开发网核心提示:作者:邹飞E-mail:zouf.tech@ATechSoft.comHomepage:http://www.atechsoft.com/people/zouf/ 用Delphi的过程中难免会遇到很多奇怪的问题,而Delphi的文档也出奇的少,Delphi点滴,因此只能自己慢慢的总结,所以有了下文(由于只是零散的细节
作者:邹飞
E-mail:zouf.tech@ATechSoft.com
Homepage:http://www.atechsoft.com/people/zouf/ 用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) 解决方法二: 在线程中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;
E-mail:zouf.tech@ATechSoft.com
Homepage:http://www.atechsoft.com/people/zouf/ 用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) 解决方法二: 在线程中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;
- ››Delphi实现把10进制转换成16进制的函数进制转化
- ››Delphi中将字符串按给定字符分隔(似split函数功能...
- ››Delphi 动态创建窗体,锁定窗口赋值
- ››Delphi 与 VC 共享接口和对象
- ››Delphi图像处理 -- 表面模糊
- ››Delphi之多线程实例
- ››Delphi SelectSingleNode的使用 根据节点属性获取...
- ››Delphi接口详述
- ››delphi 远程调试
- ››Delphi与DirectX之DelphiX(34): TDIB.Lightness()...
- ››Delphi Application.MessageBox详解
- ››Delphi只能运行一个程序实例的两种方法
更多精彩
赞助商链接