jueves, 21 de enero de 2010

Process: Obtener información de un proceso concreto

Otra información que podemos obtener es la de un proceso en concreto, por ejemplo, para saber si ya está en ejecución, esto lo podemos hacer mediante el método GetProcessesByName, como podemos suponer, este método devuelve todos los procesos que coincidan con el nombre que le indiquemos como argumento. Este método devuelve un array del tipo Process, por tanto, si dicho array no tiene ningún elemento es que el proceso que buscamos no está en ejecución, de la misma forma, si de ese proceso existen varias copias en ejecución, la propiedad Length del array nos dirá cuantos.
Por ejemplo, con el siguiente código comprobaremos cuantas copias de Word se están ejecutando:

' Obtener un proceso por nombre
Dim ps() As Process = Process.GetProcessesByName("WINWORD")
Console.WriteLine("Hay {0} instancias de Word", ps.Length)


Como hemos comentado la clase Process nos ofrece cierta información extra del proceso, por ejemplo desde cuando está en ejecución, esto lo podemos averiguar mediante la propiedad StartTime que devolverá la fecha y la hora de inicio.
Si lo que nos interesa es saber cuanto tiempo de procesador ha consumido el proceso, lo podemos averiguar
mediante la propiedad TotalProcessorTime.
Si el proceso tiene una ventana, podemos averiguar el texto mostrado en la barra de título mediante la propiedad MainWindowTitle.

Son muchas más las propiedades que podemos usar para averiguar lo que "rodea" a cada proceso, por ejemplo la memoria que tiene asignada, tanto la física como la de paginación, si el proceso está bloqueado la propiedad Responding devolverá un valor verdadero.

Otra información que nos puede ser de mucha utilidad es averiguar el path del ejecutable, esta información (cuando esté disponible) la podemos averiguar mediante la propiedad FileName de la propiedad MainModule del proceso:

Console.WriteLine("El path: {0}",
ps(0).MainModule.FileName)


Muchas de estas propiedades no están disponibles en todos los procesos que están en ejecución, por tanto en ocasiones será conveniente utilizar una captura de errores.
Y eso es lo que hacemos en la aplicación de ejemplo que acompaña a este artículo, bueno, eso y algunas cosas más, como mostrar la información de los procesos activos, permitir detener un proceso, iniciar nuevos procesos, etc.

La ventana de la aplicación en ejecución es la que podemos ver en la figura 2.

Esta aplicación nos servirá para ver todos los procesos que están en ejecución y se irá actualizando cada cierto tiempo (el indicado por nosotros) o bien la podemos actualizar (para refrescar la información) al pulsar en el botón Actualizar.
En esa aplicación además podemos clasificar el contenido del ListView usando la clase ListViewColumnSort que ya comentamos en otro artículo aquí en Netveloper.

Como comentaba, debemos ser precavidos a la hora de obtener la información "extra" de los procesos, ya que no todos los procesos nos proporcionan toda la información a la que queremos acceder, por eso, el método que se encarga de llenar el ListView con los procesos que están en ejecución hacemos ciertas comprobaciones de error.
Este es el código de ese método:

temporizador.Enabled = False
Me.Cursor =  Cursors.WaitCursor
Me.Refresh()
lvProcesos.Items.Clear()
lvProcesos.Sorting = SortOrder.None
' quitar la clase que clasificará los elementos,
' si no lo hacemos así, se producirá un error
' ya que intentará clasificar incluso cuando no estén asignados
' todos los subitems
lvProcesos.ListViewItemSorter = Nothing
' actualizar la lista con los procesos activos
' usar el nombre del equipo indicado
Dim procesos() As Process = Process.GetProcesses(nombreEquipo)
Dim proceso As
Process
For Each proceso In procesos
 With
lvProcesos.Items.Add(proceso.ProcessName)
  Try
   .SubItems.Add(proceso.MainWindowTitle)
  Catch
ex As
NotSupportedException
   .SubItems.Add("")
  Catch 'ex As
Exception
   .SubItems.Add(" ")
  End
Try
  Try
   .SubItems.Add(proceso.StartTime.ToString("dd/MM/yy HH:mm:ss"))
  Catch ex As
NotSupportedException
   .SubItems.Add("")
  Catch
   .SubItems.Add(" ")
  End Try
  ' El path del ejecutable
  Try
   .SubItems.Add(proceso.MainModule.FileName)
  Catch
ex As
NotSupportedException
   .SubItems.Add("")
  Catch
   .SubItems.Add(" ")
  End
Try
  Try
   .SubItems.Add(proceso.PriorityClass.ToString)
  Catch
ex As
NotSupportedException
   .SubItems.Add("")
  Catch
   .SubItems.Add(" ")
  End Try
 End
With
Next
Me.Cursor = Cursors.Default
temporizador.Enabled = True



Como podemos apreciar, algunos de esos errores son del tipo NoSupportedException, es decir, "no soportado", son casos muy concretos, pero si vamos a obtener la información de todos los procesos, es conveniente que nos aseguremos de que no se produzca ni ese ni cualquier otro error, por eso, al obtener algunas de las propiedades o bien usamos errores genéricos o bien indicamos las dos capturas de error.

A la hora de detener un proceso también debemos tomar nuestras precauciones, ya que es posible que también se produzca una excepción.
En el código del método Detener es eso lo que hacemos, incluir dentro de un Try/Catch el proceso de detener la aplicación que seleccionemos del ListView.
Además de esa captura de errores, vemos cómo podemos obtener un objeto del tipo Process del proceso en cuestión, mediante el método GetProcessesByName.
Para detenerlo utilizamos el método CloseMainWindow, y a continuación comprobamos si aún sigue en funcionamiento, en caso de que sea así, (algunos procesos no finalizan en el momento), nos aseguramos de que se finalizará mediante otra llamada al método Kill.

' Detener el elemento seleccionado de la lista
Dim prog As
String
Try
prog = lvProcesos.SelectedItems(0).Text
Catch 'ex As
Exception
btnDetener.Enabled = False
Exit Sub
End
Try
Try
Dim procesos() As Process = Process.GetProcessesByName(prog)
' pedir confirmación de lo que se va a hacer...
Dim s As String = String.Format("¿Quieres detener el proceso '{0}' iniciado el:{1}?", prog, procesos(0).StartTime.ToString("dd/MM/yyyy HH:mm:ss"))
If MessageBox.Show(s, "Detener un proceso", MessageBoxButtons.YesNo, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2) = DialogResult.Yes
Then
procesos(0).CloseMainWindow()
If
procesos(0).HasExited = False
Then
procesos(0).Kill()
procesos(0).Close()
End
If
btnActualizar_Click(btnActualizar, e)
End
If
Catch ex As Exception
lblInfo.Text = " Error al
detener el proceso: " &
ex.Message
MessageBox.Show("Error al detener el proceso:" & vbCrLf & ex.Message)
End Try


El único problema lo encontraremos si existen varios procesos en ejecución con el mismo nombre, ya que es posible que cerremos el que no queríamos, pero como vemos en el código, podemos mostrar información extra sobre ese proceso con idea de que sea el usuario el que tome la última decisión.

Conclusión

Confío que todo lo dicho sobre la clase Process sea de utilidad, no ya solo para iniciar nuevas aplicaciones o documentos, que seguramente será la forma más habitual de usarla, sino también para obtener información de cualquier proceso en concreto o bien de todos los procesos que estén en funcionamiento en nuestro equipo, (e incluso en otros equipos).

No hay comentarios:

Publicar un comentario