Anwendung verrennt sich in Application.DoEvents

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von siycah.

    Anwendung verrennt sich in Application.DoEvents

    Moin,

    ich habe ein für mich interessantes Problem mit dem Aufruf von Applicaiton.DoEvents().
    Mir ist bewusst, dass das eigentlich ein Böses Spielzeug ist und man es (im CleanCode) gar nicht benutzt/braucht.

    Jetzt ist es so, dass ich Software von meinem Vorgänger übernommen habe und weiterpflege.
    Dort wurde in einer Funktion einige Male das besagte Application.DoEvents() aufgerufen.

    Diese Funktion rufe ich aus einem Nicht-UI-Thread (WorkerThread) auf.
    Und an der Stelle von Application.DoEvents() bleibt er in der Funktion stehen.
    Der WorkerThread kommt demnach auch nicht aus der Funktion raus.

    Das UI ist weiter ansprechbar da anderer Thread.

    Wenn ich die Anwendung dann unterbreche, wird mir lediglich Form1.ShowDialog() angezeigt, in der ich meinen WorkerThread erstelle, der dann die Funktion ausführt.

    Der WorkerThread wird im Shown-Event der Form1 erstellt und gestartet.

    Einfach das Application.DoEvents() aus der Kommunikationsfunktion auskommentieren, dann funktioniert es.
    Allerdings würde ich gern wissen, warum sich die Anwendung daran zu Tode rackert.
    Und es muss ja auch einen Grund geben, weshalb das Application.DoEvents() dort überhaupt steht...

    Daher meine Frage; Kann man irgendwie nachvollziehen, welche Events in der Pipeline stehen, die dann bei Application.DoEvents() abgearbeitet werden?
    Oder gibt es noch eine Andere Erklärung, warum Application.DoEvents() nicht fertig wird?

    Danke für Eure Antworten!

    ~ Mfg

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „TRiViUM“ ()

    Hm und wenn die Funktion da stehen bleibt, ist das ein Problem?
    Das Programm hat beim Vorgänger ja wohl funktioniert oder wurde das nie genutzt?

    Während dein Code abgearbeitet wird, können Events stattfinden. Entweder durch User Input oder auch durch den Code selbst. DoEvents arbeitet die Sachen ab die bis dahin angefallen sind.
    Vielleicht ist das ein Problem, wenn das im Nebenthread läuft. Eigentlich sollte sich da ja nichts in der Warteschlange stehen, da ja die UI alle Messages sowieso verabeitet.

    Ich verstehe nicht was du meinst dir wird Form1.ShowDialog angezeigt.

    TRiViUM schrieb:

    wird mir lediglich Form1.ShowDialog() angezeigt
    Das sieht mir sehr nach dem mist sch... ranz VB6-Kompatibilitätsmodus aus.
    Mach das mal anständig:
    Dialoge: Instanziierung von Forms und Aufruf von Dialogen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Haudruferzappeltnoch schrieb:

    Ich verstehe nicht was du meinst dir wird Form1.ShowDialog angezeigt.
    Wenn man die Anwendung mit dem Pause-Zeichen unterbricht, springt der Cursor an die Stelle des Codes, wo er sich zu dem Zeitpunkt befindet.Und bei mir springt er dann zu der Zeile, wo ich den Dialog mit ShowDialog() aufrufe.
    Macht ja auch irgendwie Sinn, denn der Dialog ist zu dem Zeitpunkt ja auch noch geöffnet.
    Der grüne Pfeil links am Rand zeigt die Stelle, an dem die Anwendung zu stehen scheint:


    RodFromGermany schrieb:

    VB6-Kompatibilitätsmodus
    Da hab ich mich eher falsch ausgedrückt bzw. den Teil des Quellcodes zu stark vereinfacht.


    Der eigentliche Code-Ausschnitt zum Aufrufen des Dialogs ist wie folgt:

    C#-Quellcode

    1. DialogResult loginResult = DialogResult.None;
    2. using( var dialog = new frmUserLogin(1, true))
    3. {
    4. dialog.User = Global.GetInstance.UserManagement.GetUserByName(PrfName);
    5. loginResult = dialog.ShowDialog();
    6. }
    Ich versuche mal die Architektur darzustellen.

    Die Anwendung hat den Standard-UI-Thread (MainForm).
    Dieser ruft dann einen Dialog auf.
    Dieser Dialog startet im Shown-Event einen Thread.
    In diesem Thread wird eine Funktion aufgerufen, wo sich das DoEvents drin befindet.

    Ja stimmt, das macht Sinn.
    Ich versuche mir viel durch Dekompilieren der .NET-Bibliotheken zu selbst herzuleiten, nur ist das bei der Angelegenheit glaub ich etwas undurchsichtiger als bspw. zu gucken, wie ein Steuerelement gezeichnet wird...

    Haudruferzappeltnoch schrieb:

    außer der Nebenläufigkeit sehe ich da nichts Ungewöhnliches
    na, das ist eben **sehr** ungewöhnlich.
    Ich kenne Application.DoEvents nur im GUI-Thread aufgerufen, damit Gui-Elemente, also Controls, ihre Events abarbeiten können, so zwischendrin, während eiglich ein langandauernder Vorgang den Gui-Thread blockiert.
    Was passiert, wenn Application.DoEvents im Nebenthread aufgerufen wird, liegt ausserhalb meines Horizonts. Werden da nun die Controls alle aufgefordert, ihre Events im Nebenthread zu feuern? Das ergäbe wohl unzulässige threadübergreifende Aufrufe ohne Ende.

    Ich reime mir das so zusammen, dass das Proggi vor langer Zeit geschrieben wurde, im Gui-Thread lief, und die Thread-Blockade mit (dem bösen) Application.DoEvents umgangen hat. Später ist dann richtig auf Nebenläufigkeit umgestellt worden, aber aus Unkenntnis oder Flüchtigkeit ist die Zeile drin geblieben.

    Haudruferzappeltnoch schrieb:

    versuchen die Funktion im Dialog-Thread laufen zu lassen
    Ja, das kann ich malzum Testen so umbauen.
    Allerdings wollte ich mit dem neuen Thread ja das Blockieren vom UI verhindern.

    @ErfinderDesRades
    So wie ich das jetzt verstanden habe, wird das Application.DoEvents also für das UI genutzt.
    In meinem Fall sieht mir das nach etwas anderem aus.
    Die Langandauernde Funktion ist bei mir eine Kommunikation mit einem Gerät über USB.
    Die Schnittstelle bietet Events, wenn Daten empfangen wurden.
    Ich vermute also, dass mit dem DoEvents sichergegangen werden wollte, dass neue Daten auch wirklich verarbeitet werden.

    Würde man an dieser Stelle genau das mit dem DoEvents bezwecken können?
    Falls ja, würde ich mich auch trauen, es ganz auszulassen, da es zumindest bis jetzt keine Änderung (mit oder ohne DoEvents) bewirkt, was die Kommunikation angeht...

    TRiViUM schrieb:

    Ich vermute also, dass mit dem DoEvents sichergegangen werden wollte, dass neue Daten auch wirklich verarbeitet werden.


    Das wird die Intention gewesen sein, ja.

    Wenn ich die Dokumentation richtig verstehe, dann ist das allerdings nur für Windows-Events.

    Microsoft schrieb:


    Processes all Windows messages currently in the message queue.


    Dazu kommt, dass der Thread, der DoEvents() aufgerufen wird, schlafen gelegt wird.

    Microsoft schrieb:

    Caution

    Calling this method causes the current thread to be suspended while all waiting window messages are processed. If a message causes an event to be triggered, then other areas of your application code may execute. This can cause your application to exhibit unexpected behaviors that are difficult to debug. If you perform operations or computations that take a long time, it is often preferable to perform those operations on a new thread. For more information about asynchronous programming, see Asynchronous Programming Model (APM).


    Wenn du Kontrolle über die Schnittstelle hast, dann könntest du die evtl. mit Callbacks ausstatten, falls Events (aus welchem Grund auch immer) nicht performant genug sind.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.
    Danke erstmal für Deine Aufklärungsarbeit.
    Die Aussage, dass DoEvents() mit Vorsicht zu genießen ist, wird dann ja mit der Aussage von Microsoft begründet.
    Dass der aufrufende Thread schlafen gelegt wird, macht durchaus Sinn und wundert mich eigentlich auch nicht.

    Das mein Thread dann angehalten wird, erklärt imo noch nicht, weshalb er dann im DoEvents() festhängt.

    Was genau meinst du mit "nur für Windows-Events"? Die systemeigenen Betriebssystem-Events oder die UI-Events einer Form?

    siycah schrieb:

    dann könntest du die evtl. mit Callbacks ausstatten
    Was genau meinst Du mit Callbacks? Ich dachte, wenn Events gefeuert werden, rufen Sie Delegaten auf, welche dann in meinen Augen die Callbacks darstellen...

    siycah schrieb:

    Kontrolle über die Schnittstelle
    Leider nicht.
    Ist für mich ne Blackbox im Sinne von Verweis auf die DLL vom Hersteller (vermutlich auch nur ein .NET-Wrapper).

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „TRiViUM“ ()

    TRiViUM schrieb:

    Das mein Thread dann angehalten wird, erklärt imo noch nicht, weshalb er dann im DoEvents() festhängt.
    Spekulatius: DoEvents wartet auf eine Rückmeldung von der MessageLoop, dass alles abgearbeitet wurde. Wenn es aber keine MessageLoop gibt, gibt es auch keine Rückmeldung. Bin jetzt aber zu faul, mir die DoEvents-Implementierung anzuschauen, um das zu verifizieren.

    TRiViUM schrieb:

    Was genau meinst du mit "nur für Windows-Events"? Die systemeigenen Betriebssystem-Events oder die UI-Events einer Form?
    Alles, was als Nachricht in die MessageLoop kommt. Das ist alles, was die App betrifft. Das können Nachrichten vom OS an die App sein. Aber auch App-interne Nachrichten wie UI-Evens, UserInput, Komponentenrückmeldungen (FileSystemWatcher, Timer, …) …

    TRiViUM schrieb:

    Was genau meinst Du mit Callbacks? Ich dachte, wenn Events gefeuert werden, rufen Sie Delegaten auf, welche dann in meinen Augen die Callbacks darstellen...
    Ist m.E. vom Prinzip her in die gleiche Richtung gehend. Bei nem klassischen Callback gibst Du einer Callback-nutzenden Methode (M1) die Adresse einer anderen (M2). Und M1 ruft zu gewissen Zeitpunkten M2 auf. Events+EventHandler sind die Umsetzung des Observer-Patterns. Aber während beim ObserverPattern eben ein Objekt (Event-sender) mit einer Methode (EventHandler) verbunden wird, wird bei einem (Methoden)Callback eine Methode mit einer anderen verbunden.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Vermutlich brauchst du kein DoEvents wegen der USB-Schnittstelle. Möglicherweise hat die dll einen Cache und handelt die USB-Events für sich aus. Dann musst du nur regelmäßig prüfen, ob da was angekommen ist. Ich habe bisher immer alle DoEvents von Vorgängern entfernt, wenn sie nicht ausreichend dokumentiert sind. DoEvents sind der böse Bruder von Goto. Und tauchen immer in Gruppen auf.

    VaporiZed schrieb:

    Wenn es aber keine MessageLoop gibt
    Danke für Deine Antwort, daran hatte ich noch gar nicht gedacht!


    Die DoEvents-Implementierung anzuschauen ist meine Aufgabe, da werde ich mal nachforschen, weil mich das interessiert.

    Dann frage ich mich, warum Microsoft diese simple if-Abfrage nicht mit eingebaut hat, vorher zu prüfen, ob es überhaupt ein MessageLoop gibt.
    Aber ich habe mal gehört, dass da auch nur Menschen arbeiten... :)

    TRiViUM schrieb:

    Dann frage ich mich, warum Microsoft diese simple if-Abfrage nicht mit eingebaut hat
    Das hab ich Microsoft und mich bei einer anderen Geschichte auch gefragt: Adding Nothing to TextBox.AutoCompleteCustomSource stops app without any info. Da fehlte auch ein einfaches If, um zu sehen, ob das überhaupt geht, was probiert wird.

    Wird sicherlich nicht die letzte fehlende Kleinigkeit bei .NET sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    nicht die letzte fehlende Kleinigkeit bei .NET
    Klar, gar keine Frage. Ist scheinbar gängig, dass man bei so Problemen erstmal die eigene Entwicklung in Frage stellt - aber manchmal sind einfach die Anderen schuld :rolleyes:

    Allerdings habe ich gerade selber mal ein bisschen experimentiert und ein Projekt erstellt, welches nur die Architektur (wie oben beschrieben) wiederspiegelt.
    Dort kommt er aus dem DoEvents() heraus, obwohl für den WorkerThread kein MessageLoop existiert.

    Das kann dann also schonmal nicht die Erklärung für das Verhalten sein...
    Da sind mir andere hier zuvorgekommen und haben deine Fragen beantwortet :)

    Der Vorteil von Callbacks vs. Events ist, dass du weniger intransparente Schichten dabei hast. Das Framework macht da nichts mit, sondern du speicherst dir halt die Adresse deine Methode/Lambda weg und rufst sie dann auf.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems

    Selbstständiger Softwareentwickler & IT-Techniker.