[Win32]PeekMessage und MsgProc

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Schrompf
Moderator
Beiträge: 4878
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

[Win32]PeekMessage und MsgProc

Beitrag von Schrompf »

Moin,

ich bin gerade verwirrt. Man hat ja irgendwo in seiner Hauptschleife ein

Code: Alles auswählen

while( PeekMessage(...) )
{
  case Bla:
  default:
    DispatchMessage();
}
Und dazu ein Fenster mit einer MsgProc, in der man nochmal auf ne subtil andere Art Messages behandelt. Nun war ich der Meinung, dass die beiden gleichwertig sein sollten. Ich müsste also alle Nachrichten auch in der while()-Schleife behandeln können. Aber Pustekuchen. Seit ich das Fenster nicht mehr selbst erstelle und demzufolge die MsgProc in anderen Händen liegt, kriege ich z.B. Alt+F4 nicht mehr mit. Oder einen Klick auf den Schließen-Button des Fensters.

Daher allgemein: wie hängen diese beiden Nachrichtenbehandler zusammen? Anscheinend kommen ja bestimmte Sachen direkt über den Callback rein und gehen an der PeekMessage()-Schleife vorbei. Und was bewirkt das Dispatch() am Ende? Ich dachte, das reicht die Nachrichten an den Callback weiter, aber woher soll es den kennen? Und was passiert, wenn keiner gesetzt ist?

Verwirrt.

Thomas
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von dot »

Schrompf hat geschrieben:Nun war ich der Meinung, dass die beiden gleichwertig sein sollten. Ich müsste also alle Nachrichten auch in der while()-Schleife behandeln können. Aber Pustekuchen. Seit ich das Fenster nicht mehr selbst erstelle und demzufolge die MsgProc in anderen Händen liegt, kriege ich z.B. Alt+F4 nicht mehr mit. Oder einen Klick auf den Schließen-Button des Fensters.

richtig beobachtet
Schrompf hat geschrieben:Anscheinend kommen ja bestimmte Sachen direkt über den Callback rein und gehen an der PeekMessage()-Schleife vorbei.
Hab leider grad nicht genug Zeit für eine ausführliche Antwort, daher: Schau dir mal an, was genau der Unterschied zwischen SendMessage() und PostMessage() ist. ;)
Schrompf hat geschrieben:Ich dachte, das reicht die Nachrichten an den Callback weiter [...]
tut es
Schrompf hat geschrieben:[...], aber woher soll es den kennen? Und was passiert, wenn keiner gesetzt ist?
Die Nachricht enthält ja das HWND des Fensters, an das sie geschickt werden soll. Wie genau würdest du denn ein Fenster ohne WndProc erzeugen? ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Krishty »

