el Guille, la Web del Visual Basic, C#, .NET y más...
Ir a la sección de Visual Studio 2005 Utilidades .NET 2.0

Buscar ficheros

 
Publicado el 29/Ago/2007
Actualizado el 01/Sep/2007
Autor: Guillermo 'guille' Som

Existe una versión más reciente de esta utilidad.


Una pequeña utilidad para buscar ficheros en los directorios (y subdirectorios) indicados, mostrando los ficheros en un ListView y pudiendo abrir el directorio que contiene el fichero hallado.



 

La batallita del agüelo:

El portátil que tengo ahora (un Sony Vaio) no se porqué, pero no "activa" el salvapantallas de Windows Vista ni se desconecta la pantalla pasado un tiempo. Y como no me gusta que la pantalla se quede con la imagen fija todo el tiempo, (el portátil no lo apago nuca), pues pensé que si el salvapantallas no se "dispara" solo, lo mejor sería tener un acceso directo en el escritorio a un salvapantallas y poder ponerlo en marcha cuando me apeteciera.

Estuve mirando en las propiedades de Windows para ver los salvapantallas que había disponibles y así poder elegir uno de ellos. Me gustó el de la aurora boreal esa... así que... me dije, bueno, vamos a buscar ese ficherillo en el disco C. Y como yo ya sabía que los salvapantallas tienen la extensión .scr abrí el "Search" de Windows en el disco C, escribí el típico "*.scr" en la casilla de búsqueda y esperé a ver si me lo encontraba... seguí esperando... y después de mostrarme... ¡nada! me harté... Bueno, respiré un poco, me calmé (es que lo de buscar en Windows Vista siempre me altera la bilirrubina o lo que me tenga que alterar), y me dije, busquemos por el nombre, así que, escribí Aurora que es el nombre del salvapantallas ese. No sé cuantas Auroras tengo en la libreta de direcciones de Windows Mail (la versión para Windows Vista de mi querido Outlook Express), pero me salieron unas cuantas... y es que con tantos años que llevo guardando las direcciones de los correos que recibo o respondo, pues... es lógico que salieran algunas...

La cuestión es que salían nombres, pero ningún fichero de salvapantallas... Hasta llegué a pensar que esta gente de Microsoft al hacer lo de Windows Vista pondrían de otra forma el tema este de los salvapantallas para que no se colara ningún intruso por ese medio... es que como los salvapantallas se pueden hacer hasta con Visual Basic, pues... cualquiera puede hacer algo "raro" con ese tipo de programa...

Me puse a buscar directamente en el directorio de Windows, pero allí solo estaba el salvapantallas de BOINC, aunque la verdad es que lo probé y no funcionaba, así que... podía ser que mis sospechas tuvieran algo de validez y los salvapantallas ahora estuvieran "escondidos".

Pero como tampoco me parecía algo "lógico", eso de que escondieran tanto los salvapantallas me parecía algo exagerado, pues pensé que por "lógica deducción" y conociendo los antecedentes que tiene el sistema de búsqueda de Windows Vista, (que sí, que está muy bien para buscar en los mensajes y todo eso, pero es una patata para buscar ficheros), me decidí a hacerme un programilla que buscara en el disco (o directorio) que yo le dijera y buscando por lo que yo quisiera, en este caso, una extensión, pero también vale para cualquier cosa que forme parte del nombre de un fichero.

Y así es como ha surgido la versión de Visual Basic 2005 de esta utilidad. Porque, aunque publico las cosas en Visual Basic y en C#, siempre empiezo los nuevos proyectos con Visual Basic 2005 que es más fácil de programar, al menos para mí.

 

La utilidad de buscar

El objetivo de esta utilidad lo tenía claro:
-Indicar una "especificación" es decir, algo que esté en el nombre de un fichero, como puede ser la extensión.
-Indicar en que directorio debería buscar.
-Indicar si se debían examinar los directorios que tuviera ese directorio (subdirectorios).

El resultado de la búsqueda lo mostraría en un control ListView en vista detalles con idea de mostrar el nombre del fichero y el directorio, de forma que al hacer doble clic en el elemento que quisiera se abriera en el explorer ese directorio, de esa forma podría acceder fácilmente al fichero en cuestión y poder crear el acceso directo.

Buscar algo en el disco con las clases de .NET es algo muy sencillo. Lo que yo necesitaba era recuperar la lista de ficheros que cumplieran la condición que pusiera y mostrar esos ficheros en el ListView.

Básicamente lo que hay que hacer es esto:

Dim di As New DirectoryInfo(Directorio)
Dim fics() As FileInfo
fics = di.GetFiles(Filtro, SearchOption.AllDirectories)

Es decir, crear un objeto del tipo DirectoryInfo con el nombre del directorio a examinar, llamar al método GetFiles al que le indico el filtro o especificación de lo que quiero buscar, por ejemplo *.scr, le digo si quiero tener en cuenta todos los directorios (como es el caso del código anterior) y eso me devuelve un array del tipo FileInfo que es lo que usaré para asignar los valores en el ListView.

El problema empezaba en que Windows Vista es muy "proteccionista" con el disco C, (que es donde estaba buscando por los salvapantallas), y hay muchos directorios que dan error cuando se quiere acceder a ellos y no se está haciendo como "administrador" con todos los privilegios.

El segundo problema es que si se iban encontrando cosas, lo lógico sería que se fuesen mostrando en la lista eso que se fuese hallando, porque entre otras cosas, parece como menos aburrido tener que estar esperando sin ver nada...

Así que... pensé en el dicho ese de "divide y vencerás", que no se porqué lo pensé ya que en realidad no tenía que dividir nada... bueno, sí, un poco el trabajo de buscar en los directorios.

Además de que si "dividía" el trabajo, en este caso, buscando de forma independiente en cada directorio, cada vez que se produjera una excepción de "falta" de permisos, podría dejar de examinar ese directorio y pasar al siguiente.

Al principio también iba mostrando un mensaje de error cada vez que se producía ese fallo de acceso, así que... pensé (sí, recuerda que algunas veces también pienso), que lo mejor sería poner una opción en el formulario de búsqueda por si quería que me avisara de cada fallo o simplemente lo ignorara y siguiera con lo que estaba haciendo.

Y con todo esto y un buen "batido", salió esta utilidad a la que he llamado gsBuscar... ¿te imaginas porqué? Pues eso.

Este es el formulario en modo de diseño:

Figura 1. El formulario en modo diseño
Figura 1. El formulario en modo diseño

He creado valores de configuración para almacenar los datos habituales: posición y tamaño de la ventana, el filtro de búsqueda, el directorio en el que se hizo la última búsqueda, si se deben incluir los subdirectorios y si se deben ignorar los avisos de error.

La parte de inicio del formulario lo único que hace es asignar esos valores a los controles correspondientes y el botón Examinar lo que hace es usar el componente FolderBrowserDialog para seleccionar el directorio en el que se empezará la búsqueda.

El "grueso" del trabajo está en el código del botón Buscar y en un método que se encarga de ir buscando directorio por directorio, de forma que si se produce un error, simplemente se pasa al siguiente (en realidad se sale del método y el bucle principal se encarga de comprobar si debe seguir buscando o no).

Lo que acabo de comentar de que el "bucle principal se encarga de comprobar si debe seguir buscando o no" es porque el botón de Buscar también sirve para cancelar la búsqueda, ya que no es plan de que no queramos que se siga buscando y tengamos que esperar una eternidad a que termine.

Empecemos viendo el código del botón Buscar.

El inicio de la búsqueda y comprobación si se debe cancelar

Veamos primero el código y después te explico lo que hago.

