Wie verhindere ich, dass Windows das Programm blockiert, während ein Fenster gezogen oder eine Menütaste gedrückt gehalten wird?

Lesezeit: 9 Minuten

Benutzeravatar von Leonardo
Leonardo

Ich bin Neuling bei Win32und ich habe ein Problem verfolgt (wenn es überhaupt als Problem bezeichnet werden kann), bei dem Windows den Fluss Ihres Programms während des Ereignisses blockiert, wenn ein Benutzer die Titelleiste des Fensters ergreift und sie auf dem Bildschirm bewegt.

Ich habe keinen legitimen Grund, dieses Problem zu lösen, außer dass es mich stört. Ein paar Möglichkeiten beinhalten das vollständige Entfernen des Rahmens, aber es scheint ein unbequemer Hack zu sein. Bei manchen Spielen (Einzelspieler) ist das überhaupt kein Problem. Ich habe jedoch gelesen, dass bei Multiplayer-Spielen Probleme auftreten können, wenn das Programm einfriert, da es einen kontinuierlichen Informationsfluss erwartet und nach einer solchen Verzögerung überfordert sein kann.

Ich habe versucht, dies zu meinem hinzuzufügen WindowProc

switch (uMsg)
{
    case WM_SYSCOMMAND:
        if (wParam == SC_CLOSE)
            PostQuitMessage(0);

        return 0;
    ...
    ...
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;

Und das scheint ein schneller Hack zu sein, außer dass ich die Maus wegziehen und loslassen kann, ohne das Programm zu schließen, wenn ich mit der Maus über das Schließen-Symbol gehe, und während dieser Zeit, wenn das Schließen-Symbol gedrückt gehalten wird, wird das Programm erneut blockiert.

Außerdem weiß ich nicht, wie ich den Code manuell einfügen kann, der zum Verschieben des Fensters erforderlich ist, wenn der Benutzer auf die Titelleiste klickt und die Maus zieht. Für den Anfang weiß ich nicht welche uMsg‘s und wParamzu handhaben ist.

Meine Frage ist dann, wie ich das Blockieren verbiete, wenn der Benutzer auf die Schaltfläche „Beenden“ klickt (oder Schaltflächen minimieren/maximieren), während der Fall weiterhin behandelt wird, wenn die Maus über die Schaltfläche geklickt und losgelassen wird, und wie erlaube ich das Benutzer das Fenster verschieben/ziehen, ohne dass es das Programm blockiert (oder welche Nachricht wird gesendet, wenn auf die Titelleiste geklickt wird, ohne dass es sich um eine Schaltfläche oder ein Menü handelt)?

Ich erstelle das Fenster mit WS_SYSMENU | WS_MINIMIZEBOX.

Ich möchte immer noch, dass das Programm auf Befehle zum Minimieren, Maximieren und Beenden reagiert.

Wenn Multi-Threading das Problem beheben kann, ist das interessant, aber ich frage mich, ob ich es auf Single-Core-Prozessoren zum Laufen bringen kann. Und ich habe über Hooks gelesen, aber die MSDN-Seite ist immer noch schwer für mich zu interpretieren.

  • durch program is blocked, ich denke, Sie meinen, Sie erhalten keine Windows-Nachrichten; was genau meinst du damit it expects continuous flow of information? erwarten Sie Windows-Meldungen, WM_TIMER Nachrichten, Infos von einem Port, …?

    – Eduard Clements

    4. August 2013 um 10:16 Uhr

  • Es ist nicht blockiert, Sie haben lediglich ein Problem damit Maus erfassen. Sie vermeiden dies, indem Sie die Art von Nachrichten abfangen, für die eine Mauserfassung erforderlich ist, um ordnungsgemäß zu funktionieren. Die Verwendung des Spy++-Dienstprogramms ist eine gute Möglichkeit, diese Nachrichten zu sehen und zu wissen, welche Sie selbst bearbeiten möchten.

    – Hans Passant

    4. August 2013 um 17:41 Uhr

Benutzeravatar von Cody Gray
Cody Gray

Warum friert meine App ein? – Eine Einführung in Nachrichtenschleifen und -threads

Dieses Phänomen ist nicht auf eine bestimmte Nachricht beschränkt. Dies ist eine grundlegende Eigenschaft der Windows-Nachrichtenschleife: Wenn eine Nachricht verarbeitet wird, kann gleichzeitig keine andere Nachricht verarbeitet werden. Es ist nicht genau so implementiert, aber Sie können es sich als eine Warteschlange vorstellen, in der Ihre App die Nachrichten aus der Warteschlange zieht, um sie in der umgekehrten Reihenfolge zu verarbeiten, in der sie eingefügt wurden.

Daher verbringen zu lange Verarbeitung irgendein Nachricht wird die Verarbeitung anderer Nachrichten aussetzen und Ihre Anwendung effektiv einfrieren (weil sie keine Eingaben verarbeiten kann). Der einzige Weg, dieses Problem zu lösen, ist der offensichtliche: Verbringen Sie nicht zu lange mit der Verarbeitung einer einzelnen Nachricht.

Oft bedeutet das, die Verarbeitung an einen Hintergrund-Thread zu delegieren. Sie müssen weiterhin alle Nachrichten im Hauptthread verarbeiten, und die Worker-Threads im Hintergrund müssen der Hauptmethode Bericht erstatten, wenn sie fertig sind. Die gesamte Interaktion mit der GUI muss in einem einzigen Thread stattfinden, und das ist fast immer der Hauptthread in Ihrer Anwendung (weshalb er oft als UI-Thread bezeichnet wird).

(Und um einen in Ihrer Frage erhobenen Einwand zu beantworten, ja, Sie können mehrere Threads auf Einzelprozessormaschinen betreiben. Sie werden nicht unbedingt Leistungsverbesserungen sehen, aber die Benutzeroberfläche wird dadurch reaktionsschneller. Die Logik hier ist, dass ein Thread dies kann nur eine Sache auf einmal tun, aber ein Prozessor kann extrem schnell zwischen Threads wechseln und so effektiv simulieren, dass er mehr als eine Sache gleichzeitig tut.)

Weitere nützliche Informationen finden Sie hier in diesem MSDN-Artikel: Hängen in Windows-Anwendungen verhindern

Sonderfälle: Modale Ereignisverarbeitungsschleifen

Bestimmte Fensteroperationen unter Windows sind modal Operationen. Modal ist ein gebräuchliches Wort in der Computertechnik, das sich im Grunde darauf bezieht, den Benutzer in einen bestimmten Modus zu sperren, in dem er nichts anderes tun kann, bis er den Modus ändert (dh aus diesem herauskommt). Immer wenn eine modale Operation begonnen wird, wird eine separate neue Nachrichtenverarbeitungsschleife gestartet und die Nachrichtenbehandlung findet statt dort (anstelle Ihrer Hauptnachrichtenschleife) für die Dauer des Modus. Gängige Beispiele für diese modalen Operationen sind Drag-and-Drop, Größenanpassung von Fenstern und Meldungsfelder.

Betrachtet man hier das Beispiel der Größenänderung von Fenstern, erhält Ihr Fenster a WM_NCLBUTTONDOWN Nachricht, an die Sie übergeben DefWindowProc für die Standardverarbeitung. DefWindowProc herausfindet, dass der Benutzer beabsichtigt, einen Vorgang zum Verschieben oder Ändern der Größe zu starten, und eine Schleife mit Nachrichten zum Verschieben/Größenanpassen eingegeben hat, die sich irgendwo tief in den Eingeweiden des Windows-eigenen Codes befindet. Daher läuft die Nachrichtenschleife Ihrer Anwendung nicht mehr, da Sie in einen neuen Bewegungs-/Größenanpassungsmodus eingetreten sind.

Windows führt diese Verschiebungs-/Größenanpassungsschleife aus, solange der Benutzer das Fenster interaktiv bewegt/skaliert. Es tut dies, damit es Mausnachrichten abfangen und entsprechend verarbeiten kann. Wenn der Verschiebe-/Größenanpassungsvorgang abgeschlossen ist (z. B. wenn der Benutzer die Maustaste loslässt oder die Esc Taste), kehrt die Steuerung zu Ihrem Anwendungscode zurück.

Es ist erwähnenswert, dass Sie sind benachrichtigt, dass diese Modusänderung über aufgetreten ist WM_ENTERSIZEMOVE Botschaft; die entsprechende WM_EXITSIZEMOVE Botschaft zeigt an, dass die modale Ereignisverarbeitungsschleife beendet wurde. Auf diese Weise können Sie einen Timer erstellen, der weiterhin generiert WM_TIMER Nachrichten, die Ihre Anwendung verarbeiten kann. Die tatsächlichen Details, wie dies implementiert wird, sind relativ unwichtig, aber die schnelle Erklärung lautet so DefWindowProc versendet weiter WM_TIMER Nachrichten an Ihre Anwendung innerhalb einer eigenen modalen Ereignisverarbeitungsschleife. Verwenden Sie die SetTimer Funktion um einen Timer als Reaktion auf die zu erstellen WM_ENTERSIZEMOVE Nachricht und die KillTimer Funktion um es als Reaktion auf die zu zerstören WM_EXITSIZEMOVE Botschaft.

Ich weise aber nur der Vollständigkeit halber darauf hin. Bei den meisten Windows-Apps, die ich geschrieben habe, musste ich das nie tun.

Also, was ist falsch an meinem Code?

Abgesehen davon ist das Verhalten, das Sie in der Frage beschreiben, ungewöhnlich. Wenn Sie eine neue, leere Win32-Anwendung mit der Visual Studio-Vorlage erstellen, bezweifle ich, dass Sie dieses Verhalten replizieren können. Ohne den Rest Ihrer Fensterprozedur zu sehen, kann ich nicht sagen, ob Sie irgendwelche Nachrichten blockieren (wie oben besprochen), aber der Teil I kann siehe in der Frage ist falsch. Du musst stets Anruf DefWindowProc für Nachrichten, die Sie nicht explizit selbst verarbeiten.

In diesem Fall könnten Sie glauben, dass Sie das tun, aber WM_SYSCOMMAND kann viele verschiedene Werte für seine haben wParam. Sie handhaben nur eines von diesen, SC_CLOSE. Alle anderen werden einfach wegen dir ignoriert return 0. Dazu gehören alle Funktionen zum Verschieben und Ändern der Größe von Fenstern (z SC_MOVE, SC_SIZE, SC_MINIMIZE, SC_RESTORE, SC_MAXIMIZEusw. usw.).

Und es gibt wirklich keinen guten Grund, damit umzugehen WM_SYSCOMMAND dich selbst; Lass einfach DefWindowProc kümmere dich darum. Die einzige Zeit, die Sie handhaben müssen WM_SYSCOMMAND Dies ist der Fall, wenn Sie dem Fenstermenü benutzerdefinierte Elemente hinzugefügt haben, und selbst dann sollten Sie jeden Befehl, den Sie nicht kennen, an weiterleiten DefWindowProc.

Eine grundlegende Fensterprozedur sollte wie folgt aussehen:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_CLOSE:
            DestroyWindow(hWnd);
            return 0;
        
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

es ist Auch möglich, dass Ihre Nachrichtenschleife falsch ist. Die idiomatische Win32-Nachrichtenschleife (befindet sich am unteren Rand Ihrer WinMain Funktion) sieht so aus:

BOOL ret;
MSG msg;
while ((ret = GetMessage(&msg, nullptr, 0, 0)) != 0)
{
    if (ret != -1)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        // An error occurred! Handle it and bail out.
        MessageBox(nullptr, L"Unexpected Error", nullptr, MB_OK | MB_ICONERROR);
        return 1;
    }
}

