13

MessageBox()メッセージ ループ内のように一見同期の Windows 関数を呼び出すと、Sleep()代わりに (または同様の関数) を呼び出したかのようにループ自体がフリーズしないのはなぜですか? 私の要点を説明するために、次のスケルトンを取り上げますWndProc

int counter = 0;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CREATE:
             SetTimer(hwnd, 1, 1000, NULL); //start a 1 second timer
             break;
        case WM_PAINT:
             // paint/display counter variable onto window
             break;
        case WM_TIMER: //occurs every second
             counter++;
             InvalidateRect(hwnd, NULL, TRUE); //force window to repaint itself
             break; 
        case WM_LBUTTONDOWN: //someone clicks the window
             MessageBox(hwnd, "", "", 0);
             MessageBeep(MB_OK); //play a sound after MessageBox returns
             break;
        //default ....
    }
    return 0;
}

上記の例では、プログラムの主な機能はタイマーを実行し、カウンターの値を毎秒表示することです。ただし、ユーザーがウィンドウをクリックすると、プログラムはメッセージ ボックスを表示し、ボックスが閉じられた後にビープ音を鳴らします。

ここが興味深いところです。メッセージ ボックスが閉じられるまで実行されないMessageBox()ため、 が同期関数であることがわかります。MessageBeep()ただし、タイマーは実行され続け、メッセージ ボックスが表示されている間でもウィンドウは 1 秒ごとに再描画されます。そのため、MessageBox()明らかにブロッキング関数呼び出しですが、他のメッセージ ( WM_TIMER/ WM_PAINT) は引き続き処理できます。MessageBox を次のような別のブロッキング呼び出しに置き換える場合を除いて、それは問題ありませんSleep()

    case WM_LBUTTONDOWN:
         Sleep(10000); //wait 10 seconds
         MessageBeep(MB_OK);
         break;

これにより、アプリケーションが完全にブロックされ、メッセージ処理が 10 秒間行われません ( WM_TIMER/WM_PAINTが処理されない、カウンターが更新されない、プログラムが「フリーズ」するなど)。MessageBox()では、メッセージ処理を続行できるのに、そうでないのはなぜSleep()でしょうか? 私のアプリケーションがシングル スレッドであることを考えると、MessageBox()この機能を可能にするのは何ですか? システムはアプリケーション スレッドを「複製」して、WM_LBUTTONDOWN一度コードを終了できるMessageBox()ようにしながら、その間に元のスレッドが他のメッセージを処理できるようにしますか? (それは私の根拠のない推測でした)

前もって感謝します

4

2 に答える 2

11

および同様のMessageBox()Windows API 関数は、IO 操作やミューテックスのように実行をブロックしていません。このMessageBox()関数は通常、[OK] ボタンのあるダイアログ ボックスを作成します。そのため、メッセージ ボックスに関連するウィンドウ メッセージの自動処理が期待できます。これは独自のメッセージ ループで実装されます。新しいスレッドは作成されませんが、選択されたメッセージ (ペイントなど) がWndProc()関数を再帰的に呼び出して処理され、他のメッセージは送信されないため、アプリケーションは応答性を維持します。ウィンドウを作成しました。

Sleep()および他の関数 (WndProc()ウィンドウ メッセージの処理から直接呼び出された場合) は、シングル スレッド メッセージ ループの実行を実際にブロックします。他のメッセージは処理されません。

于 2009-08-10T20:21:02.813 に答える
3

MessageBox は、独自の Win32 メッセージ ループを実行します (呼び出し元のアプリをフリーズしないようにするため)。

再入不可の関数で使用する場合は注意してください...

編集:詳しく説明します:Windowsのメッセージループはそのようなものです(msdnから盗まれました):

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
} 

DispatchMessage は、必要なウィンドウ プロシージャを呼び出します。そのウィンドウ プロシージャは、(同じスレッドで) 独自のループを開始することができ、DispatchMessage 自体を呼び出します。これにより、任意のメッセージ ハンドラーが呼び出されます。

見たい場合は、アプリをデバッガーで起動し、メッセージ ボックスをポップアップしてブレークします。そのループ内のどこかにドロップされます。コールスタックを見て、親ループが見つかるかどうかを確認してください。

于 2009-08-10T20:18:38.323 に答える