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 wParam
zu 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.
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_MAXIMIZE
usw. 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.
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.
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.
durch
program is blocked
, ich denke, Sie meinen, Sie erhalten keine Windows-Nachrichten; was genau meinst du damitit 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