miércoles, 1 de octubre de 2014

Receta C# No. 4-16: Cómo Terminar un Proceso

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Básicos sobre la terminación de procesos
3.2 Método CloseMainWindow
3.3 Método Kill
4. Práctica: Código C#
5. Artefactos
6. Conclusiones
7. Glosario
8. Literatura & Enlaces

0. Introducción

En la receta C# número 4-15 aprendimos cómo iniciar un nuevo proceso, ahora tendremos la ocasión para comprender los básicos para realizar la operación de finalización de un proceso en ejecución utilizando varios de los elementos de programa disponibles en la clase Process (N:System.Diagnostics). Uno de los casos básicos de ejemplo de uso a los que recurriremos consiste en la iniciación del Bloc de notas de Windows y después de transcurrido cierta cantidad de tiempo terminar su ejecución abruptamente con el método Kill.

1. Problema

Después de haber iniciado un proceso o servicio en el sistema, requerimos también entender y usar un método para su finalización.

2. Solución

La clase System.Diagnostics.Process además de proveer los métodos de iniciación de un proceso, también facilita un conjunto de métodos para la finalización de un proceso o servicio. Como:
  • CloseMainWindow, y 
  • Kill
En la siguiente sección comprenderemos cómo podemos usar estos dos métodos para finalizar una aplicación que contiene una ventana principal, y para la terminación abrupta de procesos y servicios, respectivamente.

3. Discusión de la Solución

3.1 Básicos sobre la terminación de procesos

En la receta C# 4-15 (cfrCómo Iniciar un Nuevo Proceso) discutimos cómo iniciar un proceso utilizando varias de las versiones sobrecargadas del método Start. A partir de aquí debemos tener claro que la obtención de una instancia de proceso representada como un objeto Process nos facilitará a través de código administrado (cfrLiberación de Recursos y Finalizadores) su finalización normal o abrupta (en el peor de los casos).

Para lograr lo anterior, la clase Process [5] provee un conjunto de métodos para la obtención de los procesos o aplicaciones en ejecución. Estos métodos los podemos enumerar en la Figura 1 [1]:
Métodos de Process para obtener el conjunto de procesos en ejecución
Figura 1. Métodos de Process para obtener el conjunto de procesos en ejecución [1].
Luego, los pasos a seguir una vez que obtenemos la instancia (o representación lógica) del proceso, debemos seguir las siguientes estas acciones para terminar su ejecución:
  1. Invocar el método CloseMainWindow o Kill.
  2. En el caso de invocar al método CloseMainWindow, podemos validar que la finalización de la aplicación ha sido correcta a través del valor de tipo de retorno de este método (true o false).
  3. Si hemos invocado al método Kill lo que hará es terminar de forma forzada o abrupta el proceso o servicio.
Veamos con más detenimiento estos métodos de Process para comprender su uso y utilidad.

3.2 Método CloseMainWindow

El método CloseMainWindow [6] como mencionamos arriba finaliza un proceso que posee interfaz de usuario. El mensaje de terminación es enviado a la ventana principal de la aplicación. Esta es su firma:

public bool CloseMainWindow()

Descripción puntual:
  • Tipo de retorno:
    • bool: true si el mensaje de finalización fue enviado satisfactoriamente (esto no garantiza que el proceso asociado a la aplicación haya finalizado de forma correcta); false, si el proceso no posee una ventana principal o si la ventana principal ha sido deshabilitada (diálogo modal habilitado o en primer plano).
Los procesos o servicios en ejecución ejecutan un conjunto de rutinas que permiten al sistema operativo conocer su estado. Estas rutinas se invocan cada vez que un mensaje del sistema operativo Windows envía una señal al proceso. La invocación de CloseMainWindow causa que la aplicación solicite al usuario su finalización (por ejemplo si tiene un editor de texto en el que no se han guardado los cambios de edición). El usuario puede optar por continuar usando la aplicación y omitir el cierre solicitado por el método CloseMainWindow. Para evitar lo anterior tendremos que usar el método Kill (que discutiremos en la siguiente sub-sección) para la finalización forzada de la aplicación.

Comprendamos mejor cómo funciona este método recurriendo al siguiente ejemplo de uso (adaptado de [6]):

Dentro del bloque try (líneas 15-43) ocurren las siguientes acciones:
  • Línea 17: definimos una variable de tipo Process.
  • Línea 19: Iniciamos un nuevo proceso que ejecuta el Bloc de notas de Windows (notepad.exe).
  • Líneas 23-43: Ciclo de 5 iteraciones para comprobar la memoria física en uso del proceso recién creado.
  • Línea 29: Invocación al método Refresh [7] para descartar cualquier información de caché relacionada con el proceso.
  • Línea 32: Muestra en la salida estándar el consumo actual de memoria física por parte del proceso.
Continuando, en la línea 46 invocamos al método CloseMainWindow para solicitar el cierre de la ventana principal del Bloc de notas. Finalmente, con la invocación del método Close (línea 49) liberamos los recursos de sistema ocupados por el proceso recién terminado.

Compilación:


  1. csc /target:exe UsoCloseMainWindow.cs

Ejecución assembly:


  1. .\UsoCloseMainWindow.exe

> Prueba de ejecución:
Ejecución assembly UsoCloseMainWindow.exe
Figura 2. Ejecución assembly UsoCloseMainWindow.exe.

3.3 Método Kill

El método Kill [8] detiene de forma abrupta el proceso asociado a una instancia de Process. Esta es su firma:

public void Kill()


Como se manifiesta en [8], este método forza la terminación de un proceso, mientras que la invocación de CloseMainWindow (sección 3.2) únicamente solicita la terminación de la aplicación. Forzar la terminación de una aplicación puede causar la pérdida de datos de usuario (e.g., documento de texto con cambios en la edición) o de aplicación (e.g., pérdida de preferencias de aplicación, configuración, perfil de usuario).



Notas a tener en cuenta respecto a Kill:
Invocación asincrónica del método Kill
Figura 3. Invocación asincrónica del método Kill [8].

Generación de la excepción Win32Exception
Figura 4. Generación de la excepción Win32Exception [8].
Introduzcamos un ejemplo de uso para demostrar cómo podemos realizar la finalización abrupta del Bloc de notas mientras editamos un archivo de texto plano:

Archivo C# UsoKill.cs [enlace alternativo]:

En la línea 12 creamos un nuevo proceso para la ejecución del Bloc de notas de Windows. Más adelante, línea 15, realizamos una espera de 3 segundos para dejar que el proceso se ejecute en la transición de este tiempo, finalmente (línea 18) invocamos el método Kill para terminar abruptamente la aplicación.

Compilación:


  1. csc /target:exe UsoKill.cs

Ejecución assembly:


  1. .\UsoKill.exe

> Prueba de ejecución:
Ejecución assembly UsoKill.exe
Figura 5. Ejecución assembly UsoKill.exe.

En la animación (Figura 5) podemos observar que a pesar que tenemos datos texto editados en el documento de Bloc de notas, la invocación del método Kill no toma medidas en el guardado de datos, lo que se traduce en pérdida de datos de usuario.

4. Práctica: Código C#

Adaptemos el ejemplo encontrado en [1] para afianzar los conceptos presentandos en la sección anterior.

En este ejemplo creamos un proceso para la ejecución de una instancia del Bloc de notas de Windows. Pasados 5 segundos terminamos su ejecución. El primer intento de terminación de ejecución del Bloc de notas consiste en la invocación del método CloseMainWindow. Si la invocación previa retorna false, se debe a que el Bloc de notas aún se haya en ejecución. Consecuentemente, invocamos al método Kill para la terminación definitiva (abrupta) del Bloc de notas.

En la línea 15 creamos un proceso para la ejecución del archivo notepad.exe y la apertura de un archivo de texto plano llamado ArchivoTextoPlano.txt. En la línea 20 invocamos al método Sleep para esperar que el Bloc de notas finalice su ejecución. Con la sentencia if (línea 26) validamos que la invocación del método CloseMainWindow no ha sido satisfactoria, se ser así, entonces se invoca el método Kill (línea 32) para la terminación abrupta de la aplicación. En caso contrario, líneas 34-47, con una una sentencia if invocamos al método WaitForExit para dar un tiempo de gracia (2 segundos) a la terminación del Bloc de notas; si esto no funciona invocamos al método Kill (línea 45).

Compilación:


  1. csc /target:exe TerminacionBlocNotas.cs

Ejecución assembly:


  1. .\TerminacionBlocNotas.exe

> Prueba de ejecución:
Ejecución assembly TerminacionBlocNotas.exe
Figura 6. Ejecución assembly TerminacionBlocNotas.exe.

5. Artefactos

Conjunto de artefactos producidos en la preparación de esta receta C#:

6. Conclusiones

Hemos preparado esta receta con el propósito de entender los básicos para la terminación de un proceso o servicio. Demostramos el uso de los métodos CloseMainWindow (1) y el método Kill (2). Estos dos métodos permiten: (1) solicitud de terminación de la ventana principal de aplicación con interfaz gráfica, (2) terminar de forma forzada (abrupta) un proceso o servicio. Estos nuevos conocimientos serán parte de nuestra caja de herramienta y nos ayudarán terminar aplicaciones apropiados en contextos de construcción de software que ameritan su aplicación y uso. Antes de pasar a la serie de recetas dedicadas a Archivo, Directorio, y Entrada/Salida, escribiremos la última receta C# de Threads, Procesos, y Sincronización que consistirá en asegurar la ejecución concurrente de una única de instancia de una aplicación.

7. Glosario

  • Aplicación
  • Bloc de notas
  • Caché
  • Finalización
  • Proceso
  • Servicio
  • Terminación
  • Windows

8. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: Receta C# No. 4-15: Cómo Iniciar un Nuevo Proceso | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/09/receta-csharp-no-4-15-como-iniciar-un-nuevo-proceso.html
[3]: Process Kill - CodeProject - http://www.codeproject.com/Articles/344488/Process-Kill
[4]: Liberación de Recursos y Finalizadores en C# | OrtizOLón Software (xCSw) - http://ortizol.blogspot.com/2013/10/liberacion-de-recursos-y-finalizadores.html
[5]: Process Class (System.Diagnostics) - http://msdn.microsoft.com/en-us/library/System.Diagnostics.Process(v=vs.110).aspx
[6]: Process.CloseMainWindow Method (System.Diagnostics) - http://msdn.microsoft.com/en-us/library/system.diagnostics.process.closemainwindow(v=vs.110).aspx
[7]: Process.Refresh Method (System.Diagnostics) - http://msdn.microsoft.com/en-us/library/system.diagnostics.process.refresh(v=vs.110).aspx
[8]: Process.Kill Method (System.Diagnostics) - http://msdn.microsoft.com/en-us/library/system.diagnostics.process.kill(v=vs.110).aspx


M