Mist; ich habe gerade auch keine Zeit :(

ALT+F4 löst WM_CLOSE aus. Der DispatchMessage()-Aufruf leitet das an die WndProc weiter. Das Hauptfenster sollte in diesem Fall PostQuitMessage() aufrufen – dann gibt eines der nächsten PeekMessage()s einen Abbruchwert zurück und die while-Schleife unterbricht.

Mehr in ein paar Stunden …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Helmut »

SendMessage umgeht in bestimmten Fällen die Nachrichtenschleife und ruft direkt die Windowsprozedur auf. Zum Beispiel in dem Fall, wenn DefWindowProc WM_CLOSE bearbeitet und über DestroyWindow WM_DESTROY sendet. Wenn du Nachrichten von einem Fenster abfangen willst kannst du z.B. SetWindowSubclass verwenden.
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Krishty »

Das DispatchMessage() schlägt zu jeder Nachricht nach, an welches Fenster sie geht; prüft, welche WndProc diesem Fenster zugeordnet ist (GetClassLongPtr(GCLP_WNDPROC)); und bewirkt dann die Abarbeitung. Windows-interne Nachrichten, die an kein Fenster gerichtet sind, werden ebenfalls verarbeitet.
Schrompf hat geschrieben:Und was passiert, wenn keiner gesetzt ist?
Falls kein HWND als Nachrichtenziel gesetzt ist, handelt es sich um eine Nachricht, die für deinen gesamten Thread bestimmt ist. Das kann inter-Prozess-Kommunikation sein (COM, Systemdienste, Windows Thread Pool?) oder Windows-interner Kram (Lebt das Programm noch?).

Wenn du jetzt das switch(msg) direkt in die Schleife schreibst:
  • Wie Helmut und dot sagen: die SendMessage()-Nachrichten kommen nicht mehr an. (Schlecht!)
     
  • Du verarbeitest auch Nachrichten, die nicht für dich bestimmt sind: Falls ein Hintergrunddienst sein COM-Hilfsfenster schließt, rauschst du in dein case WM_CLOSE. (Siehe The Old New Thing — The Dangers of Filtering Window Messages.) (Fatal und furchtbar zu debuggen!)
     
  • Re-entrancy wird unmöglich: WndProc ist rekursiv, dein while ist aber bloß sequentiell.
     
    Z.B. löst WM_PAINT ein WM_ERASEBKG aus (ohne es in die Fensterschleife zu stellen!); wartet, bis es verarbeitet wurde; und zeichnet dann weiter. Bei deinem Design ist das unmöglich weil das WM_ERASEBKG an deine WndProc geschickt würde, die aber niemals läuft weil du ja nur die Nachrichten verarbeitest, die in der Schleife landen.
     
    Das hat dann so lustige Nebenwirkungen wie, dass alle Timer deines Programms zu funktionieren aufhören, und viele mehr. (Fatal und furchtbar zu debuggen!)
     
  • Der Fenster-Manager schaltet manchmal in einen Modus: Während du ein Fenster vergrößerst oder verkleinerst kommen keine normalen Nachrichten an, sondern eine "Resize"-Schleife iteriert bis du die Maus wieder loslässt. (Darum laufen Programme nicht weiter, während man am Fensterrahmen zieht – sie werden verübergehend zu modalen Fenstern).
     
    Ich kann mir vorstellen, dass solche und ähnliche Fälle mit deinem while auch nicht mehr klappen werden. (Schlecht!)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4878
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Schrompf »

Nuja, das while war da ja schon immer :-) Das stammt aus irgendnem Tutorial-Code des Jahres 2003. Ich vermute, Du meinst andere whiles als das zitierte oben. Aber Danke für die umfangreichen Erklärungen. Es ist also unvermeidlich, sowohl PeekMessage()-Schleife als auch WndProc zu bedienen. Da ich letztere leider an GLFW abgegeben habe, muss ich mir also einen Rückkanal von dort einbauen. Der nächste Native-Hack für GLFW. <sarkasmus>Gut, dass ich endlich mal aufhöre, das Rad neu zu erfinden</sarkasmus>

Wobei, der Fairness halber, der Wechsel zu GLFW sich ja erst dann auszahlen wird, wenn ich Mac&Linux anpeile. Vielleicht patch ich da auch gleich noch Code aus der SDL2 oder SFML rein, damit die Lib auch iOS und Android mitmacht. Auf ihrer internen Wunschliste steht das ja schon seit Ewigkeiten. Oder ich bohr das GLFW-Inputgedöns auf, dass es mit RawInput arbeiten kann, dann kann ich OIS rauswerfen. Da stände ja auch ein größerer Rewrite an, um diesen absurden Factory-Mist rauszuwerfen, der ein Rudel impliziter Annahmen und Extra-Code bei den instanziierten Klassen braucht, nur um zu wissen, welche Instanz genau denn jetzt erzeugt werden soll.

Perfektionismus vs. schnelle Ergebnisse. Eigentlich hab ich ja Zeit und könnte mal einige Baustellen angehen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Krishty »

Ups; ja – ich meinte das switch direkt in der while anstelle der WndProc() :) Letztere bekommt Nachrichten, die erstere nicht bekommt, und umgekehrt. Geht es um Raw Input?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4878
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Schrompf »

