Perché Environment.Exit () non termina più il programma?


134

Questo è qualcosa che ho scoperto solo pochi giorni fa, ho ottenuto la conferma che non è solo limitato alla mia macchina da questa domanda .

Il modo più semplice per riproporlo è avviando un'applicazione Windows Form, aggiungi un pulsante e scrivi questo codice:

    private void button1_Click(object sender, EventArgs e) {
        MessageBox.Show("yada");
        Environment.Exit(1);         // Kaboom!
    }

Il programma ha esito negativo dopo l'esecuzione dell'istruzione Exit (). Su Windows Form viene visualizzato "Errore durante la creazione dell'handle della finestra".

L'abilitazione del debug non gestito rende in qualche modo chiaro cosa sta succedendo. Il ciclo modale COM è in esecuzione e consente di recapitare un messaggio WM_PAINT. È fatale in una forma eliminata.

Gli unici fatti che ho raccolto finora sono:

  • Non si limita solo all'esecuzione con il debugger. Anche questo fallisce senza uno. Anche piuttosto male, la finestra di dialogo di arresto anomalo WER viene visualizzata due volte .
  • Non ha nulla a che fare con il testimone del processo. Il livello wow64 è piuttosto noto, ma una build AnyCPU si blocca allo stesso modo.
  • Non ha nulla a che fare con la versione .NET, 4.5 e 3.5 si bloccano allo stesso modo.
  • Il codice di uscita non ha importanza.
  • Chiamare Thread.Sleep () prima di chiamare Exit () non lo risolve.
  • Ciò accade sulla versione a 64 bit di Windows 8 e Windows 7 non sembra essere interessato allo stesso modo.
  • Questo dovrebbe essere un comportamento relativamente nuovo, non l'ho mai visto prima. Non vedo aggiornamenti pertinenti forniti tramite Windows Update , sebbene la cronologia degli aggiornamenti non sia più accurata sul mio computer.
  • Questo è un comportamento gravemente sconvolgente. Scriveresti codice come questo in un gestore eventi per AppDomain.UnhandledException e si arresta in modo anomalo allo stesso modo.

Sono particolarmente interessato a cosa potresti eventualmente fare per evitare questo incidente. In particolare lo scenario AppDomain.UnhandledException mi sorprende; non ci sono molti modi per terminare un programma .NET. Si noti che la chiamata a Application.Exit () o Form.Close () non è valida in un gestore eventi per UnhandledException, quindi non sono soluzioni alternative.


AGGIORNAMENTO: Mehrdad ha sottolineato che il thread del finalizzatore potrebbe essere parte del problema. Penso che sto vedendo questo e sto anche vedendo alcune prove per il timeout di 2 secondi che il CLR fornisce al thread del finalizzatore per terminare l'esecuzione.

Il finalizzatore si trova all'interno di NativeWindow.ForceExitMessageLoop (). C'è una funzione IsWindow () Win32 lì che corrisponde approssimativamente alla posizione del codice, offset 0x3c quando si guarda il codice macchina in modalità 32-bit. Sembra che IsWindow () sia un deadlock. Tuttavia, non riesco a ottenere una buona traccia dello stack per gli interni, il debugger pensa che la chiamata P / Invoke sia appena tornata. Questo è difficile da spiegare. Se riesci a ottenere una traccia dello stack migliore, mi piacerebbe vederlo. Il mio:

System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.ForceExitMessageLoop() + 0x3c bytes
System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Finalize() + 0x16 bytes
[Native to Managed Transition]
kernel32.dll!@BaseThreadInitThunk@12()  + 0xe bytes
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes

Nulla al di sopra della chiamata ForceExitMessageLoop, debugger non gestito abilitato.


2
Ho appena provato questo con .NET 4, 4 Client Profile, 3.5, 3.5 Client Profile, 3.0 e 2.0 e non ho ricevuto alcun errore su nessuno di essi. 64-bit Windows 7 è il mio sistema operativo, usando VS2010.
Steve

2
@Steve This happens on the 64-bit version of Windows 8Hans l'ha detto!
Parimal Raj,

7
Posso riprogrammare questo (Win 8, 64 bit), copiare / incollare il codice e cablare un pulsante e ottengo i sintomi esatti descritti.
keyboardP

3
Un'app in modalità console non è stata in grado di dimostrare questo problema, nulla può andare storto quando Exit () continua a pompare i messaggi.
Hans Passant,

3
Ho riscontrato questo tipo di comportamento Exit(0)un po 'di tempo fa con un po' di Win7 a 64 bit, il cambio ExitCodeora non ha aiutato a usare Process.GetCurrentProcess().Kill()senza alcun problema funziona
Sriram Sakthivel

Risposte:


85

Ho contattato Microsoft per questo problema e che sembrava aver ripagato. Almeno mi piacerebbe pensarlo :). Anche se non ho ricevuto la conferma di una risoluzione da loro, il gruppo di Windows è difficile da contattare direttamente e ho dovuto usare un intermediario.

