2016-10-26 12 views
8

私は、プログラム内の他のスレッドからのメッセージ(通知)を処理するために、Application.OnMessageイベントハンドラを使用します。ポップアップメニューがアクティブ(開かれている)場合、このイベントハンドラは呼び出されないことが分かりました。テストコードは、(それがスレッドなしであるが、原理は同じである)は、以下である:ポップアップメニューが表示されているときにApplication.OnMessageが呼び出されないのはなぜですか?

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    Application.OnMessage := ApplicationEvents1Message; 
end; 

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; 
    var Handled: Boolean); 
begin 
    if Msg.message = WM_USER then 
    Beep(); 
end; 

procedure TForm1.tmr1Timer(Sender: TObject); 
begin 
    PostThreadMessage(GetCurrentThreadId, WM_USER, 0, 0); 
end; 

答えて

8

OnMessageは、メインスレッドのメッセージループから呼び出されます。このメッセージループは、DelphiのVCLライブラリコードで実装されています。したがって、このライブラリコードでは、OnMessageのイベントハンドラを呼び出す機会があります。

ポップアップメニューは、Win32関数TrackPopupMenuExを呼び出して表示されます。この関数は、モーダルメッセージループを実装して、メニューのトラッキングUIを実行します。このメッセージループはWin32コードで実装されているため、VCLコードにOnMessageイベントを発生させる機会はありません。 Win32コードはVCLを何も知らず、プレーンなメッセージループを実行します。メッセージは処理され、ディスパッチされますが、VCL固有のコードは実行できません。

これは、PostThreadMessageを避けるべき理由の完全な例です。すべてのメッセージループを制御する場合にのみ使用できます。その他の障害点には、システムメッセージダイアログ、ドラッグアンドドロップモーダルループ、ウィンドウ移動/サイズモーダルループなどがあります。

PostThreadMesaageの使用を中止する必要があります。代わりにAllocateHWndを使用してメインスレッドにウィンドウハンドルを作成します。ワーカースレッドからそのウィンドウにメッセージを投稿します。

+0

詳細な対応をありがとうございます。セカンダリスレッド(独自のメッセージキューを持つ)では、PostThreadMessageを介して送信されたメッセージを受信することは安全です。また、セカンダリスレッドからプログラムのメインスレッドにメッセージが送信された場合に限り、処理する方が良い別ウィンドウのウィンドウプロシージャ内のメッセージ? – Dmitro25

+0

セカンダリスレッドにUIがない場合は、そうでなければならず、コード以外のメッセージループが実行されることはありません。スレッドメッセージは安全です。一貫性を保つために、メッセージを受信するためにウィンドウを使用する方がきれいです。しかし、VCLの設計が長く壊れているため、AllocateHWndをメインスレッドから呼び出すことはできません。だからあなたはあなた自身のバージョンでそれを置き換えなければならないだろう。 –

+0

[スレッドメッセージはモーダルループで食べられる](https://blogs.msdn.microsoft.com/oldnewthing/20050426-18/?p=35783) –

関連する問題