gsDirDif

Una utilidad para comparar el contenido de dos directorios
y manipular los ficheros en ellos contenidos.

 

Revisión del 9/Mar/1998


Durante todos estos años que llevo "computerizado" me he topado con algún que otro fallo en los discos duros y disquetes, además de los que causan los programas cuando se "cuelgan"; todo esto acarrea pérdidas de datos...
Me imagino que esto no sólo me ha pasado a mi, por desgracia es algo que casi todos hemos experimentado.
¿A ti no? Pues, una de dos, o es que eres extremadamente precavido/a o es que llevas poco tiempo en esto... en cualquier caso serás una excepción.
Antes mis copias de seguridad las hacia en disquetes, algunas veces usando el típico programa de copia de seguridad, otras usando algún compresor, (el PKZIP ha sido siempre un asiduo de mis disquetes de utilidades), pero también he hecho "copias a pelo", es decir, simplemente copiando los ficheros de un sitio a otro.
Antes del Windows 95 y los nombres largos, usaba el Comandante Norton para MS-DOS, (otro programa habitual en mis discos de utilidades), este programa tiene una opción que sirve para comparar el contenido de dos directorios, "marcando" los ficheros que son diferentes.
Durante mucho tiempo he usado esta utilidad, pero los nombres largos son un "pequeño" inconveniente, ya que no entiende de nombres largos y estos se pierden. Ahora hay una para Windows95, pero la verdad es que es un poco engorrosa de usar y lo poco que he probado no me termina de convencer.
Hacer una aplicación que maneje nombres largos, realmente no es un inconveniente, sobre todo si la programamos con el Visual Basic de 32 bits. Otra cosa es hacerla para que funcione en MS-DOS, por ejemplo con el QuickBasic, aunque también me fabriqué una utilidad que me sirviera en MS-DOS con este lenguaje... realmente la tengo hecha con la versión 7 de este compilador para MS-DOS. Pero no te preocupes que la que vamos a realizar aquí es para Windows y con el Visual Basic 5, aunque con pequeños retoques, al menos en lo que a las clases usadas se refiere, podrás crearla con el VB4-32 bits.

 

El concepto de esta utilidad

Lo que vamos a hacer, o al menos lo que te voy a explicar, para conseguir el objetivo de esta utilidad es usar, básicamente, dos controles FileList, además de los correspondientes controles para manejar la unidad y directorios, ya que vamos a necesitar seleccionar los directorios que vamos a comparar, sino no sería una utilidad para comparar directorios...
En cada uno de estos controles estarán los ficheros que queremos comparar. Una vez que tengamos el contenido de los dos directorios que queremos comparar, lo que haremos será comparar cada uno de los ficheros con todos los que estén en el otro directorio, si encontramos uno con el mismo nombre, comprobaremos la fecha y "marcaremos" el que tenga la fecha más reciente. En caso de que el fichero no esté en el otro directorio, también quedará marcado.
De esta forma, una vez comprobados todos los ficheros, se quedarán seleccionados los ficheros que sean diferentes (más recientes y los que no estén).
Una vez comprobados los ficheros, podremos hacer varias cosas con los que estén seleccionados, copiarlos, moverlos y eliminarlos.
Como opciones extras, podremos invertir la selección realizada, seleccionar todos o quitar la selección.
Por supuesto que también permitirá la selección manual de ficheros.
Lo que no hará esta utilidad, será "Drag & Drop", así que no creo que Microsoft conceda el "logo" de compatibilidad Win95.
Pero esto será algo que en un futuro implementaré y te mostraré cómo hacerlo. Ya que prefiero empezar por algo fácil y comprensible y siempre habrá tiempo para mejorarla.

 

Las piezas de la utilidad

Para conseguir nuestro propósito, además de que así aprendas otros conceptos de programación, que es de lo que realmente se trata, vamos a usar una serie de clases y colecciones con las que manipularemos la información de los ficheros de cada directorio.

