[.NET / WPF] Voorkom "has stopped working" popup bij crash

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Ik heb een vrij ingewikkelde WPF applicatie waarbij het zo nu en dan voorkomt dat er iets mis gaat wat een unhandled exception als gevolg heeft. Ik gebruik de AppDomain.CurrentDomain.UnhandledException event om dit soort exceptions toch te kunnen loggen voordat ik de applicatie afsluit.

Een test situatie:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            
            // Handle exceptions
            AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;

            // Create test window
            var w = new MainWindow();
            w.Show();

            // Run test method that throws exception
            w.RunTest();
        }

        private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // Log exception
            var ex = (Exception)e.ExceptionObject;
            HandleException("Unhandled exception (domain).", ex);

            MessageBox.Show("Exception");

            // Shutdown
            Shutdown(1);
        }

        public void HandleException(string action, Exception ex)
        {
            //... Logging
        }
    }


Als ik in MainWindow.RunTest een simpele exception gooi dan gaat alles goed, de exception word gelogd en de applicatie sluit netjes af (eventueel kan ik zelf een message dialog tonen aan de user).


Echter, als er een exception voorkomt in een thread, dan gaat dit niet helemaal goed.
C#:
1
2
3
4
5
6
7
8
9
10
        public void RunTest()
        {
            var t = new Thread(T);
            t.Start();
        }

        private void T()
        {
            throw new Exception("1");
        }


Als ik dit draai dan wordt de exception wel gelogd, maar daarna sluit de applicatie niet "netjes" af. De boel lijkt nog steeds te crashen met een windows popup "TestApplication has stopped working" als gevolg.

Dit is een probleem omdat deze applicatie vaak in de achtergrond zal draaien terwijl de user een spel aan het spelen is. In sommige situaties (Windows 10 vooral) zal deze popup de user uit het spel halen (spel naar de achtergrond) en dat is enorm storend (het gaat om een race simulator waarbij je race gewoon voorbij is als dit gebeurt).


Hoe kan ik voorkomen dat de applicatie alsnog crasht? Het is prima dat de applicatie afsluit, maar graag zonder popup...


Een "oplossing" die ik heb gevonden is gebruik van de volgende setting in de config:
XML:
1
2
3
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="1"/>
  </runtime>


Als ik dit gebruik is het gedrag een beetje vreemd. De applicatie lijkt nu niet meer te crashen, echter blijft alles zelfs draaien. De 'Shutdown' wordt blijkbaar niet uitgevoerd. Dit is ook niet helemaal de bedoeling, als er zo'n unhandled exception optreedt kan ik natuurlijk niet garanderen dat alles nog lekker loopt dus is het beste om de boel maar af te sluiten.

Vreemd genoeg lijkt de code na de exception handling wel nog gewoon uitgevoerd - ik zie wel de messagebox met "Exception". Dit is een beetje lastig te debuggen want in de debugger komt de code nooit zover en blijft hij op de exception terug komen. Als ik de exe zonder debugger uitvoer echter dan wordt de messagebox gewoon getoond, maar de applicatie sluit niet af.

Verder vind ik ook hier en daar mensen die sterk afraden deze setting te gebruiken. Zijn er betere oplossingen?

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Wat/waar is de implementatie van Shutdown()?

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Dat is de Application.Shutdown method die ik probeer te gebruiken om de applicatie expliciet te stoppen.

Zojuist heb ik ook geprobeerd om Environment.Exit(0) er achteraan te gooien maar ook dat doet niks.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Neverwinterx
  • Registratie: December 2005
  • Laatst online: 07-10 11:34
Je kan dit doen:
We will have to set this registry value to 1 instead: HKEY_CURRENT_USER\Software\ Microsoft\Windows\Windows Error Reporting\DontShowUI
MSDN: How to disable the pop up that Windows shows when an app crashes &#8211...


Maar echt handig is dat natuurlijk niet, dan moet elk van je gebruikers in zijn registry settings gaan rommelen.

Acties:
  • 0 Henk 'm!

  • superschotje
  • Registratie: Juni 2010
  • Laatst online: 18-04 15:18
En wat als je Shutdown(1) achterwege laat, en alleen gebruik maakt van Environment.Exit(1) (ERROR_INVALID_FUNCTION)?

Wel die "legacyUnhandledExceptionPolicy" weer even uitzetten a.u.b.

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Neverwinterx schreef op zondag 4 december 2016 @ 17:19:
Je kan dit doen:


[...]

MSDN: How to disable the pop up that Windows shows when an app crashes &#8211...


Maar echt handig is dat natuurlijk niet, dan moet elk van je gebruikers in zijn registry settings gaan rommelen.
Het lijkt erop dat dit system-wide is? Dat is niet echt de bedoeling natuurlijk. Mijn applicatie mag "stilletjes" crashen maar verder wil ik niks aanpassen.
superschotje schreef op zondag 4 december 2016 @ 18:02:
En wat als je Shutdown(1) achterwege laat, en alleen gebruik maakt van Environment.Exit(1) (ERROR_INVALID_FUNCTION)?

Wel die "legacyUnhandledExceptionPolicy" weer even uitzetten a.u.b.
Ah, dit werkt inderdaad wel. Vreemd... Maar bedankt :)

Zijn er verder nog nadelen aan deze manier van afsluiten? Voor zover ik kan zien blijven er geen processen draaien oid dus het lijkt allemaal wel ok.

[ Voor 41% gewijzigd door NickThissen op 04-12-2016 21:26 ]

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Helaas nog steeds niet helemaal perfect...

Het lijkt erop dat ik maar minimale acties mag uitvoeren voor de Exit aan te roepen om de popup te voorkomen. Als ik bijvoorbeeld een database connectie probeer te sluiten ofzo dan gaat het alsnog mis en komt alsnog de popup.

Wat ik uiteindelijk wil is het volgende: als het spel waar het om gaat actief is EN het programma NIET actief is, dan mag er geen popup komen. Als het spel niet actief is, of als m'n programma wel actief is (beide gevallen ga ik er vanuit dat men niet aan het spelen is), dan toon ik een message box.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
        private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // Log exception
            var ex = (Exception)e.ExceptionObject;
            HandleException("Unhandled exception (domain).", ex);

            Environment.Exit(1);
        }

        public void HandleException(string action, Exception ex)
        {
            // Logging weg gelaten
            // ...
            
            // Check of er een messagebox moet komen
            bool shouldShowMessage = true;
            if (Game.IsRunning() && !ActivityUtil.IsApplicationActive())
            {
                // Spel draait en applicatie is niet actief, ga er van uit dat user aan het spelen is
                shouldShowMessage = false;
            }
            
            if (shouldShowMessage)
            {
                MessageBox.Show("Error");
            }
        }


Het vreemde is nu...

1. Als ik dit test met het spel op de achtergrond (Game.IsRunning = true) maar mijn applicatie actief (IsApplicationActive = true), dan werkt het prima, ik krijg de message box, dan sluit de applicatie af en er komt geen standaard windows popup.

2. Als ik dit test terwijl het spel actief is (IsApplicationActive = false) dan komt er geen message box (ok), maar komt er wel de standaard windows popup...

Huh?

Juist in de situatie waar ik het niet wil gaat het mis met exact dezelfde code... Ik snap het even niet meer.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Robicide
  • Registratie: Maart 2016
  • Laatst online: 22:02
Misschien trap ik hier een open deur in, maar waarom krijg je uberhaupt unhandled exceptions? Zou je die niet allemaal moeten afvangen en correct afhandelen, juist om het crashen van je applicatie te voorkomen?

Acties:
  • 0 Henk 'm!

  • jip_86
  • Registratie: Juli 2004
  • Laatst online: 10-10 11:40
Geen enkel programma is foutloos. Wij doen het ook, juist om te weten of je ze überhaupt krijgt. Gebruikers melden het namelijk veelal niet eens als ze de foutmelding bijvoorbeeld simpel weg kunnen klikken en door kunnen werken.

Acties:
  • 0 Henk 'm!

  • superschotje
  • Registratie: Juni 2010
  • Laatst online: 18-04 15:18
Wat Robicide zegt; het lijkt me sowieso verstandig alle exceptions af te vangen en af te handelen waar mogelijk.

Maar misschien is dit om één of andere reden niet te doen (3rd party code, uitzonderingen etc.)?

Probeer het eens te debuggen in Visual Studio en kijk welke Exceptions je krijgt. Misschien crasht die nu wel in jouw HandleException(..) methode. Misschien heb je last van CorruptedStateExceptions die niet standaard afgevangen worden in jouw handler (https://msdn.microsoft.com/en-us/magazine/dd419661.aspx). Met de debugger eraan moet je het kunnen achterhalen.

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 27-09 13:36
Uiteraard doe ik mijn best om alle exceptions die ik kan verwachten al eerder af te handelen. Echter is de applicatie ingewikkeld genoeg en afhankelijk van andere componenten (bijv het spel waar ik het over had) dat het af en toe nog wel eens voorkomt dat er iets tussendoor glipt.

Als dit gebeurt dan ga ik niet proberen om de applicatie draaiend te houden, het is beter dat de boel gewoon crasht (nadat de error gelogd is).

Het probleem is dat het crashen standaard een windows popup ter gevolg heeft, en deze popup kan in sommige gevallen het spel minimaliseren. Dit is niet een probleem met mijn applicatie specifiek, elke soortgelijke popup van elke applicatie kan dit doen, maar aangezien mijn applicatie op de achtergrond hoort te draaien wil ik het zo veel mogelijk voorkomen.

Een crash is prima, maar graag "stilletjes" als het spel draait zodat de gebruiker niet gestoord wordt.


Debuggen is erg lastig in dit geval. Ik heb een tijdelijke method toegevoegd die gewoon een Exception gooit en kijk dan wat er gebeurt. Ik kan echter geen breakpoint zetten op code die "na" de exception komt omdat de debugger daar nooit komt, hij blijft steeds terugvallen op de exception. Misschien is er een manier om er doorheen te komen maar het is nog niet gelukt... De manier waarop ik het nu test is de exe gewoon te draaien zonder debugger, dan loopt hij in ieder geval door tot de crash en kan ik zien of de popup komt of niet.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

Verwijderd

Tja, of het chique is, is een tweede, maar als je zeker weet dat de Exceptions zijn oorsprong hebben in de Thread void dan kun je overwegen om die te laten uitvoeren in een try/catch:

Dus in plaats van:

code:
1
2
3
4
 private void T()
        {
            throw new Exception("1");
        }


dit:
code:
1
2
3
4
5
6
7
8
9
10
11
 private void T()
        {
            try{
                
                 // hier je "gevaarlijke code" aanroepen
                 throw new Exception("1");
            
             }catch(Exception error){
                // handle exception here
            }
        }

[ Voor 18% gewijzigd door Verwijderd op 08-12-2016 11:01 ]

Pagina: 1