著者: ゾウ・フェイ
電子メール: [email protected]
ホームページ: http://www.atechsoft.com/people/zouf/ Delphi を使っていると、どうしても変な問題が多く出てきますし、Delphi のドキュメントも意外と少ないので、自分でゆっくりしかまとめられないので、あります(詳細が散在しているだけです。書くのにあまり力を入れていないので、少し乱雑かもしれませんが、理解できるはずです;-))。次の場合: 1. 次の問題に対するより良い解決策がある場合は、csdn で私と私の友人に教えてください。 2. 他に質問がある場合は、問題とその回答をリストして、csdn で私と私の友人に教えてください。コミュニケーションがすべてを生み出す!
本文: Q: Delphi DLL で作成したフォームを Exe で ShowModal にすると、タスクバーに 2 つのアイコンが表示されます。なぜですか?この問題を解決するにはどうすればよいでしょうか? A: 次は DLL にフォームを配置する一般的な方法です: DLL: function ShowFrm: TModalResult; stdcall;beginForm1 := TForm1.ShowModal;関数 ShowFrm: TModalResult ; 'TestDLL.dll';…begin…ShowFrm;…end. この方法で作成された DLL 内の Form には、Delphi の DLL に対して別のアプリケーションが作成されるためです。アプリケーションにはタスクバーアイコンが表示されます。解決策: 次のように、メイン EXE のアプリケーションをメイン アプリケーションの DLL に渡します: DLL: function ShowFrm(app: TApplication): TModalResult; stdcall;varoldApp: TApplication;beginoldApp := Application;Application := app;Form1 : = TForm1.Create(Nil);tryForm1.ShowModal;finallyForm1.Free;end;Application := oldApp;end;Main EXE: function ShowFrm(app: TApplication): stdcall; external 'TestDLL.dll';…begin…ShowFrm(Application);…end 注: DLL 内のアプリケーションと EXE 内のアプリケーションがあります。まだいくつかの違いがあります。Forms.pas のコードを見てください。constructor TApplication.Create(AOwner: TComponent);begin…if not IsLibrary then CreateHandle;…end;DLL 内の Application にはハンドルがないことがわかり、メッセージ ループ処理は実行されません。これも正しいです。 Q: Delphi の DLL で問題が頻繁に発生します。 A: この問題が発生する理由は、Delphi 独自のメモリ管理メカニズムにあります。例: DLL にオブジェクトを作成します: x := TClass.Create(Application); このとき、Delphi はアドレス空間の有効期限が切れている可能性があるため (オペレーティング システムによって異なります)、x の解放操作は自動的に行われます。例外を引き起こします。別の例: EXE でオブジェクトを作成し、DLL 内のローカル変数として DLL を渡します。DLL が破棄されると、Delphi はスコープ外のすべての変数を自動的に解放します。そのため、これを EXE で再度オブジェクトとして使用すると、例外がスローされます。一般に、この問題は、「スマート」Delphi コンパイラのメモリ管理メカニズムと Windows の DLL 追加/アンロード メカニズムが原因で発生し、DLL と EXE でメモリ アクセスの競合が発生します。解決策: (次の原則に従う限り、ほとんどの問題は回避できます) 1. DLL と EXE の間では、Delphi の自動メモリ管理メカニズムを使用しないようにしてください。たとえば、上記の x := TClass.Create(Application); を次のように変更します。 TClass.Create(nil); このようにして、アプリケーションはそれを解放しなくなります。もちろん、プログラマは自分でそれを解放する必要があります。 2. DLL と EXE の間で、同じオブジェクトを指す異なるポインターが存在しないようにしてください。たとえば、DLL では x は TClass オブジェクトを指し、EXE では y は TClass オブジェクトを指します。このように、どちらかの側でメモリが解放されると、もう一方のメモリが無効になります。 3.その他... Q: 定期的なタスクを実行するスレッドは、一時停止してから実行を継続する必要がありますが、この時点でスレッドを停止する必要がある場合 (たとえば、プロセスが終了した場合) はどうすればよいですか? A: 解決策 1: スレッド内で Sleep による定期的なループを実行します。 (スリープによってスレッドが一時停止された場合、スレッドは再開やその他のメソッドによって再開できません。) KillThread によってスレッドを終了します。これは最も単純な方法ですが、あまりにも大雑把で問題が発生する可能性があります (KillThread は Windows では推奨されない API) 解決策 2: スレッド内で一時停止し、スレッドの外でタイマーを渡し、時々再開します。コードは次のとおりです。 // ThreadPRocedure Execute;beginwhile not Terminated dobegin... // 処理コード Suspend;end;end;// 外部 // タイマー プロシージャ OnTimer(Sender: Tobject);beginthd.Resume;end;//スレッドを終了するには Place...thd.Resume;thd.Terminate;thd.WaitFor; //通常、スレッドを終了した後、WaitFor を使用してスレッドが本当に終了したかを確認する必要があります。 ...問題: スレッドと外部との結合が強すぎるため、スレッドの動作サイクルさえも外部タイマーを通じて決定する必要があります。解決策 3 (これが私が考えた最善の方法です): セマフォを介してスレッドを一時停止します。 // ThreadTMyThread = class(TThread)private Event: TEvent;protected プロシージャ Execute; public コンストラクタ Create(loginInfo: TLoginInfo); destructor Destroy; end; { TMyThread }constructor TMyThread(loginInfo:) TLoginInfo);begin イベント := TEvent.Create(nil, True, True, 'EventName');end;デストラクター TMyThread.Destroy;begin Event.Free; 継承;end;プロシージャ TMyThread.Execute;begin 継承; 終了していない間 do begin // ... Event.ResetEvent; end;end;procedure TMyThread.SetEvent;begin Event.SetEvent;end;スレッドを中断する必要があるプログラムの場合は、次のコードだけで十分です。 …thd.Terminate;thd.SetEvent;thd.WaitFor;…end;