Las clases que usaremos serán tres:

  • Una básica para almacenar cada uno de los ficheros.
  • En otra guardaremos el nombre de cada directorio y todos los ficheros de ese directorio.
  • La tercera contendrá los dos directorio que queremos comparar o manipular.

 

La clase básica, se llama cFichero y tiene estas propiedades:

Propiedades   Descripción
ID   Clave única para almacenar en la colección.
Nombre   El nombre del fichero, sin la ruta.
Tamaño   Tamaño en bytes del fichero.
FechaHora   La fecha y hora de este fichero.
Seleccionado   Un valor Verdadero/Falso que indica si está seleccionado o no.

El tamaño, en principio no lo vamos a usar para nada, para lo que si que puede servir es en el caso de que se quiera mostrar información extra o bien que se use un control ListView para mostrar la información de los ficheros. Pero en esta primera aproximación no se empleará.

 

La clase que contendrá la colección de los ficheros de un directorio se llama: cDirectorio y sus propiedades y métodos son:

Propiedades / Métodos   Descripción
ID   Clave única para almacenar en la colección.
Nombre   El nombre del directorio o ruta del directorio.
Ficheros   Colección de los ficheros de este directorio.
HaySeleccionados   Nos indicará si hay algún fichero seleccionado.
Eliminar   Servirá para eliminar el contenido de la colección de ficheros.

 

Por último, la clase cUnidad será la que contenga los directorios usados. Aunque en principio sólo serán dos, puede contener todos los directorios que la memoria del sistema nos permita, pero nuestra aplicación sólo usará dos.

Propiedades / Métodos   Descripción
Directorios   Colección de los directorios usados.
CompararDirectorios   Compará el contenido de los directorios indicados en los parámetros y seleccionará los que sean distintos o no estén en el otro.

 

Este tipo de utilidad es muy propia para el empleo de ventanas MDI, pero como sólo vamos a usar dos, no será necesario, aunque hay que hacerle ver al usuario con que directorio va a trabajar.

Habrá dos paneles, cada uno de ellos contendrá los controles necesarios para manipular los directorios a comparar y/o manipular.
Para contener estos controles, vamos a usar un frame.
Los controles empleados en cada uno de los frames serán: un control DriveListBox, un DirListBox, un FileListBox y unos cuantos Labels. Todos estos controles formará parte de un array, de esta forma será más fácil acceder a cada uno de ellos sin tener que hacer comprobaciones extras.
Las acciones que se ejecutarán, se harán sobre el panel activo, por tanto necesitaremos que se vea de forma clara cual es el que está activo en cada momento. Para ello usaremos un label que al estar activo mostrará los colores que tenga la barra de una ventana activa y cuando no esté activo, estos colores cambiarán al que estén definidos como inactivos. Siempre usando los colores que el usuario ha seleccionado en las propiedades del escritorio. Esta es una norma que deberíamos emplear siempre y que el VB5 facilita gracias al uso de unas constantes que hacen referencia a estos colores.
Además de este "caption" al estilo de las ventanas, también indicaremos cual es el activo o inactivo cambiando la propiedad Enabled de los controles de manejo de ficheros/directorios/unidad, aunque sólo será "más" visible en el control FileListBox.

Estos efectos del caption se manipulan tanto al pulsar sobre cualquiera de los frames como en la etiqueta que simula el caption de la ventana, en el evento Click de estos controles se llama a un procedimiento que es el que se encarga de esta tarea, veamos el código usado para coseguirlo:

Private Sub Frame1_Click(Index As Integer)
    ActivarPanel Index
End Sub


Private Sub lblCaption_Click(Index As Integer)
    ActivarPanel Index
End Sub


Private Sub ActivarPanel(ByVal Index As Integer)
    'Activar el panel activo y desactivar el otro
    Dim i%
    
    DirActivo = Index + 1
    
    With lblCaption(Index)
        .ForeColor = vbTitleBarText
        .BackColor = vbActiveTitleBar
    End With
    File1(Index).Enabled = True
    Drive1(Index).Enabled = True
    Dir1(Index).Enabled = True
    
    'El otro frame
    i = -1 * (Index = 0)
    With lblCaption(i)
        .ForeColor = vbInactiveCaptionText
        .BackColor = vbInactiveTitleBar
    End With
    File1(i).Enabled = False
    Drive1(i).Enabled = False
    Dir1(i).Enabled = False