Sie benötigen keinerlei Haken. Die MSDN-Dokumentation dazu ist sehr gut, aber Sie haben recht: Sie sind kompliziert. Bleiben Sie weg, bis Sie ein besseres Verständnis des Win32-Programmiermodells haben. Es kommt in der Tat selten vor, dass Sie die Funktionalität eines Hooks benötigen.

  • Brillante Antwort, ich weiß genau, wohin ich von hier aus gehen muss. Vielen Dank.

    – Leonardo

    4. August 2013 um 21:18 Uhr

Benutzeravatar von gifnoc-gkp
gifnoc-gkp

Wenn Multi-Threading das Problem beheben kann, ist das interessant, aber ich frage mich, ob ich es auf Single-Core-Prozessoren zum Laufen bringen kann. Und ich habe über Hooks gelesen, aber die MSDN-Seite ist immer noch schwer für mich zu interpretieren.

Du kann Verwenden Sie mehrere Threads auf einem Single-Core-Prozessor. Die Leistung wäre auf Mehrkernsystemen besser, aber das sollte Sie nicht davon abhalten, Multithread-Anwendungen zu schreiben. Wie auch immer, mach es.

  • Das ist interessant, aber ich muss Multithreading lernen und bin mir nicht sicher, ob es mein Problem lösen würde.

    – Leonardo

    4. August 2013 um 10:13 Uhr

  • Danke für den Enthusiasmus, ich nehme an, ich habe einen weiteren Grund gefunden, Multi-Threading zu lernen.

    – Leonardo

    4. August 2013 um 10:21 Uhr

  • @Leonardo Erwägen Sie auch die Verwendung einer GUI-Bibliothek. FLTK wäre großartig, da es plattformübergreifend ist und auch OpenGL unterstützt.

    – gifnoc-gkp

    4. August 2013 um 10:22 Uhr

Durch Drucken aller gesendeten Nachrichten an WindowProc es erscheint WM_NCLBUTTONDOWN wird zuletzt gesendet, bevor die Sperrung erfolgt. Sie könnten die Mausposition überprüfen, nachdem dieses Ereignis aufgetreten ist, aber es scheint eine unbequeme Möglichkeit zu sein, ein einfaches Problem zu lösen.

  • Sie sollten die GUI und Ihre Verarbeitungseinheit in separaten Threads halten. Es ist eine Regel 🙂

    – gifnoc-gkp

    4. August 2013 um 10:18 Uhr

1432700cookie-checkWie verhindere ich, dass Windows das Programm blockiert, während ein Fenster gezogen oder eine Menütaste gedrückt gehalten wird?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy