Publicado el 20/Dic/2002 |
En este artículo veremos cómo podemos obtener información de los procesos que hay activos, además de poder crear (o lanzar) nuevos procesos y también cómo eliminar o finalizar un proceso activo.
NOTA IMPORTANTE:
He de advertir que no se debe jugar con algunos de los procesos que se mostrarán al ejecutar este ejemplo, ya que pueden ser procesos del sistema y si los cerramos, es posible que produzcamos algún "percance".
Por tanto, te rogaría que si vas a detener un proceso, lo hagas bien porque sabes que es eso lo que quieres hacer bien porque sea uno que has iniciado para comprobar si esto funciona...
El que avisa...Después de la recomendación, te voy a explicar un poco lo que hace la utilidad que te muestro en este artículo, así como algunas cosas que te pueden resultar interesantes en otras aplicaciones, aunque algunas de ellas ya las he mostrado con anterioridad.
En el código de ejemplo, podremos realizar las siguientes acciones con los procesos:
-Mostraremos en un ListView los procesos que hay activos, éstos se "refrescarán" cada cierto tiempo, (ese lapso de tiempo lo podremos definir nosotros), además de que pulsando en el botón Actualizar podremos refrescar dicha información.
-Podremos elegir un ejecutable o cualquier tipo de documento para poder iniciarlo.
-Y como ya te he comentado en la nota anterior, podremos detener o "eliminar" el proceso que queramos, es decir seleccionamos un proceso de la lista y si pulsamos en el botón Detener, éste proceso finalizará.Además de manejar los procesos, veremos cómo clasificar el contenido de un ListView y cómo simular líneas 3D para usarlas como separador en un formulario.
El código de las líneas 3D no lo veremos aquí ya que está publicado en el artículo de cómo comprimir/descomprimir ZIPs.
Sin embargo la clase para clasificar las columnas de un ListView si que la veremos, a pesar de estar también publicada anteriormente en esta página, ya que le he introducido algunos cambios, aunque son sólo para ir "acostumbrándome" a usar las funciones propias del .NET Framework en lugar de las "específicas" de Visual Basic .NET; en esta ocasión he sustituido StrComp por String.Compare y los CDate y CDec por la función Parse de los tipos de datos implicados en la conversión; por otro lado, estoy intentando usar funciones que tengan mejor rendimiento, y he cambiado las conversiones realizadas con CType por DirectCast, ya que esta última tiene mejor rendimiento si los objetos a "convertir" son realmente del tipo que se quiere utilizar.Diferencia entre CType y DirectCast:
Como comentario para que comprendas la diferencia entre CType y DirectCast, decirte que CType puede convertir un tipo de dato diferente del indicado, siempre y cuando esto sea posible, (por ejemplo, podemos convertir de Double a Integer), mientras que DirectCast fallaría en esa conversión, simplemente porque un Double no es del tipo Integer.
Por tanto, si piensas que es mejor usar CType, ¡adelante! así te asegurarás de que se puedan hacer más conversiones... pero si sabes que el objeto a convertir "debería" ser del mismo tipo que el objeto una vez convertido, es recomendable usar DirectCast.Respecto al uso de la clase para clasificar los elementos de un ListView, he de aclarar que, mientras estemos añadiendo elementos al ListView, debemos quitar la asignación a la propiedad ListViewItemSorter, ya que de no hacerlo, (y hemos clasificado por cualquier columna distinta de la primera), se producirá una excepción al no estar asignado el elemento cuando el ListView "intenta" clasificar el contenido de los elementos, ya que, mientras los está asignando, no existen todos los Subitems.
Una vez aclarado estas cosillas, pasemos a ver el código, tanto para Visual Basic .NET como para C#.
En este link puedes bajarte los proyectos de prueba: Procesos.zip 25.0 KB
¡A disfrutar!
Nos vemos.
Guillermo
Una captura de la ventana en tiempo de ejecución:
El programa en ejecución
Aquí tienes los links para las diferentes clases (para VB .NET):
'------------------------------------------------------------------------------ ' Procesos (17/Dic/02) ' para ver los procesos activos, iniciar y terminar procesos ' ' ©Guillermo 'guille' Som, 2002 '------------------------------------------------------------------------------ Option Strict On Public Class fProcesos Inherits System.Windows.Forms.Form #Region " Windows Form Designer generated code " '... #End Region ' Private lvColumnsSortOrder() As SortOrder Const cIntervalDef As Double = 60000 Private WithEvents temporizador As New Timers.Timer(cIntervalDef) Private openFD As New OpenFileDialog Private separador3D As cLine3DEx Private iniciando As Boolean = True ' Private Sub btnCerrar_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnCerrar.Click Me.Close() End Sub Private Sub fProcesos_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load separador3D = New cLine3DEx(linea1, linea2, Me) separador3D.IndentValue = 4 separador3D.AdjustWidth = True ' txtProceso.Text = "" txtArgumentos.Text = "" txtIntervalo.Text = cIntervalDef.ToString btnIniciar.Enabled = False btnDetener.Enabled = False ' lblInfo.Text = "©Guillermo 'guille' Som, 2002" If DateTime.Now.Year > 2002 Then lblInfo.Text &= "-" & DateTime.Now.Year.ToString End If ' ReDim lvColumnsSortOrder(lvProcesos.Columns.Count - 1) Dim i As Integer For i = 0 To lvProcesos.Columns.Count - 1 lvColumnsSortOrder(i) = SortOrder.None Next ' ' posicionarla arriba en el centro Me.Top = -4 Me.Left = (Screen.PrimaryScreen.WorkingArea.Width - Me.Width) \ 2 ' ' asignar el evento para el temporizador ' esta es otra forma de hacerlo, ' además de usanr el Handles en el procedimiento AddHandler temporizador.Elapsed, AddressOf temporizador_Elapsed temporizador.Interval = 1000 temporizador.Enabled = True ' iniciando = False End Sub Private Sub fProcesos_Resize(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Resize If iniciando Then Exit Sub If Me.WindowState <> FormWindowState.Minimized Then separador3D.Resize() End If End Sub Private Sub btnExaminar_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnExaminar.Click With openFD .Title = "Selecciona el ejecutable" .FileName = txtProceso.Text .Filter = "Ejecutables (*.exe)|*.exe|Todos los ficheros (*.*)|*.*" If .ShowDialog = DialogResult.OK Then txtProceso.Text = .FileName End If End With End Sub Private Sub btnIniciar_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnIniciar.Click ' iniciar el proceso indicado en la caja de textos ' Try Dim proceso As New Process proceso.StartInfo.FileName = txtProceso.Text If txtArgumentos.Text <> "" Then proceso.StartInfo.Arguments = txtArgumentos.Text End If proceso.Start() Catch ex As Exception lblInfo.Text = " Error al iniciar el proceso: " & ex.Message MessageBox.Show("Error al inciar el proceso:" & vbCrLf & ex.Message) End Try End Sub Private Sub btnDetener_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnDetener.Click ' detener el argumento seleccionado de la lista Dim prog As String ' Try prog = lvProcesos.SelectedItems(0).Text Catch 'ex As Exception Exit Sub End Try ' Try Dim procesos() As Process = Process.GetProcessesByName(prog) procesos(0).CloseMainWindow() If procesos(0).HasExited = False Then procesos(0).Kill() procesos(0).Close() End If ' btnActualizar_Click(btnActualizar, e) 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 End Sub Private Sub btnActualizar_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles btnActualizar.Click ' 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 Dim procesos() As Process = Process.GetProcesses ' Dim proceso As Process For Each proceso In procesos Try With lvProcesos.Items.Add(proceso.ProcessName) .SubItems.Add(proceso.MainWindowTitle) .SubItems.Add(proceso.StartTime.ToString("dd/MM HH:mm:ss")) Try .SubItems.Add(proceso.PriorityClass.ToString) Catch .SubItems.Add(" <error>") End Try End With Catch ex As Exception If MessageBox.Show(ex.Message, "Error al actualizar los procesos", MessageBoxButtons.OKCancel) = DialogResult.Cancel Then Exit Sub End If End Try Next ' Me.Cursor = Cursors.Default ' temporizador.Enabled = True End Sub Private Sub lvProcesos_SelectedIndexChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles lvProcesos.SelectedIndexChanged If iniciando Then Exit Sub btnDetener.Enabled = (lvProcesos.SelectedIndices.Count > 0) End Sub Private Sub txtProceso_TextChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles txtProceso.TextChanged If iniciando Then Exit Sub Try If IO.File.Exists(txtProceso.Text) Then btnIniciar.Enabled = True End If Catch ex As Exception btnIniciar.Enabled = False End Try End Sub Private Sub temporizador_Elapsed(ByVal sender As Object, _ ByVal e As System.Timers.ElapsedEventArgs) _ 'Handles temporizador.Elapsed Static primeravez As Boolean = True ' temporizador.Enabled = False btnActualizar_Click(btnActualizar, New EventArgs) ' si es la primera vez, cambiar el valor del intervalo If primeravez Then primeravez = False temporizador.Interval = cIntervalDef End If temporizador.Enabled = True End Sub Private Sub txtIntervalo_TextChanged(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles txtIntervalo.TextChanged txtIntervalo_Leave(sender, New EventArgs) End Sub Private Sub txtIntervalo_Leave(ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles txtIntervalo.Leave Dim intervalo As Double ' If iniciando Then Exit Sub ' temporizador.Enabled = False Try intervalo = Double.Parse(txtIntervalo.Text) Catch 'ex As Exception intervalo = 0 End Try temporizador.Interval = intervalo If intervalo > 100 Then temporizador.Enabled = True End If End Sub Private Sub lvProcesos_ColumnClick(ByVal sender As Object, _ ByVal e As System.Windows.Forms.ColumnClickEventArgs) _ Handles lvProcesos.ColumnClick ' clasificar según la columna pulsada ' ' detener el temporizador Dim b As Boolean = temporizador.Enabled temporizador.Enabled = False ' ' Crear una instancia de la clase que realizará la comparación ' indicando la columna en la que se ha pulsado Dim oCompare As New ListViewColumnSort(e.Column) ' ' Asignar el orden de clasificación ' según el estado de clasificación de esa columna If lvColumnsSortOrder(e.Column) = SortOrder.Ascending Then oCompare.Sorting = SortOrder.Descending Else oCompare.Sorting = SortOrder.Ascending End If lvColumnsSortOrder(e.Column) = oCompare.Sorting ' ' El tipo de datos de la columna en la que se ha pulsado Select Case e.Column Case 0, 1, 3 oCompare.CompararPor = ListViewColumnSort.TipoCompare.Cadena Case 2 oCompare.CompararPor = ListViewColumnSort.TipoCompare.Fecha End Select ' Asignar la clase que implementa IComparer ' y que se usará para realizar la comparación de cada elemento lvProcesos.ListViewItemSorter = oCompare ' ' poner el temporizador como estaba temporizador.Enabled = b End Sub End Class
El código de la clase para clasificar los elementos de un ListView
'------------------------------------------------------------------------------ ' Clase para clasificar las columnas de un ListView (23/Jun/02) ' ' ©Guillermo 'guille' Som, 2002 '------------------------------------------------------------------------------ Option Strict On Public Class ListViewColumnSort Implements IComparer ' Public Enum TipoCompare Cadena Numero Fecha End Enum Public CompararPor As TipoCompare Public ColumnIndex As Integer = 0 Public Sorting As SortOrder = SortOrder.Ascending ' ' Constructores Sub New() ' no es necesario indicar nada, ' ya que implíctamente se llama a MyBase.New End Sub Sub New(ByVal columna As Integer) ColumnIndex = columna End Sub ' Public Overridable Function Compare(ByVal a As Object, _ ByVal b As Object) As Integer _ Implements IComparer.Compare ' ' Esta función devolverá: ' -1 si el primer elemento es menor que el segundo ' 0 si los dos son iguales ' 1 si el primero es mayor que el segundo ' Dim menor As Integer = -1, mayor As Integer = 1 Dim s1, s2 As String ' If Sorting = SortOrder.None Then Return 0 End If ' ' Convertir el texto en el formato adecuado ' y tomar el texto de la columna en la que se ha pulsado s1 = DirectCast(a, ListViewItem).SubItems(ColumnIndex).Text s2 = DirectCast(b, ListViewItem).SubItems(ColumnIndex).Text ' ' Asignar cuando es menor o mayor, ' dependiendo del orden de clasificación If Sorting = SortOrder.Descending Then menor = 1 mayor = -1 End If ' Select Case CompararPor Case TipoCompare.Fecha ' Si da error, se comparan como cadenas Try Dim f1 As Date = DateTime.Parse(s1) Dim f2 As Date = DateTime.Parse(s2) If f1 < f2 Then Return menor ElseIf f1 = f2 Then Return 0 Else Return mayor End If Catch Return s1.CompareTo(s2) * mayor Return System.String.Compare(s1, s2, True) * mayor End Try Case TipoCompare.Numero ' Si da error, se comparan como cadenas Try Dim n1 As Decimal = Decimal.Parse(s1) Dim n2 As Decimal = Decimal.Parse(s2) If n1 < n2 Then Return menor ElseIf n1 = n2 Then Return 0 Else Return mayor End If Catch Return System.String.Compare(s1, s2, True) * mayor End Try Case Else 'Case TipoCompare.Cadena Return System.String.Compare(s1, s2, True) * mayor End Select End Function End Class
Aquí tienes los links para las diferentes clases (para C#):
//----------------------------------------------------------------------------- // Procesos (17/Dic/02) // para ver los procesos activos, iniciar y terminar procesos // // ©Guillermo 'guille' Som, 2002 //----------------------------------------------------------------------------- using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Diagnostics; using System.IO; using System.Timers; ////// Summary description for fProcesosCS. /// /// public class fProcesosCS : System.Windows.Forms.Form { // //... // bool primeravez = true; // private SortOrder[] lvColumnsSortOrder; const double cIntervalDef = 60000; private System.Timers.Timer temporizador = new System.Timers.Timer(cIntervalDef); private OpenFileDialog openFD = new OpenFileDialog(); private cLine3DEx separador3D; private bool iniciando = true; // private void btnCerrar_Click(object sender, System.EventArgs e) { this.Close(); } private void fProcesos_Load(object sender, System.EventArgs e) { separador3D = new cLine3DEx(linea1, linea2, this); separador3D.IndentValue = 4; separador3D.AdjustWidth = true; // txtProceso.Text = ""; txtArgumentos.Text = ""; txtIntervalo.Text = cIntervalDef.ToString(); btnIniciar.Enabled = false; btnDetener.Enabled = false; // lblInfo.Text = "©Guillermo 'guille' Som, 2002"; if( DateTime.Now.Year > 2002 ) { lblInfo.Text += "-" + DateTime.Now.Year.ToString(); } // lvColumnsSortOrder = new SortOrder[lvProcesos.Columns.Count]; for(int i = 0; i <= lvProcesos.Columns.Count - 1; i++) { lvColumnsSortOrder[i] = SortOrder.None; } // // asignar el evento para el temporizador this.temporizador.Elapsed += new System.Timers.ElapsedEventHandler(this.temporizador_Elapsed); temporizador.Interval = 1000; temporizador.Enabled = true; // // posicionarla arriba en el centro this.Top = -4; this.Left = (Screen.PrimaryScreen.WorkingArea.Width - this.Width) / 2; // iniciando = false; } private void fProcesos_Resize(object sender, System.EventArgs e) { if( iniciando ) return; if( this.WindowState != FormWindowState.Minimized ) { separador3D.Resize(); } } private void btnExaminar_Click(object sender, System.EventArgs e) { openFD.Title = "Selecciona el ejecutable"; openFD.FileName = txtProceso.Text; openFD.Filter = "Ejecutables (*.exe)|*.exe|Todos los ficheros (*.*)|*.*"; if( openFD.ShowDialog() == DialogResult.OK ) { txtProceso.Text = openFD.FileName; } } private void btnIniciar_Click(object sender, System.EventArgs e) { // iniciar el proceso indicado en la caja de textos // Process proceso = new Process(); try { proceso.StartInfo.FileName = txtProceso.Text; if( txtArgumentos.Text != "" ) { proceso.StartInfo.Arguments = txtArgumentos.Text; } proceso.Start(); } catch(Exception ex ) { lblInfo.Text = " Error al iniciar el proceso: " + ex.Message; MessageBox.Show("Error al inciar el proceso:" + Console.Out.NewLine + ex.Message); } } private void btnDetener_Click(object sender, System.EventArgs e) { // detener el argumento seleccionado de la lista String prog; // try { prog = lvProcesos.SelectedItems[0].Text; } catch { return; } // try { Process[] procesos = Process.GetProcessesByName(prog); procesos[0].CloseMainWindow(); if( procesos[0].HasExited == false ) { procesos[0].Kill(); procesos[0].Close(); } // btnActualizar_Click(btnActualizar, e); } catch(Exception ex ) { lblInfo.Text = " Error al detener el proceso: " + ex.Message; MessageBox.Show("Error al detener el proceso:/r" + ex.Message); } } private void btnActualizar_Click(object sender, System.EventArgs e) { // temporizador.Enabled = false; // this.Cursor = Cursors.WaitCursor; this.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 = null; // // actualizar la lista con los procesos activos Process[] procesos = Process.GetProcesses(); // foreach(Process proceso in procesos) { try { ListViewItem lvi = lvProcesos.Items.Add(proceso.ProcessName); lvi.SubItems.Add(proceso.MainWindowTitle); lvi.SubItems.Add(proceso.StartTime.ToString("dd/MM HH:mm:ss")); try { lvi.SubItems.Add(proceso.PriorityClass.ToString()); } catch //(Exception ex) { lvi.SubItems.Add(" <error>"); } } catch(Exception ex) { if (MessageBox.Show(ex.Message,"Error al actualizar los procesos", MessageBoxButtons.OKCancel) == DialogResult.Cancel) { return;; } } } // this.Cursor = Cursors.Default; // temporizador.Enabled = true; } private void lvProcesos_SelectedIndexChanged(object sender, System.EventArgs e) { if( iniciando ) return; btnDetener.Enabled = (lvProcesos.SelectedIndices.Count > 0); } private void txtProceso_TextChanged(object sender, System.EventArgs e) { if( iniciando ) return; try { if( System.IO.File.Exists(txtProceso.Text) ) { btnIniciar.Enabled = true; } } catch //(Exception ex ) { btnIniciar.Enabled = false; } } private void temporizador_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { // temporizador.Enabled = false; btnActualizar_Click(btnActualizar, new EventArgs()); // si es la primera vez, cambiar el valor del intervalo if( primeravez ) { primeravez = false; temporizador.Interval = cIntervalDef; } temporizador.Enabled = true; } private void txtIntervalo_TextChanged(object sender, System.EventArgs e) { txtIntervalo_Leave(sender, new EventArgs()); } private void txtIntervalo_Leave(object sender, System.EventArgs e) { double intervalo; // if( iniciando ) return; // temporizador.Enabled = false; try { intervalo = Double.Parse(txtIntervalo.Text); } catch { //ex As Exception intervalo = 0; } temporizador.Interval = intervalo; if( intervalo > 100 ) { temporizador.Enabled = true; } } private void lvProcesos_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e) { // { // Handles lvProcesos.ColumnClick{ // clasificar según la columna pulsada // // detener el temporizador bool b = temporizador.Enabled; temporizador.Enabled = false; // // Crear una instancia de la clase que realizará la comparación // indicando la columna en la que se ha pulsado ListViewColumnSort oCompare = new ListViewColumnSort(e.Column); // // Asignar el orden de clasificación // según el estado de clasificación de esa columna if( lvColumnsSortOrder[e.Column] == SortOrder.Ascending ) { oCompare.Sorting = SortOrder.Descending; } else { oCompare.Sorting = SortOrder.Ascending; } lvColumnsSortOrder[e.Column] = oCompare.Sorting; // // El tipo de datos de la columna en la que se ha pulsado switch(e.Column) { case 0: case 1: case 3: oCompare.CompararPor = ListViewColumnSort.TipoCompare.Cadena; break; case 2: oCompare.CompararPor = ListViewColumnSort.TipoCompare.Fecha; break; } // Asignar la clase que implementa IComparer // y que se usará para realizar la comparación de cada elemento lvProcesos.ListViewItemSorter = oCompare; // // poner el temporizador como estaba temporizador.Enabled = b; } }
El código de la clase para clasificar los elementos de un ListView
//----------------------------------------------------------------------------- // Clase para clasificar las columnas de un ListView (23/Jun/02) // // ©Guillermo 'guille' Som, 2002 //----------------------------------------------------------------------------- using System; using System.Collections; using System.Windows.Forms; ////// Clase para clasificar las columnas de un ListView /// public class ListViewColumnSort : IComparer { public enum TipoCompare { Cadena, Numero, Fecha } public TipoCompare CompararPor; public int ColumnIndex = 0; public SortOrder Sorting = SortOrder.Ascending; public ListViewColumnSort() { // // TODO: agregar aquí la lógica del constructor // } public ListViewColumnSort(int columna) { ColumnIndex = columna; } public int Compare(Object a, Object b) { /* ' Esta función devolverá: ' -1 si el primer elemento es menor que el segundo ' 0 si los dos son iguales ' 1 si el primero es mayor que el segundo */ int menor = -1, mayor = 1; String s1, s2; // if (Sorting == SortOrder.None) return 0; /* ' Convertir el texto en el formato adecuado ' y tomar el texto de la columna en la que se ha pulsado */ s1 = ((ListViewItem)a).SubItems[ColumnIndex].Text; s2 = ((ListViewItem)b).SubItems[ColumnIndex].Text; // // Asignar cuando es menor o mayor, dependiendo del orden de clasificación if (Sorting == SortOrder.Descending) { menor = 1; mayor = -1; } // switch(CompararPor) { case TipoCompare.Fecha: try { DateTime f1, f2; f1 = DateTime.Parse(s1); f2 = DateTime.Parse(s2); // if( f1 < f2 ) return menor; else if( f1 == f2 ) return 0; else return mayor; } catch { return System.String.Compare(s1, s2, true) * mayor; } //break; case TipoCompare.Numero: try { decimal n1, n2; n1 = decimal.Parse(s1); n2 = decimal.Parse(s2); if( n1 < n2 ) return menor; else if( n1 == n2 ) return 0; else return mayor; } catch { return System.String.Compare(s1, s2, true) * mayor; } //break; default: //case TipoCompare.Cadena: return System.String.Compare(s1, s2, true) * mayor; //break; } } }