c++ abkürzung - Vermeiden Sie das Label "(Nicht reagieren)" in Windows, während Sie viele Daten in einem Stück verarbeiten




mfc tutorial (8)

Ich muss gelegentlich eine große Menge von Daten aus einem Paket aus dem Netzwerk verarbeiten, was ausreichend lange dauert, wenn der Benutzer versucht, mit dem Anwendungsfenster zu interagieren, indem er dem Fenstertitel die Zeichenfolge "(Nicht reagieren)" hinzufügt. Ich bin mir bewusst, dass dies daran liegt, dass die Verarbeitung innerhalb eines Anrufs ausgeführt wird, um eine Nachricht zu verarbeiten (etwas im Stapel nach oben) und daher die Nachrichtenpumpe blockiert. Mir ist auch bewusst, dass der beste Weg, dies zu bewerkstelligen, darin besteht, die Daten asynchron in einem separaten Thread zu verarbeiten, so dass die Pumpe weiterlaufen kann. Dies ist jedoch eine GROSSE Desktop-Anwendung, die von oben bis unten mit einem einzigen Gewinde versehen ist und diese Verarbeitung sicher abschließt ist in unserem Zeitrahmen nicht machbar.

Also in diesem Sinne, gibt es zufällig eine Möglichkeit, dass ich zumindest den "nicht reagierenden" Moniker vermeiden kann (was für die meisten Nutzer wie "abgestürzt" heißt), indem ich sage, dass meine Anwendung gerade beschäftigt ist, bevor ich anfange Arbeit? Ich glaube, da ist etwas in dieser Richtung, wenn man auf eine Anfrage zum Schließen reagiert, man kann Fenster immer wieder nach mehr Zeit fragen, um zu vermeiden, dass man nicht "rechtzeitig schließt".

Ich sollte hinzufügen, dass dies eine C ++ - MFC-Anwendung ist.


Answers

Sie müssen die Verarbeitung irgendwie mit der Nachrichtenbehandlung verschachteln. Wenn Threads nicht in Frage kommen, möchten Sie vielleicht die Verarbeitung in mehrere Phasen aufteilen. Eine Möglichkeit, dies zu tun, besteht darin, beim ersten Empfang des Pakets eine gewisse Verarbeitung vorzunehmen und dann eine Nachricht an die Anwendung zu senden, in der steht: "hier weitermachen". Wenn die Anwendung die Nachricht "continue processing here" erhält, führt sie eine weitere Verarbeitung aus und sendet entweder eine weitere Nachricht "continue processing here" oder beendet sie.

Es gibt jedoch ein paar Überlegungen:

  1. Sie müssen sicherstellen, dass der Status der Anwendung jedes Mal konsistent ist, wenn Sie eine Nachricht an Sie selbst senden und sich auf die Nachrichtenschleife verschieben, da sonst möglicherweise eine andere Nachrichtenverarbeitung stattfindet. Dies kann zB dadurch geschehen, dass nur der Zustand in der letzten Verarbeitungsphase geändert wird.
  2. Ein anderes Paket kann ankommen, während Sie noch das erste Paket verarbeiten. Wenn das Ändern der Verarbeitungsreihenfolge für die Anwendung schlecht wäre, könnten Sie dies umgehen, indem Sie beispielsweise eine Nachricht "Erinnere mich, dieses Paket später zu verarbeiten" ausgeben, wenn dies geschieht.

Ich weiß nicht, ob dies innerhalb des Designs Ihrer Anwendung möglich wäre, aber es wäre eine Möglichkeit, das Problem zu lösen.


Wenn Sie einen Thread abzweigen, sind Sie wahrscheinlich besorgt über eine andere Benutzeraktion, die von dem Ergebnis der lang andauernden Operation abhängt (yeah, concurrency). Wenn Sie einen neuen Thread ausgliedern und einen Fortschrittsbalken erstellen, können Sie den Fokus auf den Fortschrittsbalken setzen, um zu verhindern, dass ein Benutzer mit dem Rest der Anwendung interagiert. Das sollte ausreichen, um einen wirklich einfachen zweiten Thread zu implementieren, ohne sich wirklich Sorgen um Nebenläufigkeit machen zu müssen, da Sie den Rest der App im Wesentlichen sperren, indem Sie die Benutzerinteraktion deaktivieren.


Wenn Sie nicht bereit sind, einen CWinApp::OnIdle , aber die lang andauernde Aufgabe in kleinere Teile CWinApp::OnIdle können, können Sie die Verarbeitung in CWinApp::OnIdle MFC CWinApp::OnIdle . Diese Funktion wird von der Nachrichtenpumpenschleife aus aufgerufen, wenn keine Windows-Nachrichten auf sie warten. Solange die Arbeit, die Sie in jedem OnIdle Anruf OnIdle , ausreichend kurz ist, halten Sie Ihre App ansprechend.