End Sub

En este procedimiento también se asigna a la variable DirActivo cual es el directorio activo, esta variable se usará en otros procedimientos y eventos para saber la colección de ficheros a usar y que controles son de los que hay que tomar los datos.

De todas formas, para evitar cualquier tipo de descuido o metedura de pata, antes de efectuar una acción sobre los ficheros, se mostrará un cuadro de diálogo que pedirá confirmación de la acción a realizar.
Aunque no vendrá mal que prestemos atención a estos avisos, ya que un despiste puede causar que se borre accidentalmente el contenido de un directorio. Pero este tipo de utilidades tienen ese "inconveniente", ya que de lo que se trata es de manipular ficheros...

Aunque también podríamos "curarnos en salud" y usar funciones del API que al borrar ficheros los mande a la papelera de reciclaje, pero eso será cuestión de cambiar el KILL que se usa para borrar y sustituirlo por la correspondiente llamanda a la función ShellDelete la cual puedes copiar de la página del API.
De todas formas hay que ser conscientes de que no estamos programando un juego, vamos a manipular ficheros y no es cuestión de juguetear y hacer las cosas con "conciencia".
Como yo soy un gran "despistado", he puesto, en los casos de Eliminar y Mover, el botón que ACEPTA la acción como botón no predeterminado, así habrá algo de más "mala cabeza" a la hora de meter la pata.

El formulario no permite que se cambie de tamaño y siempre se muestra centrado en la pantalla, se podría mejorar permitiendo el cambio de tamaño y adaptar los controles al nuevo tamaño, pero como ya dije antes, la intención es hacer algo sencillo y que te sea fácil de seguir, para que no te pierdas en cosas extras que lo único que hacen es mejorar el look pero no la utilidad del programa. Todo esto puede que esté en una nueva versión de esta utilidad.

 

El aspecto del programa.

Este es el diseño que mostrará de cara al usuario:

aspecto del programa en acción...
Figura 1 - Aspecto del programa en ejecución

Las distintas acciones a realizar están incluidas en el menú Archivo, cada una de ellas lleva asociada una tecla de acceso rápido, básicamente he usado las disponibles en el Comandante Norton, al menos para las operaciones de Copiar (F5), Mover (F6) y Eliminar (F8), aunque en este último caso podría ser más intuitivo el uso de la tecla Suprimir, pero esto es fácil de cambiar.
Además de estas tres "acciones", desde este menú podremos comparar los directorios, seleccionar todos los archivos, invertir la selección y también quitar la selección. Como extras, están las opciones clásicas Acerca de... y Salir.

Este es el aspecto del Menú Archivo:

el menú de Archivo
Figura 2 - Las opciones del menú

Fijate que hay una serie de opciones que están desabilitadas, estas opciones sólo estarán disponibles cuando haya ficheros seleccionados en el panel activo y en el caso de Copiar y Mover, también estarán inhabilitadas si los directorios de los dos paneles hacen referencia al mismo path.
Esto se consigue en el evento Click del menú de Archivo, ya que antes de mostrar el contenido de un menú se produce ese evento, ahí es un buen sitio para hacer las comprobaciones pertinentes.

Private Sub mnuArchivo_Click()
    mnuCopiar.Enabled = m_Directorio(DirActivo).HaySeleccionados
    mnuEliminar.Enabled = mnuCopiar.Enabled
    mnuMover.Enabled = mnuCopiar.Enabled
    mnuQuitarSelec.Enabled = mnuCopiar.Enabled
    
    'Si son el mismo directorio, no copiar ni mover
    If m_Directorio(1).Nombre = m_Directorio(2).Nombre Then
        mnuCopiar.Enabled = False
        mnuMover.Enabled = False
        mnuComparar.Enabled = False
    End If
    
End Sub

