私は一般的にイベント ループ (あまりうまくいかない) を理解しようとしてきましたが、Windows メッセージング ループはシングル スレッドであることを読みました。もしそうなら、Application.DoEvents はどのように機能しますか? イベント ループは一度に 1 つのメッセージを処理し、各メッセージ/イベントの処理中にブロックしませんか? Application.DoEvents を可能にするには、メッセージを処理しているスレッドとは別のスレッドにメッセージ イベント ループが存在する必要があるのではないでしょうか。別々のスレッドがある場合、「メイン」スレッドと呼んでいるのはどれですか? 非常に単純なものが欠けていると確信しています。それが何であるかわかりません。
1 に答える
私はこれを理解するために一日の大部分を費やしています(私が言うことが間違っている場合は、コメントして知らせてください。修正できるようになります)。私は実際に古いWin32アプリケーションを構築し、自分でメッセージループを作成する必要がありました(私はかなり永続的なSOBです)。したがって、次のようなメッセージループを起動するWinMainという関数があります。
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GetMessage()についてのことは、メッセージがメッセージキューで利用可能になるまでブロックすることです。Windowedアプリケーションを実行し、そこに座ってウィンドウを見ると(キューにメッセージを送信するアクションを発生させないでください)、メインスレッド(ウィンドウが作成されたスレッド)はGetMessage()で一時停止されます。 。ここで、メッセージが投稿されると、whileループに入ります(つまり、メッセージが終了しない場合は0です)。DispatchMessage()はここで興味深い関数です。この関数は、最終的に(.NETで)EventHandlerの制御と実行によって発生するイベントにつながります。呼び出しスタックがGetMessage()/ DispatchMessage()/.../ EventHandlerである場合、私が困惑したのは、Application.DoEvents()がメッセージを処理する方法を教えてください。まあ、それはかなり簡単です。Win32のDoEventsは次のようになります。
void DoEvents()
{
MSG msg;
HACCEL hAccelTable;
hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_TESTWIN32));
// Main message loop:
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0)
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
したがって、DoEvents()は、最初のメッセージループのDispatchMessage()内で、イベントを処理するために実際に別のループを開始します。主な違いは、キューにメッセージが入るまでブロックするGetMessage()を使用する代わりに、0を返し、キューにメッセージがなくなるとループが存在するPeekMessage()を使用することです。
では、ボタンを2回クリックし、そのボタンのEventHandlerにDoEvents()呼び出しがあるとしたらどうでしょうか。最初のイベントループは最初のクリックを処理し、イベントを発生させます。EventHandlerの実行中、DoEvents()呼び出しで、イベントが2回発生し、EventHandlerが再度入力されます(再帰呼び出しのようなものです)。それが怖いです!
したがって、最終的には、すべてが単一のスレッドで発生し、DoEvents()は、すべてのメッセージが処理されてから返されるまで実際にブロックします。今、私は数日間寝るつもりです。