13 Stimmen

Wenn MessageBox()/verwandt sind synchron, warum nicht meine Nachricht Schleife einfrieren?

Warum ist es so, dass, wenn ich eine scheinbar synchrone Windows-Funktion wie MessageBox() innerhalb meiner Nachrichtenschleife, friert die Schleife selbst nicht ein, als ob ich die Sleep() (oder eine ähnliche Funktion) zu verwenden? Um meinen Standpunkt zu veranschaulichen, nehmen Sie das folgende Grundgerüst 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;
}

Im obigen Beispiel besteht die Hauptfunktion des Programms darin, einen Timer laufen zu lassen und den Wert des Zählers jede Sekunde anzuzeigen. Wenn der Benutzer jedoch auf unser Fenster klickt, zeigt das Programm ein Meldungsfenster an und piept dann, nachdem das Fenster geschlossen wurde.

Jetzt wird es interessant: Wir können sagen MessageBox() ist eine synchrone Funktion, weil MessageBeep() wird erst ausgeführt, wenn das Nachrichtenfeld geschlossen wird. Der Timer läuft jedoch weiter, und das Fenster wird jede Sekunde neu gezeichnet, auch wenn das Meldungsfenster angezeigt wird. Während also MessageBox() ist offenbar ein blockierender Funktionsaufruf, andere Meldungen ( WM_TIMER / WM_PAINT ) können noch bearbeitet werden. Das ist in Ordnung, außer wenn ich MessageBox durch einen anderen blockierenden Aufruf wie Sleep()

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

Dadurch wird meine Anwendung vollständig blockiert, und während der 10 Sekunden findet keine Nachrichtenverarbeitung statt ( WM_TIMER / WM_PAINT nicht verarbeitet werden, der Zähler nicht aktualisiert wird, das Programm "einfriert", usw.). Wie kommt es also, dass MessageBox() ermöglicht die Fortsetzung der Nachrichtenverarbeitung, während Sleep() nicht? Angesichts der Tatsache, dass meine Anwendung single-threaded ist, was ist es, das MessageBox() um diese Funktionalität zu ermöglichen? Repliziert das System meinen Anwendungs-Thread, so dass es den WM_LBUTTONDOWN Code einmalig MessageBox() erledigt ist, während der ursprüngliche Thread in der Zwischenzeit noch andere Nachrichten bearbeiten kann? (das war meine unausgegorene Vermutung)

Vielen Dank im Voraus

12voto

CsTamas Punkte 3923

En MessageBox() und ähnliche Windows-API-Funktionen blockieren die Ausführung nicht, wie es eine IO-Operation oder Mutexing tun würde. Die MessageBox() Funktion erzeugt ein Dialogfeld, das in der Regel eine OK-Schaltfläche enthält, so dass man erwarten kann, dass die Fenstermeldungen im Zusammenhang mit dem Meldungsfeld automatisch behandelt werden. Dies wird mit einer eigenen Meldungsschleife implementiert: Es wird kein neuer Thread erstellt, aber Ihre Anwendung bleibt reaktionsfähig, da ausgewählte Meldungen (z.B. zum Malen) durch rekursiven Aufruf Ihrer WndProc() Funktion, während andere Nachrichten aufgrund des modalen Typs des erstellten Fensters nicht übertragen werden.

Sleep() und andere Funktionen (bei direktem Aufruf aus Ihrer WndProc() eine Fenstermeldung behandeln) würde die Ausführung Ihrer Single-Thread-Nachrichtenschleife blockieren - keine andere Nachricht würde verarbeitet werden.

3voto

Eugene Punkte 6941

MessageBox führt seine eigene Win32-Nachrichtenschleife aus (um die aufrufende Anwendung nicht einzufrieren).

Hüten Sie sich vor der Verwendung in nicht ablauffähigen Funktionen...

EDIT: zur Präzisierung: Die Nachrichtenschleife unter Windows ist so ähnlich (gestohlen von msdn):

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

DispatchMessage ruft alle erforderlichen Fensterprozeduren auf. Diese Fensterprozedur kann ihre eigene Schleife (auf demselben Thread) starten und ruft DispatchMessage selbst auf, das wiederum alle Message-Handler aufruft.

Wenn Sie es sehen wollen, starten Sie Ihre Anwendung im Debugger, öffnen Sie die Nachrichtenbox und brechen Sie ab. Sie werden irgendwo in der Schleife der Anwendung landen. Sehen Sie sich den Aufrufstapel an und prüfen Sie, ob Sie die übergeordnete Schleife finden können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X