Para saber si deben estar habilitados o no se usa el método HaySeleccionados, el cual devolverá un valor verdadero si alguno de los ficheros está seleccionado.
En el caso de que los dos directorios sean iguales, se deshabilitarán las opciones de Copiar, Mover y la de Comparar, ya que no tiene ninguna utilidad hacer estas operaciones con el mismo directorio.

 

Las interioridades del programa.

Ahora veamos algunas de las distintas operaciones a realizar con las opciones del menú.

Antes veremos la parte que se encarga de rellenar las colecciones de ficheros.
Para manipular las clases se declara a nivel de módulo una variable de tipo cUnidad:
Dim m_Directorio As cUnidad

En el evento Form_Load es cuando realmente se "carga" esta clase:
Set m_Directorio = New cUnidad

Ahora que sabemos que m_Directorio es la clase que contiene los directorios, veamos el contenido del menú Comparar:

Private Sub mnuComparar_Click()
    'Comparar los archivos de los dos paneles
    Dim i&
    
    'Asignar los ficheros a la colecciones
    For i = 0 To 1
        CalculaTamaño i
    Next
    
    m_Directorio.CompararDirectorios 1, 2
    
    bCalculando = True
    'Mostrar los ficheros seleccionados de los dos directorios
    With File1(0)
        For i = 1 To m_Directorio(1).Ficheros.Count
            .Selected(i - 1) = m_Directorio(1).Ficheros(i).Seleccionado
        Next
    End With
    CalculaTamaño 0
    
    With File1(1)
        For i = 1 To m_Directorio(2).Ficheros.Count
            .Selected(i - 1) = m_Directorio(2).Ficheros(i).Seleccionado
        Next
    End With
    CalculaTamaño 1
    bCalculando = False
End Sub

Antes de poder comparar los contenidos de los directorios, hay que llenar las colecciones, de ello se encarga el procedimiento del formulario: CalculaTamaño. En este procedimiento también se muestra información del número de ficheros seleccionados además de cuanto espacio ocupan dichos ficheros.

Private Sub CalculaTamaño(ByVal Index As Integer)
    '----------------------------------------------
    ' Calcula el tamaño de los archivos seleccionados
    ' y rellena la colección correspondiente
    '----------------------------------------------
    Dim i As Integer
    Dim nSelect As Integer
    Dim lngTotalSelec As Long
    Dim lngTotal As Long
    Dim sTmp As String
    Const cUnK = 1024&
    Const cUnMega = 1000& * cUnK
    Dim iSelected%
            
    Index = Index + 1
    
    'Borrar el contenido de la colección
    m_Directorio(Index).Eliminar
    'Asignar el nombre del directorio actual
    m_Directorio(Index).Nombre = File1(Index - 1).Path
    
    Label1(Index - 1) = " No hay archivos"
    Label1(Index + 1) = ""
    'Sólo comprobar si hay archivos
    If File1(Index - 1).ListCount > 0 Then
        nSelect = 0
        lngTotalSelec = 0
        With File1(Index - 1)
            lblCaption(Index - 1) = " " & .Path
            sTmp = AddBSlash(.Path)
            For i = 0 To .ListCount - 1
                m_Directorio(Index).Ficheros(i + 1).Nombre = .List(i)
                m_Directorio(Index).Ficheros(i + 1).Tamaño = FileLen(sTmp & .List(i))
                lngTotal = lngTotal + m_Directorio(Index).Ficheros(i + 1).Tamaño
                m_Directorio(Index).Ficheros(i + 1).FechaHora = FileDateTime(sTmp & .List(i))
                m_Directorio(Index).Ficheros(i + 1).Seleccionado = False
                If .Selected(i) Then
                    iSelected = i
                    m_Directorio(Index).Ficheros(i + 1).Seleccionado = True
                    nSelect = nSelect + 1
                    lngTotalSelec = lngTotalSelec + m_Directorio(Index).Ficheros(i + 1).Tamaño
                End If
            Next
            '
            If lngTotalSelec = 0 Then
                Label1(Index - 1) = " " & .ListCount & " ficheros "
                lngTotalSelec = lngTotal
            ElseIf nSelect = 1 Then
                Label1(Index - 1) = " " & .List(iSelected)
                lngTotalSelec = FileLen(sTmp & .List(iSelected))
            Else
                Label1(Index - 1) = " " & nSelect & " ficheros seleccionados "
            End If
            
            If lngTotalSelec > cUnMega Then
                sTmp = Format(lngTotalSelec / cUnMega, "#,###.##") & " M "
            ElseIf lngTotalSelec > cUnK Then
                sTmp = Format(lngTotalSelec / cUnK, "#,###.##") & " K "
            Else
                sTmp = Format(lngTotalSelec, "#,### ")
            End If
        End With
        Label1(Index + 1) = sTmp
    End If