Private Sub btnBuscar_Click(ByVal sender As Object, _
                            ByVal e As EventArgs) _
                            Handles btnBuscar.Click

    ' Buscar de forma recursiva (si es necesario)
    Static yaEstoy As Boolean

    If yaEstoy Then
        cancelar = True
        Me.btnBuscar.Text = "Cancelando..."
        Application.DoEvents()
        Exit Sub
    End If
    yaEstoy = True

    With My.Settings
        .Location = Me.Location
        .Size = Me.Size
        .Directorio = Me.txtDir.Text
        .Filtro = Me.txtFiltro.Text
        .conSubDir = Me.chkConSubDir.Checked
        .IgnorarError = Me.chkIgnorarError.Checked
        .Save()

        Dim di As New DirectoryInfo(.Directorio)

        Me.lvFics.Items.Clear()

        Me.LabelInfo.Text = "Buscando los ficheros..."
        Me.Cursor = Cursors.AppStarting
        Me.btnBuscar.Text = "Buscando..."
        Me.Refresh()

        recorrerDir(di)

    End With
    Me.Cursor = Cursors.Default
    Me.LabelInfo.Text = "Se han hallado " & Me.lvFics.Items.Count & " ficheros"
    Me.btnBuscar.Text = "Buscar"
    Me.Refresh()

    cancelar = False

    yaEstoy = False
End Sub

