作者:鄒飛
E-mail:[email protected]
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;beginForm1 := TForm1.Create(Nil);tryForm1.ShowModal;finallyForm1.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;varoldApp: TApplication;beginoldApp := Application;Application := appForm;1 := TForm1.Create(Nil);tryForm1.ShowModal;finallyForm1.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。程式碼如下:// ThreadPRocedure Execute;beginwhile not Terminated dobegin… // 處理程式碼Suspend;end;end;///// 定時器procedure OnTimer(Sender: Tobject);beginthd.Resume;end// 要結束執行緒;地方…thd.Resume;thd.Terminate;thd.WaitFor; //一般在結束執行緒後得透過WaitFor確認執行緒已經真的結束了。 …問題:線程和外部的耦合太強了,甚至線程的操作週期得透過外面的定時器來確定。解決方法三(這是我想到的最好方法):在線程中透過信號量進行暫停操作。 // ThreadTMyThread = class(TThread)private Event: TEvent;protected procedure Execute; override;public constructor Create(loginInfo: TLoginInfo); overload; destructor Destroy; override; procedure SetEvent:endf{ TThThread; 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;