End Sub

Este procedimiento recibe como parámetro el índice del array de controles que debe manipular. Como las colecciones empiezan la numeración por UNO en lugar de CERO, se tiene en cuenta este detalle y por eso se cambia el valor de la variable Index.

El método CompararDirectorio es el que se encarga de hacer las comparaciones pertinentes. Este método forma parte de la clase cUnidad.

Public Sub CompararDirectorios(ByVal dUno As Variant, ByVal dDos As Variant)
    'Comparar los archivos de los dos directorios indicados
    Dim i&, j&
    Dim tFichero1 As cFichero
    Dim tFichero2 As cFichero
    Dim tDir1 As cDirectorio
    Dim tDir2 As cDirectorio
    
    On Local Error Resume Next
    
    i = Directorios(dUno).Ficheros.Count
    j = Directorios(dDos).Ficheros.Count
    'El directorio UNO será el que tenga más archivos
    If i > j Then
        Set tDir1 = Directorios(dUno)
        Set tDir2 = Directorios(dDos)
    Else
        Set tDir1 = Directorios(dDos)
        Set tDir2 = Directorios(dUno)
    End If
    
    'Hay que marcarlos como seleccionados para que se haga
    'siempre la comprobación la primera vez que se repita el bucle
    For Each tFichero2 In tDir2.Ficheros
        tFichero2.Seleccionado = True
    Next
    
    'Comparar todos los archivos del directorio 1 con el 2
    For Each tFichero1 In tDir1.Ficheros
        tFichero1.Seleccionado = True
        For Each tFichero2 In tDir2.Ficheros
            'Los NO seleccionados es que ya se sabe que
            'son de fechas anteriores
            If tFichero2.Seleccionado Then
                If tFichero1.Nombre = tFichero2.Nombre Then
                    'Tienen el mísmo nombre...
                    'No seleccionar el más antiguo
                    If tFichero1.FechaHora = tFichero2.FechaHora Then
                        tFichero1.Seleccionado = False
                        tFichero2.Seleccionado = False
                    ElseIf tFichero1.FechaHora < tFichero2.FechaHora Then
                        tFichero1.Seleccionado = False
                        tFichero2.Seleccionado = True
                    Else
                        tFichero1.Seleccionado = True
                        tFichero2.Seleccionado = False
                    End If
                End If
            End If
        Next
    Next

    Err = 0
End Sub

Los parámetros que se usan en este método son los directorios a comparar, aunque en esta utilidad sólo se usan dos, no sería necesario, pero así estará preparada para que puedas, si lo crees conveniente, almacenar más directorios en esta colección.
Fijate que en lugar de usar las colecciones directamente uso unos "punteros" a esas colecciones, esto lo hago por la razón de que la colección que tenga menos ficheros será la segunda a usar, es decir se realizará una comprobación de una de las colecciones con el contenido de la otra, siendo ésta última la que tenga menos ficheros... no es que acelere el tema, pero de esta forma puedes ver mejor cómo manejar las clases y colecciones.

Una vez que se han comparado los ficheros de los dos directorios, hay que mostrarlos en los controles correspondientes, de eso se encargan los bucles que asignan la propiedad Selected(xxx) del ListBox.

 

Las acciones a realizar.

Ahora vamos a ver el código usado para las distintas opciones del menú.

