2012-07-18 12 views
10

Cuando leí la documentación en MSDN para Application.Exit(), que dice:Application.Exit(), que es la primera operación de

Informa a todas las bombas mensaje de que deben terminar, y luego cierra todas las ventanas de la aplicación después de los mensajes han sido procesados

En mi entender, para informar a todas las bombas de mensajes que terminen, este método finalmente publicará un mensaje WM_QUIT en la cola de mensajes de la aplicación. Y después de publicado el mensaje, el método cerraría cada ventana (por MSDN). El problema está surgiendo aquí, cuando este método intenta cerrar cada ventana, el mensaje WM_QUIT no debería haber sido procesado, pero MSDN dijo "cierra todas las ventanas después de se han procesado los mensajes ".

Parece que la documentación es contradictoria para mi deducción. ¿Cuál es el problema aquí? Cualquier ayuda es muy apreciada.

+0

¿Por qué crees que el mensaje WM_QUIT no se habría procesado? –

+0

Una mejor pregunta sería: "¿Qué problema tiene debido a este orden de operaciones?" – Tergiver

+0

No hay un solo signo de interrogación en la PREGUNTA ... –

Respuesta

14

Pregunta interesante; utilizando ILSpy, vamos a echar un vistazo a lo Application.Exit() hace:

Vemos que el método crítico es ExitInternal

private static bool ExitInternal() 
{ 
    bool flag = false; 
    lock (Application.internalSyncObject) 
    { 
     if (Application.exiting) 
     { 
      return false; 
     } 
     Application.exiting = true; 
     try 
     { 
      if (Application.forms != null) 
      { 
       foreach (Form form in Application.OpenFormsInternal) 
       { 
        if (form.RaiseFormClosingOnAppExit()) 
        { 
         flag = true; 
         break; 
        } 
       } 
      } 
      if (!flag) 
      { 
       if (Application.forms != null) 
       { 
        while (Application.OpenFormsInternal.Count > 0) 
        { 
         Application.OpenFormsInternal[0].RaiseFormClosedOnAppExit(); 
        } 
       } 
       Application.ThreadContext.ExitApplication(); 
      } 
     } 
     finally 
     { 
      Application.exiting = false; 
     } 
    } 
    return flag; 
} 

Si todo va bien, la aplicación primeros cierre todas formas, a continuación, cierre todas las formas que se perdió, y luego, por último, se llama Application.ThreadContext.ExitApplication();

Como parte de ExitApplication, vemos la limpieza:

private static void ExitCommon(bool disposing) 
{ 
    lock (Application.ThreadContext.tcInternalSyncObject) 
    { 
     if (Application.ThreadContext.contextHash != null) 
     { 
      Application.ThreadContext[] array = new Application.ThreadContext[Application.ThreadContext.contextHash.Values.Count]; 
      Application.ThreadContext.contextHash.Values.CopyTo(array, 0); 
      for (int i = 0; i < array.Length; i++) 
      { 
       if (array[i].ApplicationContext != null) 
       { 
        array[i].ApplicationContext.ExitThread(); 
       } 
       else 
       { 
        array[i].Dispose(disposing); 
       } 
      } 
     } 
    } 
} 

// System.Windows.Forms.ApplicationContext 
/// <summary>Terminates the message loop of the thread.</summary> 
/// <filterpriority>1</filterpriority> 
public void ExitThread() 
{ 
    this.ExitThreadCore(); 
} 

¿Qué hace ExitThreadCore?

Bueno, no mata directamente el hilo, pero se pone en marcha el proceso:

ExitThread y ExitThreadCore en realidad no causa el hilo a terminar. Estos métodos generan el evento ThreadExit al que escucha el objeto de aplicación . El objeto Aplicación finaliza el subproceso .

Sin embargo, la parte realmente interesante parece suceder en array[i].Dispose(disposing)

Como parte de este método, vemos:

if (this.messageLoopCount > 0 && postQuit) 
{ 
    this.PostQuit(); 
} 

después del abandono() es lo que envía el mensaje WM_QUIT. Así que también deberíamos considerar cuándo se llama al Application.ThreadContext.Dispose, y siempre parece serlo después de que se cierren los formularios, aunque me complace que se corrijan allí.

De modo que el orden parece estar cerca de todas las formas, luego envíe el mensaje WM_QUIT. Creo que tiene razón, la documentación puede tener los eventos en el orden incorrecto ...

También confirma otro efecto secundario que a menudo vemos; cuando se cierra una aplicación pero todavía hay un hilo ejecutándose en segundo plano, el exe seguirá en la lista de aplicaciones en ejecución. Los formularios se han cerrado, pero todavía hay ese hilo deshonesto, zumbando a lo largo de evitar que se complete la salida().

Como Tergiver menciones:

Un hilo es o bien un hilo de fondo o un hilo plano. Los subprocesos de fondo son idénticos a los subprocesos de primer plano, excepto que los subprocesos de fondo no impiden que un proceso finalice. Una vez que todos los hilos de primer plano que pertenecen a un proceso han terminado, el tiempo de ejecución de lenguaje común finaliza el proceso. Los hilos de fondo restantes se detienen y no se completan.

(de Thread.IsBackgroundThread)

también me pregunté que hace Environment.Exit:

[SecurityCritical, SuppressUnmanagedCodeSecurity] 
[DllImport("QCall", CharSet = CharSet.Unicode)] 
internal static extern void _Exit(int exitCode); 

Se llama efectivamente a cabo con el sistema operativo para matar el proceso; esto terminará todas las ventanas con poca gracia; el OnFormClosing probablemente nunca llegue a disparar, por ejemplo. Como parte de esta finalización al por mayor, también [vacilaré en intentar el intento, ya que nunca lo he visto que falle] mato cualquier hebra, incluido el hilo "principal" en el que se está ejecutando el ciclo de mensajes.

+2

+1 buen detective. La razón por la cual su "hilo falso" deja el proceso abierto es porque ** ** no se ejecuta en segundo plano, es un hilo de primer plano. El proceso no puede finalizar hasta que todos los hilos de primer plano finalicen. Es por eso que debes establecer tus hilos de "fondo" como propiedad 'IsBackground' en' true'. – Tergiver

+0

@Tergiver gracias por la aclaración, es muy apreciado. – dash

+1

+1, gran respuesta –

Cuestiones relacionadas