DoModal/EndDialog Bug



  • Kennt jemand einen besseren Workaround für den hier:

    http://www.codeproject.com/KB/dialog/notmodaldialogs.aspx?msg=1047533

    beschriebenen EndDialog Bug der MFC?


  • Mod

    Dieser Beitrag von Nish übertreibt ein Problem, das man meistens gar nicht hat.

    Er geht schon von etwas aus, was nicht sein sollte.
    1. Ein Modaler Dialog öffnet.
    2. Ein weitere Modaler Dialog öffnet.

    A: Sind es geschachtelte Dialoge dann ist MainFRame das Parent von Dlg und Dlg von Dlg2
    B: Das Prolem tritt nur auf wen der zweite Dialog auch MainFrame als Parent erhält, was meines Erachtens schon Unfug ist!

    Die Lösung ist elementar einfach:
    Liegt die Lösung bereitsin der MFC. Wird kein Parent bei einem Diaog angegeben, dann ermittelt, die MFC dasaktuelle top Level Fenster und verwendet es als Parent.

    Also gib einfach nichts an.

    Sag mir wo Du ein Problem siehst?



  • Ich finde nicht dass Nish übertreibt - er beschreibt einfach das Problem. Aber egal. Ich frage, weil ich das Problem gerade habe.

    Und zwar nicht mit zwei gleichzeitigen modalen Dialogen, sondern so:

    void MyDialog::OnFoo()
    {
        EnableWindow(false);
    
        FooDialog::Show(this); // macht DoModal() - siehe unten
    // Problem: *hier* ist "this" schon wieder "enabled", obwohl es das nicht sein sollte.
    // Daher kann der User im MyDialog auch lustig auf Buttons klicken, und
    // die Handler werden im PumpMessages unten auch fröhlich ausgefürt.
    
        BarDialog bar;     // EDIT: "this" wird natürlich erst bei Create übergeben
        bar.Create(this);
    
        while (!condition)
        {
            PumpMessages(100); // 100 ms Messages pumpen
            bar.UpdateProgress();
        }
    
        bar.DestroyWindow();
    
        EnableWindow(true);
    }
    
    // EDIT: nachtrag:
    
    void FooDialog::Show(CWnd* parent) 
    { 
        FooDialog dlg(parent); 
        dlg.DoModal(); 
    }
    
    // /EDIT
    

    Das ganze kommt an einigen Stellen vor, und ist etwas lästig.
    Bisher haben wir da einen ziemlich groben und hässlichen Workaround verwendet, den ich jetzt aber endlich mal loswerden möchte.

    Von daher die Frage, ob es da was "vollständigeres" gibt, als einfach OnOK und OnCancel zu überschreiben.

    Fälle wo z.B. in CDialog::OnInitDialog CDialog::EndDialog aufgerufen wird, werden davon ja nicht abgedeckt.


  • Mod

    Eben! Du übergibst nicht das ecte top level Parent. Sondern wieder das Eltern-Fenster.

    Dein Fehler!



  • Wieso sollte ich das Top-Level Parent Fenster übergeben?
    Der "owner" vom FooDialog soll ja MyDialog sein. Alleine schon wegen der Z-Order.

    Ich verstehe echt nicht wie das funktionieren soll, wenn ich verschachtelten Modalen Dialogen immer dasselbe Parent-Fenster übergebe. Das triggert doch dann erst recht den von Nish beschriebenen Bug.

    p.S.: sämtliche Beispiele die ich kenne, übergeben bei verschachtelten Dialogen immer "this" als Parent. Wenn das falsch sein sollte, sind vermutlich alle Beispiele in der MSDN falsch.



  • OK, vielleicht liegt hier ein Misverständnis vor.
    FooDialog::Show sieht so aus (dache das wäre klar):

    void FooDialog::Show(CWnd* parent)
    {
        FooDialog dlg(parent);
        dlg.DoModal();
    }
    

    p.S.: hab noch einen Fehler korrigiert: der nicht-modale Dialog der nach dem modalen kommt, bekommt das Parent-Fenster natürlich erst bei Create überbeben. Sollte aber keine Rolle für das Verständnis spielen, da der Fehler ja schon vorher passiert.

    Im Prinzip kann man es einfach nachstellen, indem man zwei Dialoge macht, und dann in einen das reinschreibt:
    (Theoretisch kann man natürlich auch nur eine Dialog-Klasse verwenden die sich selbst aufruft)

    void Dlg::OnButton1()
    {
        EnableWindow(false);
    
        Dlg2 dlg(this);
        dlg.DoModal();
    
        ASSERT(!IsWindowEnabled()); // <- BOOM
        EnableWindow(true);
    }
    

  • Mod

    Warum das überhaupt. Mach es doch so:

    void Dlg::OnButton1()
    {
        Dlg2 dlg();
        dlg.DoModal();
    }
    

    Was ist jetzt das Problem?



  • Oh Boy.
    Ich brauche das Fenster disabled, weil ich meinen eigenen Message-Pump fahre, und der Dialog eben disabled sein soll. Und ich habe keine Lust, den jedesmal neu zu disablen, bevor ich Messages pumpen gehe, nur weil irgendein blöder pseudo-modaler MFC Dialog den hinterhältigerweise wieder enabled hat.

    Siehe Code im 3. Posting.

    p.S.: natürlich kannst du gerne weiterhin davon ausgehen dass meine Frage sinnlos ist. Hilft mir aber nicht viel.


  • Mod

    Dann ist das auch kein Problem. Du kanst einfach dem dem DoModal sofort wieder das Fenster disablen. In der Zwischenzeit kann keine neue Nachricht eingehen.

    Ansonsten Frage ich mich gerade warum Du überhaupt DuModal verwendest.

    Warum nicht so:
    - Du disablest das Main-Window
    - Erzeugst Deinen Dialog nicht modal
    - Dann gehst Du in die Message-Loop.
    - Du kannst jederzeit mitbekommen, wenn der User OK/Cnacel drückt und entsprechend weiterverweilen in Deiner Schleife

    Geht das nicht?



  • Dann ist das auch kein Problem.

    Es ist kein unlösbares Problem, schon klar. Ein Bug in der MFC ist es IMO schon. Ich kenne zumindest kein anderes Windowing-Toolkit welches bei modalen Dialogen ein ähnlich seltsames Verhalten zeigen würde.

    Du kanst einfach dem dem DoModal sofort wieder das Fenster disablen

    So ähnlich hab' ich das jetzt gemacht. Die betroffene DLL hat sowieso eine gemeinsam verwendete Dialog-Basisklasse, und diese überschreibt jetzt einfach DoModal.

    Da drinnen such ich mir genau das Fenster raus welches die MFC dann in DoModal (bzw. PreModal) als Parent bestimmen wird (der Code ist fast 1:1 aus der MFC übernommen), und wenn das vor dem DoModal disabled war, tu ich es nachher auch wieder disablen.

    Zusätzlich hab' ich wie Nish vorschlägt OnOK und OnCancel überschrieben.

    Geht soweit, und muss denke ich reichen 🙂

    Ich wollte halt wissen, ob ich was übersehen habe, ob es vielleicht eine einfachere/sauberere/bessere Möglichkeit gibt.

    Warum nicht so:
    - Du disablest das Main-Window
    - Erzeugst Deinen Dialog nicht modal
    - Dann gehst Du in die Message-Loop.
    - Du kannst jederzeit mitbekommen, wenn der User OK/Cnacel drückt und entsprechend weiterverweilen in Deiner Schleife

    Geht das nicht?

    Pfuh. Vermutlich.
    Klingt aber nach mehr Aufwand, als das was ich jetzt gemacht habe.


Anmelden zum Antworten