Empezaremos por las más simples, las de seleccionar todo, invertir la selección y quitar las selecciones.

Private Sub mnuSeleccionarTodo_Click()
    'Seleccionar todos los del directorio activo
    Dim i&
    
    bCalculando = True
    With File1(DirActivo - 1)
        For i = 0 To .ListCount - 1
            .Selected(i) = True
        Next
    End With
    CalculaTamaño DirActivo - 1
    bCalculando = False
End Sub


Private Sub mnuQuitarSelec_Click()
    'invertir la selección del directorio activo
    Dim i&
    
    bCalculando = True
    With File1(DirActivo - 1)
        For i = 0 To .ListCount - 1
            .Selected(i) = False
        Next
    End With
    CalculaTamaño DirActivo - 1
    bCalculando = False
End Sub


Private Sub mnuInvertir_Click()
    'invertir la selección del directorio activo
    Dim i&
    
    bCalculando = True
    With File1(DirActivo - 1)
        For i = 0 To .ListCount - 1
            .Selected(i) = Not .Selected(i)
        Next
    End With
    CalculaTamaño DirActivo - 1
    bCalculando = False
End Sub

Fijate que en todas ellas se cambia el valor de la variable bCalculando, esta variable de tipo Boolean, se usa para que al producirse el evento File1_Click no se ejecute el código de ese evento. Esto se produce al cambiar el valor de .Selected(i) y para ser sinceros, me dió más de un quebradero de cabeza...
Este es el código de ese evento:

Private Sub File1_Click(Index As Integer)
    'Para evitar que se produzca un click cuando
    'se cambia la propiedad Selected()
    '
    If bCalculando Then Exit Sub
    
    'Asignar los nombres a la colección
    CalculaTamaño Index
End Sub

Y todo es porque dentro de este evento se llama al procedimiento de calcular tamaño.
¿Por qué lo pongo en este evento?
Porque cuando se cambia de unidad o de directorio hago una llamada a este evento, además de que de esta forma se pueden seleccionar manualmente ficheros.

A continuación tienes el código de los eventos Change de los controles Dir y Drive para que veas cómo va esto:

Private Sub Drive1_Change(Index As Integer)
    'Cambiar la unidad de disco
    
    'Si la unidad no está preparada, dará error
    On Local Error Resume Next
    
    Dir1(Index).Path = Drive1(Index).Drive
    If Err Then
        Drive1(Index).Drive = Dir1(Index).Path
    End If
    File1_Click Index
    Err = 0
End Sub


Private Sub Dir1_Change(Index As Integer)
    'Cambiar de directorio
    
    File1(Index).Path = Dir1(Index).Path
    Drive1(Index).Drive = Dir1(Index).Path
    File1_Click Index
    GuardarIni sFicIni, "Directorios", "Dir" & CStr(Index), Dir1(Index).Path
End Sub

Fijate que al cambiar de Directorio se actualiza un fichero de configuración, esto es para que se pueda mostrar, la próxima vez que se entre en el programa, el último directorio al que hemos accedido.
Por supuesto que al iniciar el programa hay que leer esa información, esto se hace en el Form_Load.
Y como debemos contar que la última unidad accedida puede que no esté disponible, bien porque sea un disquete o bien porque sea una unidad extraible o de red, tenemos que preverlo para que no nos suelte un error nada más entrar en el programa.
Para ello he usado la detección de errores, veamos el código:

'Este código está en el evento Form_Load

    sFicIni = AddBSlash(App.Path) & "gsDirDif.ini"
    
    On Local Error Resume Next
    
    'Usar el path de la última vez
    Dir1(0).Path = LeerIni(sFicIni, "Directorios", "Dir0", Dir1(0).Path)
    'Si da error, usar el path actual
    If Err Then
        Dir1(0).Path = CurDir$
    End If
    Err = 0
    Dir1(1).Path = LeerIni(sFicIni, "Directorios", "Dir1", Dir1(1).Path)
    If Err Then
        Dir1(1).Path = CurDir$
    End If
    Err = 0