Ok, zuerst habe ich Fredericks Post hochgestuft, weil es so oder so, der zweite Thread ist wahrscheinlich der beste Weg zu gehen.

Wenn Sie jedoch nicht diesen Weg gehen möchten, können Sie die Nachrichtenwarteschlange manuell in die innere Schleife Ihrer Apps pumpen. Etwas wie das;

int Refresh()
{
    MSG       msg;
    if (PeekMessage (&msg, NULL, 0, 0,PM_NOREMOVE))
        if ((msg.message == WM_QUIT) 
          ||(msg.message == WM_CLOSE) 
          ||(msg.message == WM_DESTROY) 
          ||(msg.message == WM_NCDESTROY)
          ||(msg.message == WM_HSCROLL)
          ||(msg.message == WM_VSCROLL)
          ) 
          return(1); 
    if (PeekMessage (&msg, NULL, 0, 0,PM_REMOVE))
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
    return(0);
}

Dies ist tatsächlich ein Stück Code, den ich vor dem Umschreiben von etwas Ähnlichem als einen separaten Thread verwendet habe. Im Grunde schaue ich mir die Warteschlange an, filtere unerwünschte Nachrichten und poste den Rest. Es funktioniert in gewissem Umfang, verursacht aber gelegentlich gelegentliche unangenehme Nebenwirkungen, daher die Neufassung.


Angenommen, dass die Verarbeitung der Daten die ganze Zeit und nicht das Empfangen dauert (und Sie ernsthaft einen Thread vermeiden wollen - was IMOHO in Ordnung ist) der Daten, die Sie könnten:

  1. Erstellen Sie in der Funktion, mit der Sie gerade die Nachricht bearbeiten, einen modalen Dialog, der eine Nachricht "Bitte warten" anzeigt (oder versteckt, klein, was auch immer ...). Kopieren Sie (oder senden Sie einen Zeiger usw.) die Daten, die Sie verarbeiten, an eine Mitgliedsvariable dieses Dialogs.
  2. Geben Sie im modalen Dialog eine benutzerdefinierte Nachricht an Sie selbst ein, um die Daten zu verarbeiten.
  3. Behandeln Sie im Nachrichtenhandler des Dialogs eine "Einheit" der Arbeit. Verfolgen Sie, was die nächste "Einheit" der Arbeit ist. Senden Sie dieselbe Nachricht erneut.
  4. Wiederholen Sie diese Post-Nachricht "Schleife", bis Sie fertig sind. Schließen Sie Ihren Dialog.

Die Art des modalen Dialogs hält Ihre Anwendung "reaktionsfähig", mit minimalen Unterbrechungen oder Änderungen an der Funktionsweise der Anwendung. Reentrancy kann ein Problem mit modalen Schleifen sein, besonders wenn dies in einer WM_PAINT-Nachricht enthalten ist. (irgendjemand behauptet jemals innerhalb des Malcodes? gute Zeiten, gute Zeiten ...)

Der Dialog könnte sogar einen Abbrechen-Button haben, wenn Sie möchten.


Ich denke nicht, dass die Windows API Ihnen hier helfen kann.

Oder wie wäre es, wenn Sie ein Dialogfeld mit einem Fortschrittsbalken anzeigen und es in einem separaten Thread ausführen lassen?

Ein Text wie "Dieser Vorgang kann eine halbe Stunde dauern" im Dialogfeld ist möglicherweise ebenfalls angebracht.


Win32 hat eine Methode dafür in user32.dll .

DisableProcessWindowGhosting()

Deaktiviert die Fenster-Ghosting-Funktion für den aufrufenden GUI-Prozess. Fenster-Ghosting ist eine Windows-Manager-Funktion, mit der der Benutzer das Hauptfenster einer Anwendung, die nicht reagiert, minimieren, verschieben oder schließen kann.

Zusätzlich zu dem oben dokumentierten Verhalten habe ich hier (in einer C # -Anwendung) auch verifiziert, dass dieser Win32-Aufruf auch verhindert, dass das Etikett " Not Responding" auf dem Fenster wie gewünscht angezeigt wird.

Ich fand dies über die C # Antwort auf ähnliche Frage hier: https://.com/a/15380821/29152 .


Wie wäre es, das System-Menü loszuwerden und es dann wieder an einen anderen Ort zu bringen (sagen Sie neben dem Schließen-Button usw.)?





c++ windows mfc