Manejo del proceso final de una aplicación de Windows

¿Es posible capturar el proceso final del administrador de tareas de una aplicación de Windows dentro de la misma aplicación de Windows? Estoy usando una aplicación win de C # 2.0 y me gustaría hacer un poco de procesamiento de la base de datos (cambiar una bandera de ‘Y’ a ‘N’ en la base de datos) cuando ocurre un proceso final.

No, no es posible conectar la decisión del sistema operativo para finalizar un proceso. Tenga en cuenta que esto no lo hace el administrador de tareas, y finalizar el proceso es responsabilidad del kernel.

Necesitarás hacer dos cosas aquí:

  1. Conecte los controladores de eventos a los mensajes de interfaz de usuario normales que le dicen a una aplicación que salga. Utilice estos eventos para conservar los datos, liberar recursos y, de lo contrario, salir limpiamente.
  2. Maneje las excepciones según corresponda para detectar errores y, si es posible, limpie y guarde los datos.

Aquí hay tres enlaces al blog de Raymond explicando por qué no puedes hacer lo que estás preguntando.

  • ¿Por qué no puedes atrapar TerminateProcess?
  • ¿Por qué algún proceso permanece en el Administrador de tareas después de que han sido asesinados?
  • La carrera armamentista entre progtwigs y usuarios

Además, abordé una pregunta similar de StackOverflow aquí .

¿Qué tal un enfoque ligeramente diferente?

Haga que su aplicación actualice un campo de fecha y hora, por ejemplo, LastPollDate de vez en cuando mientras se ejecuta, y tenga un campo separado, por ejemplo, “AppTerminatedNormally”, que establece en N y cambie a Y si obtiene un evento close.

Si la aplicación se elimina a través del Administrador de tareas, la fecha ya no se actualizará, y su AppTerminatedNormally seguirá siendo no.

De esta forma, podría ejecutar una consulta que encuentre todas las filas donde LastPollDate sea anterior a 10 minutos y AppTerminatedNormally es N, y usted tendría todas las sesiones que fueron canceladas anormalmente.

Todos van a escupir en este post, pero aquí va …

Está tratando de resolver el problema en el nivel incorrecto (es decir, ejecutar el código en su aplicación cuando el núcleo está matando la aplicación). El verdadero problema consiste en garantizar que la base de datos refleje correctamente la presencia (o ausencia) de sus aplicaciones cliente.

Para solucionar esto, evite que las aplicaciones se encuentren en un “estado incongruente” entre las interacciones del usuario. En otras palabras, no inicie transacciones que no pueda cometer rápidamente, no escriba datos en archivos que dejan el archivo en un estado medio escrito o ilegible, y no mantenga los recursos externos a su aplicación como incongruentes. estado fuera de las interacciones del usuario. En otras palabras, si su aplicación no está ocupada respondiendo a un controlador de eventos, debería estar lista para cerrar inmediatamente.

Si sigue la práctica anterior, encontrará muy pocos escenarios en los que necesite “limpiar rápidamente” antes de finalizar. Fuera de las interacciones en las que un usuario hace clic en “Aceptar” o “Guardar”, etc., una aplicación bien escrita debe poder sobrevivir a la terminación inmediata sin daños permanentes o daños en sus almacenes de datos.

Si tiene que establecer un indicador en la base de datos al salir (que suena típico de un patrón utilizado para detectar si un usuario está conectado o no), considere alguna de las siguientes alternativas:

  1. Periódicamente (quizás una vez cada 30 segundos) inserte / actualice un campo similar a una marca de tiempo en la base de datos, para indicar qué tan recientemente estuvo en línea una aplicación. Otras aplicaciones pueden inspeccionar estas marcas de tiempo para determinar qué tan recientemente estuvo en línea otra aplicación … si el valor está dentro de los últimos 30 segundos, la otra aplicación aún está en línea.

  2. Como Woodhenge sugirió correctamente, cree un proceso separado (idealmente un servicio) para monitorear el estado de la aplicación principal. Los servicios de Windows pueden configurarse para reiniciarse automáticamente en caso de falla del servicio. Este proceso de monitoreo emitirá marcas de tiempo en la base de datos.

Observe que las dos sugerencias anteriores resuelven el problema real (detectar si las aplicaciones están accediendo a la base de datos) sin dejar la base de datos en un “estado incongruente” (la bandera mencionada anteriormente es “Y” cuando la aplicación está realmente muerta y la bandera debería estar “NORTE”).

Si su objective es Windows Vista (o superior), puede interesarle la API RegisterApplicationRecoveryCallback …

http://msdn.microsoft.com/en-us/library/aa373345.aspx

Le permite especificar una rutina de callback en su aplicación que se invocará cuando el proceso esté por fallar. Nota: solo es para lockings, y no se llamará automáticamente si el proceso se mata deliberadamente.

Puedes p / invocar a esta API desde C # (lo he hecho), pero ten en cuenta que cuando invocas tu callback tu aplicación ya está en muy mal estado, y puedes hacer muy pocas suposiciones sobre el estado de tu memoria . Si tiene datos en memoria que quiera usar en esta rutina, los colocaría en un modo estático en un scope muy general para que tenga la mejor posibilidad de que no se haya “arreglado” cuando su rutina de callback carreras.

Existen otras API interesantes, relacionadas con esta, que le permiten reiniciar automáticamente su aplicación después de una falla, etc.

Lo que puede hacer es obtener la ID del proceso y supervisar el proceso, y puede usar la propiedad HasExited para verificar si el proceso ha finalizado o no. A continuación se muestra un código VB rápido (Disculpe, no tengo VS ahora. Esto fue escrito por mí en otro foro)

Public Class Form1 Dim p As ProcessStartInfo Dim process As Process Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click p = New ProcessStartInfo("iexplore.exe") process = process.Start(p) End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click MsgBox(process.Id) If process.HasExited Then MsgBox("yes") Else MsgBox("no") End If End Sub End Class 

El código anterior inicia Internetexplorer y el botón verifica si el proceso ha finalizado o no. Puede usar un proceso similar para obtener todo el proceso en ejecución y usar el ID de proceso.

No creo que esto sea posible desde la aplicación. El objective de End Task es detener el proceso de inmediato. Esto no permite la ejecución de ningún código de limpieza.

Shoban señaló otra forma de lograr su objective. Otra aplicación o servicio necesitaría buscar el proceso principal. Cuando el otro proceso no puede encontrar el proceso principal, puede hacer el procesamiento de la base de datos en ese punto.