Las funciones LeerIni y GuardarIni, están en un módulo, ya publicado antes en mis páginas, y lo que hacen es escribir en ficheros INI, en lugar de usar el registro de Windows, para que de esta forma sea más fácil de localizar y modificar. Normalmente este tipo de ficheros los guardo en el mismo directorio en que se encuentra el ejecutable, para ello uso la propiedad .Path del objeto App.

 

Las opciones de Copiar, Mover y Eliminar.

Estas tres tareas están en un mismo procedimiento y según la acción indicada hará una cosa u otra o una mezcla de dos, como ocurre con Mover, ya que para mover un fichero, primero hay que copiarlo y después borrarlo del sitio en que estaba... siempre que no se haya producido un error, claro.

Private Sub mnuCopiar_Click()
    'Copiar los ficheros seleccionados del directorio activo
    'al otro
    Accion cCopiar
    
End Sub


Private Sub mnuEliminar_Click()
    'Eliminar los ficheros seleccionados del directorio activo
    Accion cEliminar
End Sub


Private Sub mnuMover_Click()
    'Mover los ficheros
    Accion cMover
End Sub


Private Sub Accion(ByVal queAccion As Integer)
    'Acciones a realizar con los ficheros seleccionados
    
    Dim sTmp$
    Dim DirDestino&
    Dim tFichero As cFichero
    Dim sDir1$, sDir2$
    Dim i&
    Dim bAceptado As Boolean
    
    On Local Error Resume Next
    
    DirDestino = 1
    If DirActivo = 1 Then
        DirDestino = 2
    End If
    sDir1 = AddBSlash(File1(DirActivo - 1).Path)
    sDir2 = AddBSlash(File1(DirDestino - 1).Path)
    
    bAceptado = False
    Select Case queAccion
    Case cCopiar
        sTmp = "¿Quieres COPIAR los ficheros seleccionados" & vbCrLf & _
                  "de: " & sDir1 & vbCrLf & _
                  "a: " & sDir2 & "?"
        If MsgBox(sTmp, vbQuestion + vbYesNo, "Copiar ficheros") = vbYes Then
            bAceptado = True
            Label1(DirActivo - 1) = "Copiando..."
        End If
    Case cMover
        sTmp = "¿Quieres MOVER los ficheros seleccionados" & vbCrLf & _
                  "de: " & sDir1 & vbCrLf & _
                  "a: " & sDir2 & "?"
        If MsgBox(sTmp, vbQuestion + vbDefaultButton2 + vbYesNo, "Mover ficheros") = vbYes Then
            bAceptado = True
            Label1(DirActivo - 1) = "Moviendo..."
        End If
    Case cEliminar
        sTmp = "¿Quieres ELIMINAR los ficheros seleccionados" & vbCrLf & _
                  "de: " & sDir1 & "?"
        If MsgBox(sTmp, vbQuestion + vbDefaultButton2 + vbYesNo, "Eliminar ficheros") = vbYes Then
            bAceptado = True
            Label1(DirActivo - 1) = "Eliminando..."
        End If
    End Select
    
    If bAceptado Then
        CalculaTamaño DirActivo - 1
        Screen.MousePointer = vbArrowHourglass
        For Each tFichero In m_Directorio(DirActivo).Ficheros
            If tFichero.Seleccionado Then
                DoEvents
                Err = 0
                If queAccion = cCopiar Or queAccion = cMover Then
                    FileCopy sDir1 & tFichero.Nombre, sDir2 & tFichero.Nombre
                End If
                If Err = 0 Then
                    'En el caso de Mover,
                    'no borrar si se ha producido un error
                    If queAccion = cEliminar Or queAccion = cMover Then
                        Kill sDir1 & tFichero.Nombre
                    End If
                End If
            End If
        Next
        Screen.MousePointer = vbDefault
        bCalculando = True
        'obligar a releer los ficheros
        File1(0).Path = ".."
        File1(0).Path = Dir1(0).Path
        '
        File1(1).Path = ".."
        File1(1).Path = Dir1(1).Path

        CalculaTamaño 0
        CalculaTamaño 1

        bCalculando = False
    End If
    Err = 0
