Fokusproblem nach WaitForSingleObject und EnableWindow



  • Ich rufe aus meinem Program ein anderes Program B folgendermaßen auf:

    HWND ghWnd;
    HANDLE ghThread;
    HANDLE ghProcess;
    
    void WaitForProcess(void*)
    {
      WaitForSingleObject(ghProcess, INFINITE);	
      CloseHandle(ghThread);
      CloseHandle(ghProcess);		
      EnableWindow(ghWnd, TRUE);	
      Sleep(100);
      SetForegroundWindow(ghWnd);  
    }
    
    bool CallProcess(...)
    {
      PROCESS_INFORMATION piProcessinfo;
      STARTUPINFO siStartupinfo;
    
      EnableWindow(hWnd, FALSE);
      ...
      if (!CreateProcess(NULL, "B.exe ", NULL, NULL, FALSE, 0, NULL, NULL, &siStartupinfo, &piProcessinfo)) 
      {
        ...
        return false;
      }
      else
      {
        ghProcess = piProcessinfo.hProcess;
        ghThread = piProcessinfo.hThread;		
        ghWnd = hWnd;
        _beginthread(WaitForProcess, 0, NULL);	
      }
      return true;
    }
    

    Mein Program deaktiviert alle Benutzereingaben mittels EnableWindow(), ruft das Program auf und wartet mittels einem Thread auf das Ende des Prozesses. Ist dieser zu Ende, aktiviert die Threadfunktion wieder die Benutzereingaben und holt das Hauptfenster meines Programs wieder in den Vordergrund.

    Eigentlich könnte man auch in der Funktion CallProcess() auf das Ende des Prozesses warten und auf EnableWindow verzichten. Aber dort stört es mich gewaltig dass mein Fenster nicht aktualisiert wird, wenn ich ein Fenster aus B verschiebe. Genau aus dem Grund habe ich mich für die Lösung mittels Thread und Enablewindow entschieden.

    Die Lösung funktioniert auch soweit gut, bis auf die Tatsache das es mein Hautpfenster nicht zuverlässig in den Vordergrund holt. Im Debugger holt es öfters die Ausgabekonsole in der Vordergrund während auch ab und zu mal das Hauptfenster komplett in den Hintergrund geschoben wird. Das Hauptproblem scheint hierbei bei der Funktion EnableWindow zu liegen.

    😞

    Was mache ich hier falsch ???

    Wie kann ich einen anderen Prozess B aufrufen s.d.
    - mein Program auf das Ende von B wartet
    - der Hintergrund meines Programs aktualisiert wird sobald ich ein Fenster von B verschiebe
    - mein Program keine Eingabe akzeptiert während der Prozess B läuft ?



  • SetForeGroundWindow funktioniert nicht immer ohne weiteres:

    The system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:

    * The process is the foreground process.
    * The process was started by the foreground process.
    * The process received the last input event.
    * There is no foreground process.
    * The foreground process is being debugged.
    * The foreground is not locked (see LockSetForegroundWindow).
    * The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
    * No menus are active.

    Du kannst aber mit GetWindowThreadProcessId(GetForegroundWindow(), 0) die ThreadID des aktuellen Foreground-Fensters holen, mit GetCurrentThreadId die Deines Prozesses, dann mit AttachThreadInput den Input des Foreground-Threads an Deinen Thread attachen (anhängen?), dann SetForeGroundWindow aufrufen und mit einem erneuten Aufruf von AttachThreadInput mit false im dritten Parameter den normalen Input-Status wiederherstellen.
    Damit hast Du vorübergehend den in obiger Liste dritten Punkt erfüllt, so daß SetForeGroundWindow die gewünschte Wirkung erzielt.


  • Mod

    Dein Problem ist vollkommen hausgemacht. Windows würde sicher genre machen was Du willst! 🕶

    Aber das Problem ist, dass Dein Fenster inaktiv ist. Du selbst hast ein EnableWindow(FALSE) gemacht.
    Wenn der andere Prozess terminiert will Windows Dein Fenster aktiv machen, kann es aber nicht, denn es ist inaktiv.

    Erzeuge ein kleines Toplevel Window (Dialog o.ä.) der Dein Main-Window disabled, selbst aber aktiv bleibt und auf Deinen Prozess wartet.

    PS: Was Belli schreibt spielt hier keine Rolle.



  • Dein Problem ist vollkommen hausgemacht. Windows würde sicher genre machen was Du willst! 🕶

    Keine Sorge, ich bin kein Mensch der nach dem Motto "Wenn du ein Problem hast, suche dir immer einen Sündenbock (Windows)." handelt. 😃 Wenn ich sogar ehrlich bin, empfinde ich mich eher Linux nach Windows Konvertit. Es scheint wohl immer ein wenig davon abzuhängen unter welchem Betreibssystem man arbeitet.

    Ich habe das von Belli mal ausprobiert und es funktioniert leider nur zum Teil. Benutze ich den Quellcode wie ich ihn hier angegeben habe, funktioniert es relativ gut. Aber selbst dann sieht man schön dass das Hauptfenster in den Hintergrund gelegt wird und danach wieder in den Vordergrund gelegt wird. Wenn ich aber doch auf einen Prozess in der Funktion CallProcess (eig. Funktion CallProcess2) warte, taucht die Problematik immer wieder auf.

    Aber das Problem ist, dass Dein Fenster inaktiv ist. Du selbst hast ein EnableWindow(FALSE) gemacht.
    Wenn der andere Prozess terminiert will Windows Dein Fenster aktiv machen, kann es aber nicht, denn es ist inaktiv.

    Dumme Frage:
    Kann es sein das Windows mein Fenster aktiv machen will sobald der Prozess terminiert bzw. sobald die Funktion WaitForSingleObject beendet wurde ? Kann es sein das mein EnableWindow(ghWnd, TRUE) aufgerufen wird nachdem Windows das Fenster aktiv machen wollte ?


  • Mod

    Bitte ein Bit schrieb:

    Aber das Problem ist, dass Dein Fenster inaktiv ist. Du selbst hast ein EnableWindow(FALSE) gemacht.
    Wenn der andere Prozess terminiert will Windows Dein Fenster aktiv machen, kann es aber nicht, denn es ist inaktiv.

    Dumme Frage:
    Kann es sein das Windows mein Fenster aktiv machen will sobald der Prozess terminiert bzw. sobald die Funktion WaitForSingleObject beendet wurde ? Kann es sein das mein EnableWindow(ghWnd, TRUE) aufgerufen wird nachdem Windows das Fenster aktiv machen wollte ?

    Genau so ist es. Es ist ein immer wiederkehrendes Problem wie auch bei der Implementierung einer modalen Message-Loop (siehe CDialog::DoModal)
    Die Lösung habe ich Dir schon genannt.



  • Und sie funktioniert saugeil 😃 Danke, danke, danke 😃

    Ich muss mal jetzt nur noch schauen ob ich den Dialog versteckt bekomme oder ob ich daraus einen Hinweisdialog bastele.



  • Aber was ist der Grund dafür, daß SetForeGroundWindow mit der von mir erwähnten Methode (AttachThreadInput usw) nicht funktionieren will?


  • Mod

    Belli schrieb:

    Aber was ist der Grund dafür, daß SetForeGroundWindow mit der von mir erwähnten Methode (AttachThreadInput usw) nicht funktionieren will?

    Müsste man debuggen. Dieses Umsetzen des Focus und aktiven Fensters ohne das die Message-Loop zwischen drin läuft führt zu allerlei komischen Effekten, die sich meistens schnell erklären lassen, wenn man das ganze mit Spy++ analysiert.

    Ein Grund wäre: Windows hat genau das zuvor probiert, da ging es nicht, Nachrichten die diesen Zusatnd verändern sind nicht eingegangen, also wird der Versuch ignoeriert, obwohl EnableWindow ausgeführt wurde...
    Nur eine Vermutung oder ins blaue rein geraten.

    PS: Wenn man es richtig macht, benötigt man SetForegroundWindow für diese Aktion gar nicht.

    PPS: Nur mal so, warum ich diesen Weg sogar als falsch erachte:
    Nehmen wir an, Programm1 startet Programm2 und das dauert lange. Warum sollte Programm1 sich in den Vordergrund zwängen wenn der User weggeschaltet hatr (ALT-TAB) wenn nun Programm2 terminiert?
    SetForeGroundWindow mit AttachThreadInput des aktuellen Fensters wäre genau das was man garantiert nicht will! Es unterbricht den Benutzer in der von ihm gewählten Arbeit und zwängt Programm1 wieder in den Vordergrund.



  • Martin Richter schrieb:

    Belli schrieb:

    Aber was ist der Grund dafür, daß SetForeGroundWindow mit der von mir erwähnten Methode (AttachThreadInput usw) nicht funktionieren will?

    Müsste man debuggen. Dieses Umsetzen des Focus und aktiven Fensters ohne das die Message-Loop zwischen drin läuft führt zu allerlei komischen Effekten, die sich meistens schnell erklären lassen, wenn man das ganze mit Spy++ analysiert.

    Du meinst, weil das Ganze in einem Thread ohne Loop geschieht, denn die MessageLoop des "Haupt"Threads läuft doch weiter?

    Martin Richter schrieb:

    Ein Grund wäre: Windows hat genau das zuvor probiert, da ging es nicht, Nachrichten die diesen Zusatnd verändern sind nicht eingegangen, also wird der Versuch ignoeriert, obwohl EnableWindow ausgeführt wurde...
    Nur eine Vermutung oder ins blaue rein geraten.

    Nun, ich war überzeugt, daß EnableWindow eben genau diesen Zustand, der dem entgegensteht, ändert.

    Martin Richter schrieb:

    PPS: Nur mal so, warum ich diesen Weg sogar als falsch erachte:
    Nehmen wir an, Programm1 startet Programm2 und das dauert lange. Warum sollte Programm1 sich in den Vordergrund zwängen wenn der User weggeschaltet hatr (ALT-TAB) wenn nun Programm2 terminiert?
    SetForeGroundWindow mit AttachThreadInput des aktuellen Fensters wäre genau das was man garantiert nicht will! Es unterbricht den Benutzer in der von ihm gewählten Arbeit und zwängt Programm1 wieder in den Vordergrund.

    Okay, falsch im Sinne von falscher/schlechter Designentscheidung des Entwicklers, aber nicht falsch im Sinne von 'geht nicht' - obwohl, irgendwie scheint es ja im hier geschilderten Fall tatsächlich nicht zu gehen 🙄



  • @Belli:
    Ich habe mir leider das Problem dahinter nicht genau angeschaut, warum deine Lösung im zweiten Fall nicht funktionierte. 😞

    Mein Code macht im zweiten Fall wesentlich mehr als ich in meinem Code-Beispiel angedeutet habe. Ich starte anfangs den Prozess B. Wenn dieser terminiert sende ich eine Nachricht an die Hauptschleife meines Programs. Diese ruft wiederrum den Prozess C auf und wartet auf das Ende des Prozesses. Danach macht die Funktion noch einige Dinge und kehrt dann zur Threadfunktion für B zurück, welche das Hauptfenster in den Vordergrund legt...

    void WaitForProcess(void*)
    {
      WaitForSingleObject(ghProcess, INFINITE);   
      CloseHandle(ghThread);
      CloseHandle(ghProcess);       
    
      SendMessage(ghWnd, WM_MYMSG_1, 0, 0); // Kritisch da die Nachricht wiederrum einen Prozess startet ?
    
      EnableWindow(ghWnd, TRUE);   
      Sleep(100);
    
      GetWindowThreadProcessId(...);
      GetCurrentThreadId(...);
      AttachThreadInput(..., TRUE);
      SetForegroundWindow(ghWnd);  
      AttachThreadInput(..., FALSE);
    }
    

    Hmm, könnte mir gut vorstellen das hier der Hase vergraben ist. 😕

    Aber danke euch allen. Es funktioniert tadellos. 🙂


  • Mod

    Und an was für ein Fensterhandle hast Du den AttachThreadInput ausgeführt?
    Doch nicht etwa an das Dein aktuelles Fenster?
    Hoffentlich doch wohl an das aktuelle aktive?



  • Und an was für ein Fensterhandle hast Du den AttachThreadInput ausgeführt?
    Doch nicht etwa an das Dein aktuelles Fenster?
    Hoffentlich doch wohl an das aktuelle aktive?

    Das hatte ich überprüft und müsste auch soweit gestimmt haben.



  • Hier mal ein Link in die MSDN:
    http://msdn.microsoft.com/de-de/library/bb979463.aspx
    Ich glaube, das Problem ist tatsächlich, daß der Attach-Thread nicht der Thread ist, der das in den Vordergrund zu holende Fenster erstellt hat, und die Mess.Loop dazu verwaltet, sondern der Wartethread.


  • Mod

    Nein! Es muss der Thread sein der aktuell im Vordergrund liegt! (GetForegroundWindow)

    Denn dieser wird ja geschützt und nur dieser hat freie Erlaubnis beliebige Fenster in den Vordergrund zu bringen!



  • Klar, ich meine den anderen, mit diesem zusammenzuattachenden Thread. Das ist in diesem Fall hier ja der Wartethread, der keine Msg.Loop hat, und dementsprechend gar keine In-Message verarbeiten kann. Das könnte doch das Problem sein ... ich denke, wenn man stattdessen den "Haupt"thread mit der Msg.Loop nähme, würde es vllt. klappen ...


Anmelden zum Antworten