Índice de la sección dedicada a .NET (en el Guille) Cómo... en .NET

Mostrar los procesos activos, iniciarlos y detenerlos
Procesos: Conceptos básicos

Código para Visual Basic.NET (VB.NET)

Código para C Sharp (C#)


Publicado el 20/Dic/2002
Actualizado 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


Código para Visual Basic.NET (VB.NET)

Aquí tienes los links para las diferentes clases (para VB .NET):

 

El código del formulario

'------------------------------------------------------------------------------
' 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

 


Código para C Sharp (C#)

Aquí tienes los links para las diferentes clases (para C#):

 

El código del formulario

//-----------------------------------------------------------------------------
// 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;
        }
    }
}

 


la Luna del Guille o... el Guille que está en la Luna... tanto monta...