Ja, es geht um RawInput. Und spekulativ auch um andere Spezial-Nachrichten... ich würde z.B. auch gern mitbekommen, wenn sich das primäre Sound Device ändert, damit ich den Sound neu initialisieren kann, wenn der Nutzer Kopfhörer ansteckt.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: [Win32]PeekMessage und MsgProc

Beitrag von BeRsErKeR »

Schrompf hat geschrieben:Und dazu ein Fenster mit einer MsgProc, in der man nochmal auf ne subtil andere Art Messages behandelt. Nun war ich der Meinung, dass die beiden gleichwertig sein sollten. Ich müsste also alle Nachrichten auch in der while()-Schleife behandeln können. Aber Pustekuchen. Seit ich das Fenster nicht mehr selbst erstelle und demzufolge die MsgProc in anderen Händen liegt, kriege ich z.B. Alt+F4 nicht mehr mit. Oder einen Klick auf den Schließen-Button des Fensters.
Was verwendest du denn für Parameter für das PeekMessage? Einen konkreten Window-Handle oder lässt du dir die Nachrichten aller Fenster im aktuellen Thread liefern (hwnd = NULL)? Vielleicht kommen Alt+F4 bzw. WM_CLOSE direkt als Non-Window-Event oder für ein anderes Window-Handle. Den Nachrichtenfilter wirst du wahrscheinlich nicht einschränken.
Schrompf hat geschrieben:Daher allgemein: wie hängen diese beiden Nachrichtenbehandler zusammen? Anscheinend kommen ja bestimmte Sachen direkt über den Callback rein und gehen an der PeekMessage()-Schleife vorbei. Und was bewirkt das Dispatch() am Ende? Ich dachte, das reicht die Nachrichten an den Callback weiter, aber woher soll es den kennen? Und was passiert, wenn keiner gesetzt ist?
Die MSG-Struktur, die von PeekMessage befüllt wird, enthält den Window-Handle und den übergibst du mit an DispatchMessage. Er kennt also das Ziel-Fenster und somit auch die MsgProc, die damit assoziiert ist oder ggf. DefWindowProc.


Nachtrag: WM_QUIT scheint man nur zu bekommen, wenn man beim PeekMessage oder GetMessage einen Window-Handle von NULL verwendet. WM_CLOSE und WM_DESTROY (und einige andere) hingegen, scheinen tatsächlich nur in der Fenster-Prozedur abrufbar zu sein. Warum auch immer.

Hier ein Zitat aus StackOverflow:
It seems that WM_CLOSE is sent, and the other message is posted.

GetMessage and PeekMessage only operate on posted messages (those posted with PostMessage). If a message is not posted but sent via SendMessage, it's handled immediately inside PeekMessage or GetMessage, so you can not get MSG struct for it.
Macht irgendwie auch Sinn, weil SendMessage ja blockiert, bis die Abarbeitung beendet ist, während PostMessage das Teil einfach in die Queue packt für eine spätere Abarbeitung.


Du könntest aber ein bisschen tricksen. #undef auf SendMessage, dann ein Zeichensatz-spezifisches Re-define, welches WM_CLOSE usw. verarbeitet und dann an SendMessageA bzw. SendMessageW weiterleitet bzw. im Fall von WM_CLOSE zusätzlich ein PostMessageA bzw. PostMessageW. ;)
Ohne Input kein Output.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4878
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [Win32]PeekMessage und MsgProc

Beitrag von Schrompf »

Danke für die Erklärungen. Das hat sich inzwischen aber erledigt, weil GLFW genügend Callbacks anbietet, um das meiste davon auch so umzusetzen. Die zu RawInput gehörenden WM_INPUT-Nachrichten sind jetzt die einzigen, die ich noch in der PeekMessage()-Schleife der Anwendung verarbeite. Wie die Live-Umschaltung des Sound Devices geht, krieg ich so auf die Schnelle nicht raus, aber das wird sich noch zeigen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Antworten