WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr



  • Ich habe ein merkwürdiges Problem. Und zwar habe ich ein WinAPI Fenster und dieses empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr.

    Zum Hintergund: Ich habe einen Thread und in diesem habe ich ein WinAPI Fenster erzeugt. Mit Hilfe dieses Fensters kommuniziere ich mittels WM_COPYDATA Nachrichten mit einem fremden Prozess.

    Das funktioniert auch sehr gut, aber nach cirka 10000 WM_COPYDATA Nachrichten bricht auf einmal die Kommunikation weg.

    Die Kommunikation funktioniert folgendermaßen

    volatile std::atomic_bool mWaitingForResponse;
     
    
    void SendAndReceiveMessage()	// Wird mit 20Hz aufgerufen
    {
      // Nachricht senden, auf Antwort warten und 
      COPYDATASTRUCT cs;
      HWND Wnd; 				// Kommunikationsfenster
      HWND TargetWindow;		// Fenster des fremden Prozesses
      DWORD Timeout;			// Timeout nach abgebrochen wird in Millisekunden
    
      // Variable cs füllen
      // ...
      // Nachricht senden
      SendMessageTimeoutW(TargetWindow, WM_COPYDATA, reinterpret_cast<WPARAM>(Wnd), reinterpret_cast<LPARAM>(&cs), SMTO_NORMAL, Timeout, &q);		// Wird sauber aufgerufen
      // Nachrichtenschleife
      TimerID = SetTimer(Wnd, TRIMBLE_WINDOW_TIMER_ID, TIMER_INTERVAL, nullptr);
      while (GetMessageW(&Msg, Wnd, 0, 0))
      {
        if (!mWaitingForResponse)
          break;
        TranslateMessage(&Msg);
        DispatchMessageW(&Msg);
      }
      KillTimer(Wnd, TimerID);
    }
    
    LRESULT WndProc(HWND Wnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
      switch (uMsg)
      {
      case WM_TIMER:
      {
        // Prüfen ob der Timeout abgelaufen ist
        if (SysTimer() - mStartTime > mTimeout)				// SysTimer liefert die aktuelle Zeit in Millisekunden zurück
        {	
          mWaitingForResponse = false;
          PostThreadMessageW(ThreadID, WM_NULL, 0, 0);	// Rückgabewert ist immer ok
        }
        return 0;
      }
      case WM_COPYDATA:
      {		
        // ...
        mWaitingForResponse = false;
        PostThreadMessageW(ThreadID, WM_NULL, 0, 0);		// Rückgabewert ist immer ok
        return 1;
      }
      }
      return DefWindowProcW(Wnd, uMsg, wParam, lParam);
    }
    

    Nach den besagten 10000 Kommunikationen bleibt der Code an GetMessage hängen, obwohl mWaitingForResponse false ist.

    Der Code hat ein paar Macken: Angefangen von der Verwendung PostThreadMessageW() bis hin über while (GetMessageW(&Msg, Wnd, 0, 0)), usw...

    Aber nichtsdestotrotz frage ich mich, warum ich keine WM_TIMER Nachrichten mehr bekomme.



  • Vermutlich läuft die Queue voll mit Messages die nicht an Wnd gehen.

    Probier mal das:

      while (GetMessageW(&Msg, 0, 0, 0))
      {
        if (Msg.hwnd != Wnd)
        {
          char buf[100];
          sprintf(buf, "hwnd: %p, msg: %lu\r\n", Msg.hwnd, (unsigned long)Msg.message);
          OutputDebugStringA(buf);
        }
    
        if (!mWaitingForResponse)
          break;
        TranslateMessage(&Msg);
        DispatchMessageW(&Msg);
      }
    

    Dann kannst du nachsehen was das für Messages sind.



  • @hustbaer
    Danke für den Tipp, es sieht tatsächlich nach einer übergelaufenen Message-Queue aus.

    Ich habe da aber noch eine Sache gefunden. Ich nutze PostThreadMessage in einer nicht dokumentierten Art und Weise, in dem ich Nachrichten an einen Thread sende, die Nachrichten aber über die WndProc des Fensters in dem Thread bearbeite. Und in der MSDN Doku zu PostThreadMessage steht:

    Messages sent by PostThreadMessage are not associated with a window.

    Gehe ich nun hin und lösche zuerst die Nachrichtenschleife und nutze PostMessage anstatt PostThreadMessage, so funktioniert alles.



  • Achja, das ist mir gar nicht aufgefallen.

    In jedem Fall würde ich dir aber empfehlen den "window-filter" bei GetMessageW rauszunehmen. Bzw. verstehe ich nicht wieso du den überhaupt drinnen hast.



  • So wie ich das verstehe, gibt es 2 verschiedene Message-Loops: einmal im Mainthread und dann hier in einem eigenen Thread - und dann macht es ja Sinn, daß jeder Thread nur seine eigenen Window-bezogenen Nachrichten empfängt.
    Die Frage ist dann nur, wie geht man mit den Nachrichten um, die nicht Window-bezogen sind?



  • @Th69 sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    So wie ich das verstehe, gibt es 2 verschiedene Message-Loops: einmal im Mainthread und dann hier in einem eigenen Thread - und dann macht es ja Sinn, daß jeder Thread nur seine eigenen Window-bezogenen Nachrichten empfängt.

    Genau

    Die Frage ist dann nur, wie geht man mit den Nachrichten um, die nicht Window-bezogen sind?

    Deswegen habe ich bezogen das Kommunikationsfenster die Nachrichten auf das notwendigste reduziert. Den Timer habe ich durch eine C++ Variante ersetzt. Und ich habe die Nachrichtenschleife beim Anfang von SendAndReceiveMessage gelöscht.

    Damit läuft das Ganze schneller durch und fliegt auch nach cirka 70000 WM_COPYDATA Nachrichten, bei normaler Windows Nutzung, nicht um die Ohren.



  • @Quiche-Lorraine sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    @Th69 sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    So wie ich das verstehe, gibt es 2 verschiedene Message-Loops: einmal im Mainthread und dann hier in einem eigenen Thread - und dann macht es ja Sinn, daß jeder Thread nur seine eigenen Window-bezogenen Nachrichten empfängt.

    Genau

    Huch? Jeder Thread bekommt sowieso nur Nachrichten die für ihn bestimmt sind. Fenster sind ja an den Thread gebunden der sie erstellt hat. D.h. ein Thread bekommt Nachrichten für Fenster die er erstellt hat, sowie "Thread-Nachrichten" die an ihn gerichtet sind. Mehr nicht.

    Daher kann man in den allermeisten Fällen auch einfach GetMessageW(&Msg, 0, 0, 0) verwenden. Von GetMessage mit Einschränkung auf ein Fenster sollte man mMn. die Finger lassen, so lange man sich nicht sehr gut mit der ganzen Fenster-Gedöns von Windows auskennt. Und auch dann wird man es vermutlich selten bis nie brauchen.



  • Du hast recht. Ich habe noch mal bei GetMessage nachgelesen: jeder Thread hat seinen eigenen Nachrichtenpuffer.
    Und selbst mit Übergabe eines WND-Handles werden auch alle threadbezogenen Nachrichten (d.h. message.hwnd = null) abgerufen - dann wundert es mich aber, daß die Queue vollgelaufen ist.
    Dies könnte dann doch nur passieren, wenn noch ein 2. Fenster in diesem Thread erzeugt wurde?

    Edit: Blödsinn...



  • @Th69 sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    Und selbst mit Übergabe eines WND-Handles werden auch alle threadbezogenen Nachrichten (d.h. message.hwnd = null) abgerufen - dann wundert es mich aber, daß die Queue vollgelaufen ist.

    Wo liest du das raus? Ich muss sagen, ich weiss jetzt nicht auswendig wie das mit Thread-Messages ist. Ich hätte angenommen dass diese nicht bearbeitet werden wenn man auf ein Fenster filtert. Aber aus der Doku werd ich dazu nicht schlau.

    Dies könnte dann doch nur passieren, wenn noch ein 2. Fenster in diesem Thread erzeugt wurde?

    Wozu rumraten? Gib dir die Nachrichten einfach aus, z.B. mittels OutputDebugMessage wie in meinem Beispiel. Inklusive Window-Handle und Message-ID. Dann siehst du genau was da alles ankommt.



  • @Th69 sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    Und selbst mit Übergabe eines WND-Handles werden auch alle threadbezogenen Nachrichten (d.h. message.hwnd = null) abgerufen - dann wundert es mich aber, daß die Queue vollgelaufen ist.

    Das hast du falsch verstanden.

    Hier die entsprechenden Passagen aus deinem GetWindow Link zur WinAPI Doku

    "If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed."

    Nur wenn der hWnd parameter NULL ist, dann werden alle messages, welches zum aktuellen thread gehören, abgerufen.

    "A handle to the window whose messages are to be retrieved. The window must belong to the current thread."

    Wenn hWnd ein gültiger handle ist, dann werden nur messages für dieses Fenster abgerufen. Wobei das Fenster zum aktuellen thread gehören muss



  • @hustbaer sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    Von GetMessage mit Einschränkung auf ein Fenster sollte man mMn. die Finger lassen, so lange man sich nicht sehr gut mit der ganzen Fenster-Gedöns von Windows auskennt. Und auch dann wird man es vermutlich selten bis nie brauchen.

    Danke für den Tipp, ich gehe dem nach.

    EIne Frage: Was kann im Worst Case passieren?



  • @Quiche-Lorraine sagte in WinAPI Fenster empfängt nach einiger Zeit keine WM_TIMER Nachrichten mehr:

    EIne Frage: Was kann im Worst Case passieren?

    Das was du beobachtet hast: dass Messages nicht bearbeitet werden. Das führt direkt mal dazu dass bestimmte Dinge nicht passieren, die passieren sollten. Und dann kann es vorkommen dass irgendwann die Queue "voll" ist, und Windows neue Messages einfach verwirft anstatt sie in die Queue zu stellen.

    Aber das kommt natürlich sehr darauf an was für Messages so geschickt werden. In einem einfachen Projekt das nur ein Fenster aufmacht, kein COM, keine Thread-Messages und auch keine Third-Party Libraries, kann das schon gutgehen. Aber im Allgemeinen kann man nicht davon ausgehen.


Anmelden zum Antworten