Un aggiornamento fornito tramite Windows Update ha risolto il problema. Il notevole ritardo di 2 secondi prima dell'arresto anomalo non è più presente, suggerendo fortemente che il deadlock IsWindow () è stato risolto. E il programma si chiude in modo pulito e affidabile. L'aggiornamento ha installato patch per Windows Defender, wdboot.sys, wdfilter.sys, tcpip.sys, rpcrt4.dll, uxtheme.dll, crypt32.dll e wintrust.dll

Uxtheme.dll è la strana anatra. Implementa l'API di temi degli stili di visualizzazione ed è utilizzato da questo programma di test. Non posso esserne sicuro, ma i miei soldi sono su quello come fonte del problema. La copia in C: \ WINDOWS \ system32 ha il numero di versione 6.2.9200.16660, creato il 14 agosto 2013 sulla mia macchina.

Caso chiuso.


11
La cronologia di Windows Update non è più accurata sul mio computer. Tutto quello che so è che è stato installato il 14 agosto.
Hans Passant,

51

Non so perché non funzioni "più" , ma penso che Environment.Exitesegua i finalizzatori in sospeso. Environment.FailFastnon lo fa.

Potrebbe essere che (per qualche bizzarra ragione) hai strani finalizzatori in sospeso che devono essere eseguiti in seguito, facendo sì che ciò accada.


2
Potresti essere su qualcosa. Il finalizzatore è impegnato nell'esecuzione di NativeWindow.ForceExitMessageLoop (). Stranamente non è nidificato in nessuna chiamata.
Hans Passant,

@HansPassant: vorrei poter riproporre il problema in modo da poterlo esaminare, ma non posso. La chiamata è NativeWindow.ForceExitMessageLoopbloccata nel codice gestito o non gestito? È persino bloccato o è in attesa o in attesa di un messaggio o qualcos'altro?
user541686

Questo sembra certamente indicare il problema principale. Penso che sia la funzione Winapi IsWindow () alla base del problema. Penso di vedere anche il timeout di 2 secondi sul thread del finalizzatore, dopo di che tutto va all'inferno. Il debugger non mostra che sta eseguendo la chiamata IsWindow () ma ho già visto Windows giocare brutti scherzi con lo stack prima, cambiandolo quando si inserisce il codice critico all'interno di Windows.
Hans Passant,

4
Penso che il metodo Environment.FailFast (), nel caso specifico di eccezioni non gestite, sia probabilmente il metodo migliore da utilizzare comunque. (Non ne ero a conoscenza - grazie!) Comunque c'è un sacco di codice legacy che userebbe Environment.Exit () che sfortunatamente si schianterà in modo imbarazzante :(
Ian Yates

2
Sei decisamente su qualcosa. Nel mio caso, avevo avviato un IHost utilizzando IHost.StartAsync per eseguire alcuni test di integrazione, eppure dopo aver chiamato (e ovviamente in attesa) IHost.StopAsync, il processo non è ancora terminato. Solo dopo aver chiamato IHost.Dispose, il processo termina. Grazie per la punta
Malte R

6

Questo non spiega perché stia accadendo, ma non chiamerei Environment.Exitun gestore di eventi pulsante come il tuo campione - invece chiudo il modulo principale come suggerito nella risposta di René .

Per quanto riguarda un AppDomain.UnhandledExceptiongestore, forse potresti semplicemente impostare Environment.ExitCodepiuttosto che chiamare Environment.Exit.

Non sono sicuro di ciò che stai cercando di ottenere qui. Perché vuoi restituire un codice di uscita da un'applicazione Windows Form? Normalmente i codici di uscita sono utilizzati dalle applicazioni della console.

Sono particolarmente interessato a cosa potresti eventualmente fare per evitare questo crash. Per impedire la visualizzazione della finestra di dialogo WER, è richiesto Calling Environment.Exit ().

Hai un tentativo / cattura nel metodo principale? Per le applicazioni Windows Form ho sempre un tentativo / catch intorno al ciclo di messaggi, nonché i gestori di eccezioni non gestite.


Abbastanza sicuro che dovresti chiamare Application.Exitinvece di Environment.Exit.
user541686

7
Siamo spiacenti, questa non è una soluzione alternativa. È necessario chiamare Environment.Exit () per impedire la visualizzazione della finestra di dialogo WER. Nota anche il "fatto noto", il codice di uscita non ha importanza.
Hans Passant,

7
@Hans: catturare AppDomain.UnhandledException per cercare di evitare la finestra di dialogo WER in primo luogo legittima? Voglio dire, se c'è un'eccezione non gestita, la finestra di dialogo WER dovrebbe mostrare, non è vero?
Harry Johnston,

2

Ho riscontrato lo stesso problema nella nostra app, lo abbiamo risolto con il seguente costrutto:

Environment.ExitCode=1;
Application.Exit();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.