Lo de la variable declarada Static (que en C# no se puede hacer, pero se puede simular) es para que esa variable mantenga el valor entre distintas llamadas a este método. Inicialmente tendrá un valor False, pero en cuanto haya empezado la búsqueda tendrá un valor verdadero que es lo que se comprueba en el IF: si "ya estoy" es que se quiere cancelar, así que, asigno un valor verdadero a la variable cancelar que al estar definida a nivel de la clase será visible en todos los métodos y se podrá usar como "flag" para saber si se debe dejar de buscar. Cambio el texto del botón para que se sepa que nos hemos enterado de que se quiere cancelar y llamo al "fatídico" DoEvents para que se sigan procesando los mensajes de Windows y así de camino se refresca el formulario y la ventana principal no se queda "congelada".

Si no estaba ya en este método es que acabo de entrar, así que, asigno a las variables de configuración los valores de los controles y lo guardo, no sea que me de por querer "cancelar" el proceso, y en ese caso no se guardarían los datos.

Una vez hecho esto es cuando empieza el espectáculo.

Creo un objeto del tipo DirectoryInfo con el nombre del directorio por el que se empezará la búsqueda.
Limpio el contenido de lo que ya tuviera el ListView.
Cambio los textos del botón y muestro un aviso de que ya estoy empezando a buscar.

Lo siguiente es llamar al método recorrerDir al que le paso como argumento el valor del directorio por el que empezaré a buscar. Como veremos ahora, en ese método es donde en realidad se hace todo el trabajo.

Cuando termine de procesarse lo que haya en ese método querrá decir que la búsqueda ha finalizado, por tanto se muestra la información correspondiente y se vuelven a asignar los valores "adecuados" tanto al botón como a las variables que controlan el funcionamiento de todo esto.

Ahora veamos el código del método recorrerDir.

El método que hace todo el trabajo (o casi)

Te digo lo mismo que antes, veamos el código y después te explico lo que hace ese código.

Private Sub recorrerDir(ByVal di As DirectoryInfo)
    ' Recorrer los ficheros de este directorio
    ' añadir al listview si se encuentra alguno
    Dim fics() As FileInfo
    Dim dirs() As DirectoryInfo

    Application.DoEvents()
    If cancelar Then Exit Sub

    Me.LabelInfo.Text = di.Name & "..."
    Me.LabelInfo.Refresh()

    With My.Settings
        Try
            fics = di.GetFiles(.Filtro, SearchOption.TopDirectoryOnly)
            With Me.lvFics
                For Each fi As FileInfo In fics
                    Dim lvi As ListViewItem = .Items.Add(fi.Name)
                    lvi.SubItems.Add(fi.DirectoryName)
                Next
                .Refresh()
            End With

            If .conSubDir Then
                dirs = di.GetDirectories()
                For Each dir As DirectoryInfo In dirs
                    recorrerDir(dir)
                Next
            End If

        Catch ex As Exception
            If .IgnorarError Then
                Exit Sub
            End If

            If MessageBox.Show("Error: " & ex.Message & vbCrLf & _
                               "¿Quieres cancelar o continuar?", _
                               "Buscar en directorios", _
                               MessageBoxButtons.OKCancel, _
                               MessageBoxIcon.Exclamation _
                               ) = DialogResult.Cancel Then
                cancelar = True
                Application.DoEvents()
            End If
        End Try
    End With
End Sub

Lo primero que hago en este método, (aparte de declarar las variables que se van a usar), es comprobar si se ha cancelado.
La llamada a DoEvents es importante por dos razones, pero la más importante es que hará que nuestra ventana no se quede congelada y así de camino se evita el mensajillo ese de "not responding" que el Windows suele añadirle al título de la ventana cuando la aplicación se cuelga... que en realidad no es que se cuelgue, sino que deja de responder a los mensajes de Windows y el propio Windows se cree que ya está "liquidada".
La otra razón es que así el valor de "cancelar" se evalúa de inmediato y en caso de que sea verdadero, pues... salimos del método.

Después asigno el nombre del directorio que se está examinando a la etiqueta de "información" y la refresco para que el texto se vea, si no la refrescara, no se vería hasta que se llamara nuevamente a DoEvents.

Lo siguiente es asignar al array fics los ficheros que cumplan la especificación de búsqueda que hemos indicado. Fíjate que en el segundo parámetro del método GetFiles uso el valor TopDirectoryOnly que lo que le indica al método es que no devuelva los ficheros de los subdirectorios que tenga este directorio. Esto es así porque si se deben comprobar los subdirectorios se hace más abajo.

Si se han encontrado ficheros en este directorio, se añaden al ListView, asignando el nombre a la primera columna y el directorio a la segunda.

Después compruebo si se deben examinar los subdirectorios, de ser así, se asigna a la variable dirs los subdirectorios de este directorio, algo que se obtiene con el método GetDirectories de la clase DirectoryInfo.

Y llamo de forma recursiva a este mismo método pasándole el valor de cada uno de esos subdirectorios.

Ese pequeño detalle es el que hace que esta utilidad sea más útil, ya que así se irá examinando cada directorio  por separado y se irán mostrando los ficheros que se vayan encontrando.

En caso de que se produzca un error al acceder al método GetFiles o GetDirectories, se ejecutará el código que hay en el bloque Catch, en ese bloque se comprueba si se debe ignorar el error, en ese caso, simplemente salimos del método y si se debe mostrar el mensaje de error se hace de forma que el usuario tenga la oportunidad de cancelar. Este hecho se comprueba viendo si el valor devuelto por MessageBox.Show es DialogResult.Cancel, en cuyo caso se asigna un valor verdadero a la variable cancelar para que la próxima vez que se tenga que examinar un directorio se sepa que no hay que seguir haciéndolo.

 

Y esto es todo... o casi, solo falta la parte de comprobar si se hace doble clic en un elemento del ListView y en ese caso abrir el Explorer de Windows. Este es el código:

Private Sub lvFics_DoubleClick(ByVal sender As Object, _
                               ByVal e As EventArgs) _
                               Handles lvFics.DoubleClick

    With lvFics
        If .SelectedIndices.Count > 0 Then
            ' Abrir la ventana con el directorio del fichero indicado
            Dim dir As String = .SelectedItems(0).SubItems(1).Text
            Process.Start("explorer.exe", dir)
        End If
    End With
End Sub

Aquí lo que se hace es lanzar un proceso con el explorer.exe al que le pasamos como argumento el directorio que tiene que "explorar".

 

Ahora si que está todo.

Más abajo tienes el código completo para Visual Basic 2005 y para Visual C# 2005.

Nota:
En el código de C# tienes la forma de simular la variable estática (Static) de VB, que en realidad consiste en crear una variable a nivel de la clase.
También tienes el código de cómo simular el objeto My.Settings y My.Application.Info, al menos para las cosas que se utilizan en esta utilidad.

 

Espero que te sea de utilidad y que lo "encuentres" bien.

Nos vemos.
Guillermo

 


Espacios de nombres usados en el código de este artículo:

Microsoft.VisualBasic

System.Windows.Forms
System.IO
System.Diagnostics

En el de C# también se usa y no se usa Microsoft.VisualBasic:
System.Reflection


Código de ejemplo (comprimido):

El código de Visual Basic y el de C# está en la página de la última revisión de gsBuscar.


 


La fecha/hora en el servidor es: 23/12/2024 3:15:24

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024