End Sub

Para finalizar veamos el código usado para Crear y Eliminar directorios. Estos son bastantes simples, simplemente se llama a las funciones incorporadas en el VB. Aunque hay que hacer, como es costumbre, las pertinentes comprobaciones de error y por supuesto, pedir la conformiadad de lo que se quiere hacer.
En el caso de la creación de directorio, para pedir el nombre del directorio a crear, he usado la función INPUTBOX. Si el nombre introducido es una cadena vacía, simplemente no se hace nada.

En el caso de que queramos borrar un directorio, éste no será el que está actualmente "abierto", en su lugar se borrará el que esté seleccionado en el DirListBox.

Private Sub mnuMkDir_Click()
    'Crear un directorio
    Dim sDir1$
    Dim sTmp$
    
    sDir1 = AddBSlash(File1(DirActivo - 1).Path)
    
    On Local Error Resume Next
    sTmp = InputBox("Nombre del directorio a crear en " & vbCrLf & sDir1, "Crear directorio", "")
    
    If Len(sTmp) Then
        sTmp = sDir1 & sTmp
        MkDir sTmp
        If Err Then
            MsgBox "Imposible crear el directorio:" & vbCrLf & sTmp, vbInformation, "Error al crear directorio"
        Else
            'Releer el directorio
            sDir1 = File1(DirActivo - 1).Path
            Dir1(DirActivo - 1).Path = sTmp
            Dir1(DirActivo - 1).Path = sDir1
        End If
    End If
    Err = 0
End Sub


Private Sub mnuRmDir_Click()
    'Eliminar el directorio seleccionado en el DirListBox
    Dim sDir1$
    
    On Local Error Resume Next
    
    With Dir1(DirActivo - 1)
        sDir1 = AddBSlash(.List(.ListIndex))
    End With
    
    If MsgBox("¿Quieres eliminar el directorio:" & vbCrLf & sDir1 & "?" & vbCrLf & "(Sólo se eliminará si está vacio)", vbQuestion + vbYesNo + vbDefaultButton2, "Eliminar directorio") = vbYes Then
        RmDir sDir1
        If Err Then
            MsgBox "Imposible eliminar el directorio:" & vbCrLf & sDir1 & vbCrLf & "puede que no esté vacio o se haya denegado el acceso.", vbInformation, "Error al eliminar directorio"
        Else
            'Releer el directorio
            sDir1 = File1(DirActivo - 1).Path
            Dir1(DirActivo - 1).Path = ".."
            Dir1(DirActivo - 1).Path = sDir1
        End If
    End If
    Err = 0
End Sub

Para saber que directorio está seleccionado se usa el valor guardado en el .List(.ListIndex)
Esto mismo se usa en el evento Dir1_KeyPress, para que al pulsar Intro se cambie de directorio:

Private Sub Dir1_KeyPress(Index As Integer, KeyAscii As Integer)
    'Si se pulsa INTRO, cambiar de directorio
    
    On Local Error Resume Next
    
    If KeyAscii = 13 Then
        With Dir1(Index)
            .Path = .List(.ListIndex)
        End With
    End If
    Err = 0
End Sub

Bueno, creo que habrá quedado bien claro cómo se ha "fabricado" esta utilidad y confio en que haya sido instructivo, ya que de eso es de lo que se trata.

Para bajarte los listados, recuerda que son para VB5, pulsa en este link. (gsDirDif_cod.zip 9.85 KB)
Este otro link es para que descargues el ejecutable, pequeñito pero matón. (gsDirDif.zip 11.9 KB)
Recuerda que está compilado con el Service Pack 2 (SP2), por tanto necesitarás el runtime correspondiente, que puedes bajar de esta otra dirección:
http://www.chez.com/guille/ftp/Msvbvm50.zip
La versión usada es: 05.00.4319 (SP2)

Espero que esta utilidad te sea tan útil como lo es para mi y, si lo crees conveniente, hazme un comentario sobre lo que te ha parecido.

Nos vemos.
Guillermo


ir al índice