Las Clases en Visual Basic
(clásico o anterior a .NET)

Iniciado en Diciembre 1997
Actualizado contenido el 28/Feb/2004
Revisado el 25/Oct/2005


Nota:
Te aclaro que todo lo tratado en esta página es referente a las versiones anteriores a .NET, y en la mayoría de los casos fueron escritos con Visual Basic 4.0, por tanto es posible que tengas que revisar el código para usarlo con Visual Basic 6.0 ó 5.0, pero no serán válidos para Visual Basic .NET (2002) y posteriores.


No es, como quieren pintarla, la panacea de la programación ni tampoco un ogro al que haya que temer. Es como usar "tipos declarados", (el TYPE que se empezó a usar con QuickBasic 4.0), pero con "valor añadido".

Más de uno se habrá frustrado al intentar "meterle" mano a esto de las clases, además con los ejemplos que he visto por ahí, (que sólo los entienden los que ya saben), no se aclaran bien las cosas.
No pretendo enseñar a nadie, sólo contaros lo que sé, o parte de lo que sé, para que os resulte fácil y, espero, sencillo convivir con las clases en VB.

Veámoslo con ejemplos:

Contenido:

  1. Usarla en lugar de un array (Dic/97)
  2. En el programa gsExecute hay otro ejemplo
  3. Crear una librería OLE
  4. Un poco más de clase
  5. Y más todavía, con y sin librería OLE (Librería OLE y ejemplos de cómo usar los objetos expuestos)
  6. Servidor OLE para obtener los recursos del Sistema
  7. Un servidor OLE Outprocess: Averiguar si una aplicación se está ejecutando.
  8. Clase para simular el StatusBar (de forma simple)
  9. Una clase para saber los directorios del Sistema (Windows, System, Archivos de Programa, Menú Inicio, etc.)
  10. Más artículos sobre los objetos: publicados originalmente en VB Online edición USA (14/Ago/98)
    Objetos en Visual Basic
    Objetos en Visual Basic: Piensa objetivamente
  11. Los artículos publicados en Algoritmo sobre Objetos en Visual Basic
  12. El tercer (y por ahora último) artículo publicado en Algoritmo sobre Objetos en VB
  13. Una clase para manipular el registro del Sistema
  14. Colaboración de Luis Sanz: Ponga una clase en su vida
  15. cGetTimer: una clase para calcular periodos pequeños de tiempo
  16. Nueva colaboración de Luis Sanz: Ponga una clase en su vida (dos)
  17. Simular la herencia con el Visual Basic
  18. Notas sobre la compatibilidad binaria en componentes ActiveX
  19. Copiar objetos en Visual Basic (clonar objetos) (24/Ago/99)
  20. SelDir: Diálogo para seleccionar Directorios y/o archivos
  21. gsSelDir: Control para seleccionar Directorios
  22. gsSelDirFile: Control para seleccionar Directorios y Archivos (gsSelDirFile)
  23. Sobre la creación de componentes ActiveX/COM (entrega 47 del curso básico) (28/Feb/2004)
  24. Repaso rápido sobre las clases (entrega 47 del curso básico) (28/Feb/2004)

Más ejemplos relacionados con las clases desde Visual Basic 6.0 y anteriores


El primer: Usarla en lugar de un array.

Seguramente ocupará más memoria y puede que sea más lento, pero la ventaja es que no es necesario "redimensionar" el array cada vez que se añade o quita un elemento.
Realmente no es necesario usar clases para este menester, sólo con una colección sería suficiente, veámoslo:

¿Quién no ha necesitado procesar datos de forma "secuencial" e ir incrementando las coincidencias de determinados valores? (Candemore... hasta parece difícil de explicar esto...8-) )
Pongamos un ejemplo trivial y casi inútil: Imaginaros que queremos comprobar cuantos archivos de tipos diferentes hay en un directorio, es decir que si hay 5 de tipo BAS, 10 de tipo FRM, 8 de tipo FRX, etc, nos lo muestre.

El procedimiento a seguir sería:
1- Leer cada archivo del directorio
2- Comprobar si el tipo procesado ya existe
3- Si existe, incrementar la cantidad de este tipo
4- Si no existe, guardar este nuevo tipo
5- Repetir hasta que no queden más archivos.

Usando el Basic de toda la vida, con una variable definida para el nombre y la cantidad, sería:

Type tArchivo
	Extension As String
	Cantidad  As Integer
End Type
Dim Archivos() As tArchivo

Dim a$, b$
Dim i%, j%, k%, Hallado%

'Leer cada archivo del directorio
a$ = Dir$("*.*")
i% = 0
Do While Len(a$)
	'Comprobar la extensión
	k% = Instr(a$,".")
	b$=""
	If k% Then b$ = Mid$(a$,k%+1)
	'Comprobar si ya está en la "lista"
	Hallado% = False
	For j%=1 to i%
		If Archivos(j%).Extension = b$ Then
			'Si está incrementar la cantidad de veces
			Archivos(j%).Cantidad = Archivos(j%).Cantidad + 1
			'Flag para saber que lo hemos hallado
			Hallado% = True
			'No comprobar más, así que salimos del For
			Exit For
		End If
	Next
	'Si no está en la "lista" añadirlo
	If Not Hallado% Then
		i% = i% + 1
		'Re-dimensionar la variable sin perder los datos anteriores
		Redim Preserve Archivos(i%) as tArchivo
		Archivos(i%).Extension = b$
		Archivos(i%).Cantidad = 1
	End If
	a$=Dir$
Loop
'Mostrar los datos
Print "Extensión","Cantidad"
For j%=1 to i%
	Print Archivos(j%).Extension, Archivos(j%).Cantidad
Next

Bueno este sería el ejemplo "clásico", ahora veamoslo usando una colección.

Definiremos una clase equivalente a la variable tArchivo

'Crear un módulo de Clase (tipo cls) con el Nombre cArchivo
'Añadir estas propiedades:
Public Extension As String
Public Cantidad As Integer


'Esto ponerlo en el módulo de prueba (en el Sub Main si es un módulo BAS)

'Colección para guardar elementos de la clase Archivo
Dim Archivos as New Collection
'Variable temporal de la clase Archivo
Dim tArchivo as New cArchivo

Dim a$, b$
Dim k%

'La rutina de error hará la parte "sucia" del trabajo
On Local Error Resume Next

'Leer cada archivo del directorio
a$ = Dir$("*.*")
Do While Len(a$)
	'Comprobar la extensión
	k% = Instr(a$,".")
	b$=""
	If k% Then b$ = Mid$(a$,k%+1)
	'Comprobar si ya está en la "lista"
	Set tArchivo = Archivos(b$)
	'Esto es lo mismo que: Set tArchivo = Archivos.Item(b$)
	If Err Then     'No existe, añadirlo
		Err = 0
		Set tArchivo = Nothing
		tArchivo.Extension = b$
		tArchivo.Cantidad = 1
	Else		'Ya existe, incrementar la cantidad
		tArchivo.Cantidad = tArchivo.Cantidad + 1
	End If
	'Añadirlo a la colección
	Archivos.Add tArchivo, tArchivo.Extension
	If Err Then
		'Ya existía, quitarlo y volverlo a añadir
		Archivos.Remove tArchivo.Extension
		Archivos.Add tArchivo, tArchivo.Extension
		Err = 0
	End If

	a$=Dir$
Loop
'Mostrar los datos
Print "Extensión","Cantidad"
For Each tArchivo In Archivos
	Print tArchivo.Extension, tArchivo.Cantidad
Next

El ejemplo usando clases, está en el archivo: cls_ej01.zip (+/- 1.941 bytes)

Bueno ya está, usando la clase y la colección.
He usado el error que da al añadir un elemento a una colección cuando ya existe, (no pueden haber dos elementos en una misma colección con la misma clave), para comprobar que ya existía. Lo mismo ocurre cuando se quiere "tomar" un elemento de una colección y no existe: da error.

Por supuesto se podría haber creado un "método" en la colección para añadir los datos, en lugar de hacerlo en el código normal. (Siempre que veo "aclaraciones" como esta en los libros, me digo ¿y por qué no lo explicas?)
Así que pasemos a la acción y veámoslo en la práctica:


Ahora como en las series por capítulos de la tele:
La respuesta EL AÑO QUE VIENE, permanezcan atentos a esta pantalla.

No es "coña" es que son más de las 6 y mañana/hoy hay que seguir currando, 8-]

Ah!, Feliz Año nuevo a todos.


Bueno, ya estoy por aquí de nuevo... ¿Que tal las uvas? Espero que fueran de la buena suerte.

Lo prometido es deuda, os explico un poco y después pongo los listados.
Para este menester, he creado una clase nueva, en la cual se guarda una colección de la clase cArchivo, en lugar de usar la colección directamente, esta clase/colección tiene una función: Archivos, con un parámetro opcional que será, si se especifica, el ITEM que queremos obtener y si no se especifica, devuelve la colección, (fijaros que el tipo devuelto por la función es Variant y este tipo se lo "traga" todo).
Al especificar el parámetro, la función comprueba si ya existe, si es así devuelve ese item. Si no existe, lo añade a la colección. Pero en cualquier caso devuelve bien el nuevo dato, bien el dato especificado en el parámetro, de esta forma se puede incrementar el contenido de la Cantidad de la misma forma que se haría con una variable normal.
¿No queda claro?
Veamos el ejemplo:

x.Archivos("cls").Cantidad = x.Archivo("cls").Cantidad + 1

Primero se procesa la parte derecha de la expresión, es decir lo que está después del signo igual:
x.Archivos("cls").Cantidad + 1
x.Archivos("cls") devuelve un objeto de la clase cArchivo cuya Extensión es "cls", la primera vez que esto ocurra, lo creará, por tanto objeto.Cantidad será 0 (cero), se le incrementa 1 (+ 1) y se almacenará en el objeto de la colección cuya Extensión sea "cls" (parte izquierda de la expresión)
Si se hace de nuevo este proceso el valor devuelto por objeto.Cantidad será 1, así que al incrementarlo, se convertirá en 2, etc.
¿Más claro ahora?
Espero que sí. De todos modos si hay algo que no entendáis, no dudéis en preguntar. Aunque algunas veces es suficiente con saber que funciona, sin necesidad de entrar en más detalles... 8-)
En fin vamos con los listados del ejemplo:

La clase cArchivo es la misma que en el ejemplo anterior:

'Módulo de Clase (tipo cls) con el Nombre cArchivo
'Propiedades:
Public Extension As String
Public Cantidad As Integer


'Módulo de Clase con el Nombre cArchivos
Option Explicit
'Colección de la clase cArchivo
Private colArchivos As New Collection


Public Function Archivos(Optional Index As Variant) As Variant
	'Si se especifica el Index, se devuelve ese item
	'sino se devuelve la colección

	Static tArchivo As New cArchivo

	On Local Error Resume Next
	'Si no se especifica el parámetro...
	If IsMissing(Index) Then
		Set Archivos = colArchivos	'Devolver la colección
	Else
		'Devolver el item indicado (si existe)
		Set Archivos = colArchivos.Item(Index)
		If Err Then			'No existe, así que añadirlo
			Err = 0
			Set tArchivo = Nothing	'Necesario para dejar "vacío" el objeto
			tArchivo.Extension = Index
			'Añadirlo a la colección
			colArchivos.Add tArchivo, tArchivo.Extension
			'La función devuelve una "copia" del objeto
			Set Archivos = tArchivo
		End If
	End If
	'Liberar memoria
	Set tArchivo = Nothing
End Function


'Esta es la prueba...

'Colección para guardar elementos de la clase Archivo
Dim Lista As New cArchivos
'Variable temporal de la clase Archivo
Dim tArchivo As New cArchivo

Dim a$, b$
Dim k%

'Leer cada archivo del directorio
a$ = Dir$("*.*")
Do While Len(a$)
	'Comprobar la extensión
	k% = InStr(a$, ".")
	b$ = ""
	If k% Then b$ = Mid$(a$, k% + 1)
	'Esto es todo lo que hay que hacer,
	'del resto se encarga la función Archivos
	Lista.Archivos(b$).Cantidad = Lista.Archivos(b$).Cantidad + 1
	a$ = Dir$
Loop

'Mostrar los datos
Print "Extensión", "Cantidad"
For Each tArchivo In Lista.Archivos
    Print tArchivo.Extension, tArchivo.Cantidad
Next

Realmente es más "corta" que el ejemplo anterior, ya que el trabajo lo realiza la función Archivos y aquí es donde entra el consejo de los que defienden las clases: Podemos cambiar el funcionamiento interno de la función Archivos sin que ello nos obligue a cambiar la forma de usarla, es decir el código:
Lista.Archivos(b$).Cantidad = Lista.Archivos(b$).Cantidad + 1
permanecerá, (o así "debería" ser), igual; independientemente de cómo "actúe" la función internamente.

BUF! Creo que ya está bien para el primer día del año.

El segundo ejemplo usando clases, está en el archivo: cls_ej02.zip (+/- 2.380 bytes)


 

3.- Crear una libreria In-Process OLE (toma ya!)

Eso de In-Process OLE, queda muy bien, pero ¿qué es?
Pues una DLL, es decir una librería dinámica a la que podemos llamar desde nuestro programa, pero en lugar de hacerlo como es habitual, lo que se hace es usar los objetos que expone. ¿?
Si no te has enterado, no importa, ya verás como te quedas con la copla.
He incluido un ejemplo para ver esto en funcionamiento.
El programa, de ejemplo, es bastante simple:
Muestra unos nombres en un combo y espera que se escriba algo en un text box. Una vez pulsado Aceptar o Cancelar, devuelve a la aplicación que la ha llamado el nombre seleccionado del combo y lo que se haya escrito en el text-box.
En esta librería se incluyen dos clases: cUser y cClave, además de un form para mostrar al usuario.
En este tipo de librerías, se deben incluir forms y mostrarlos modalmente, es decir que hasta que no se cierre (o se oculte), el programa no debe seguir. Tiene su lógica, porque normalmente se usa para pedir datos, etc.
Para usar componentes OLE, que no sean "Modales", se debe hacer con la otra forma: Out-Process, en estos, si hay forms, no deben ser modales... Ya lo verás en un próximo ejemplo, que intentaré que esté lo antes posible.

La clase cUser es muy simple: sólo tiene dos propiedades, que se declaran públicas para que se puedan usar externamente. También hay que indicarle, por medio de las propiedades, que es multiuso y pública.

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "cUser"
Attribute VB_Creatable = True
Attribute VB_Exposed = True
'------------------------------
'Clase para Usuario (20/Ene/97)
'©Guillermo Som, 1996-97
'------------------------------
Option Explicit
'
Public Nombre As String
Public Clave As String

La clase cClave ya es un poco más larguilla... Las propiedades también son Multiuso y Pública. Sólo tiene un método: la función Acceso. Esta función es la que se usará para asignar los datos y mostrar el form...

'--------------------------------------------------------
'Entrada a la función de pedir las claves de acceso
'Usuarios   Array (colección) de tipo cUser (sólo se usa el Nombre)
'Modo       1,      seleccionar usuario y NO    pedir clave
'           2, NO   seleccionar usuario y       pedir clave
'           3,      seleccionar usuario y       pedir clave
'Usuario    El mostrado inicialmente
'msgClave   Para el caption del form
'
'Devuelve   el usuario seleccionado y la clave introducida
'           o vacio si se pulsa Cancelar
'---------------------------------------------------------
Public Function Acceso(ByVal Usuarios As Variant, ByVal Modo As Integer, _
    Optional ByVal Usuario As Variant, Optional ByVal msgClave As Variant) As cUser

    Dim frmClaves As New fPedirClave	'Se declara el form
    Dim tUsuario As New cUser		'Copia temporal de la clase cUser
    Dim i As Integer
    Dim j As Integer
    Dim vTmp As String
    Dim sUser As String

    'Caption a mostrar
    If IsMissing(msgClave) Then
        vTmp = "Clave de Acceso"
    Else
        vTmp = msgClave
    End If
    'Primer usuario a mostrar
    If IsMissing(Usuario) Then
        sUser = Usuarios(1).Nombre
    Else
        sUser = Usuario
    End If

    With frmClaves
        .Caption = vTmp
        .TxtClave = "NINGUNA"
        .Combo1.Clear
        j = 0
        i = 0
	'Añadir los nombres al Combo y averiguar en que posición está.
        For Each tUsuario In Usuarios
            .Combo1.AddItem tUsuario.Nombre
            j = j + 1
            If sUser = tUsuario.Nombre Then
                i = j - 1
            End If
        Next
        If i = -1 Then
            i = 0
        End If
        .Combo1.ListIndex = i
        '
        If Modo = 1 Then
            .TxtClave.Enabled = False
        ElseIf Modo = 2 Then
            .Combo1.Enabled = False
        End If
        'El form hay que mostrarlo en modo Modal
        .Show vbModal

        Set tUsuario = Nothing
        If .Cancelado Then
            tUsuario.Nombre = ""
            tUsuario.Clave = ""
        Else
            tUsuario.Nombre = .Combo1.Text
            tUsuario.Clave = .TxtClave
        End If
        Set Acceso = tUsuario
    End With
    'Liberar la memoria asignada...
    Set tUsuario = Nothing
    Unload frmClaves
End Function

El form, además del combo, el text-box y los botones de Aceptar y Cancelar, tiene una propiedad pública: Cancelar, que se usa para comprobar si se ha pulsado en cancelar o se ha cerrado la ventana de otra forma distinta a pulsar en los botones.
El código del form, es bastante simple..., tampoco necesita ser mas sofisticado... 8-)

'--------------------------------------------------
'Form para pedir las claves de acceso   (31/May/96)
'©Guillermo Som, 1996-97
'
'Nueva versión (más simple)             (20/Ene/97)
'--------------------------------------------------

Option Explicit
Private pCancelar As Boolean


Private Sub CmdAceptar_Click()
    pCancelar = False
    Hide
End Sub


Private Sub CmdCancelar_Click()
    pCancelar = True
    Hide
End Sub


Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    'Si se cierra de cualquier forma, salvo por medio del código...
    'actuará igual que si se hubiese pulsado en cancelar
    If UnloadMode <> vbFormCode Then
        pCancelar = True
    End If
End Sub


Private Sub Form_Unload(Cancel As Integer)
    'Liberar la memoria
    Set fPedirClave = Nothing
End Sub


Private Sub TxtClave_GotFocus()
    'Seleccionar todo el texto
    TxtClave.SelStart = 0
    TxtClave.SelLength = Len(TxtClave)
End Sub


Public Property Get Cancelado() As Boolean
    Cancelado = pCancelar
End Property

Además de las dos clases y el form, para crear una DLL-OLE, hay que entrar por un Sub Main, pero no es necesario que haga nada, el código es el siguiente: (yo lo uso para todas las clases que hago)

'--------------------------------------------------
'OLEMain.bas (11/Jun/96)
'©Guillermo Som, 1996-97
'
'Módulo genérico para Librerías OLE
'--------------------------------------------------
Option Explicit

Sub Main()
    '
    'Sub Main es necesario para in-process OLE server
    '
End Sub

Por último decir que debes indicarle a VB que es una librería DLL, para ello, especifica lo siguiente:
Selecciona Tools/Options...
En la solapa Project, selecciona Ole Server y en Application Description escribe lo que quieras que muestre cuando hagas referencia a esta librería.
En la solapa Advanced, selecciona Break in Class Module y Use OLE DLL Restrictions

Bueno, eso es todo. Para probarlo en el entorno, primero cargas este proyecto en VB. Lo ejecutas, parecerá que no hace nada. Pero si que está haciendo, está exponiendo las dos clases.
Abre otra copia de VB y en el menú Tools/References... habrá una referencia a este proceso. Si pulsas en el Object Browser, verás las dos clases y los objetos que puedes usar.
Para los listados completos y un ejemplo de un programa de mensajería interna:
Baja el fichero: PedirCla.zip (3.777 bytes)
Ejemplo que usa estas clases: Mensajes.zip (29.112 bytes)

Nota:
La versión VB en castellano, indicará estas opciones de otra forma, pero no sé cual será la traducción.
Lo siento, pero no me he actualizado a esa versión porque cuando la probé, me dió errores "tontos", como el de traducir True y False por Verdadero y Falso. Al usar el programa compilado, no funcionaba al no "entender" estos valores en Castellano. No sé si lo han corregido, pero tampoco lo voy a comprobar.


 

4.- Un poco más de clase (16/Feb)

Baja el listado con los ejemplos (rdir.zip 5.5 KB)

Este ejemplo que voy a poner es para leer todos los archivos de un directorio y de todos los directorios que penden de él.
Realmente no son todos los archivos, sino los de la extensión especificada.
El concepto del uso de las clases es básicamente el mismo que en el primer ejemplo, pero con algunas cosillas nuevas.
He incluido un form de prueba, que no voy a listar aquí, si quieres ver el programa completo, bájate el listado de ejemplo.
El corazón de este tinglado se basa en la función RecorrerDir, que es recursiva, es decir se llama así misma, para poder analizar los directorios que están por debajo del que se analiza.
Las clases se usan para almacenar los archivos encontrados.
La primera es básica y sólo tiene dos propiedades el ID o clave del elemento y Nombre que usaremos para guardar el nombre del archivo y el path.
La segunda maneja una colección de la clase básica y tiene un método, Nuevo, para añadir nuevos elementos a la colección
Veamos los listados de estas clases:
La primera es "suficientemente" simple:

'--------------------------------------------------
'cNombre                                (15/Feb/97)
'Clase de uso general, para guardar un nombre
'
'©Guillermo Som Cerezo, 1997
'--------------------------------------------------
Option Explicit
'
'Esta propiedad se usará para la clave del elemento
Public ID As String
'Este otro para guardar el nombre del archivo
Public Nombre As String

La segunda clase cNombres:
Tiene una colección privada a la que se accede mediante el método Nombres.
Y un método: Nuevo, para añadir nuevos elementos a la colección.

'--------------------------------------------------
'cNombres                               (15/Feb/97)
'Clase de uso general,
'Esta es una colección de objetos cNombre
'
'©Guillermo Som Cerezo, 1997
'--------------------------------------------------
Option Explicit
Private colNombres As New Collection



Public Function Nombres(Optional Index As Variant) As Variant
    'El acceso a esta función permitirá usar
    'los datos de la clase cNombre
    'Si se especifica el Index, se devuelve ese item
    'sino se devuelve la colección
    '
    Static tNombre As New cNombre

    On Local Error Resume Next

    'Si no se indica el index, se devuelve la colección
    If IsMissing(Index) Then
        Set Nombres = colNombres
    Else
        'Comprobar si está en la colección
        Set Nombres = colNombres.Item(Index)
        'Si produce error, es que no existe
        If Err Then
            Err = 0
            Set tNombre = Nothing
            tNombre.ID = Index
            'Añadirlo
            colNombres.Add tNombre, tNombre.ID
            'Devolver el item creado
            Set Nombres = tNombre
        End If
    End If
    'Liberar memoria
    Set tNombre = Nothing
End Function



Public Sub Nuevo(sNombre As String)
    'Añade una nueva entrada a la colección
    Static NumEntradas As Integer
    Dim sID As String

    'Incrementamos el número de entradas
    NumEntradas = NumEntradas + 1
    'Asignamos la clave del elemento
    sID = "nn" & Format(NumEntradas, "0000")
    'Usamos la función Nombres de esta clase
    Me.Nombres(sID).Nombre = sNombre
End Sub

Ahora veamos la función que analiza los directorios y asigna los elementos de la colección:

Public Function RecorrerDir(ByVal NewPath As String, PathAnt As String, col As Variant) As Boolean
    'Función recursiva para recorrer los directorios
    'basada en un ejemplo de Visual Basic para MS-DOS
    '
    Dim NumDirs As Integer
    Dim OldPath As String
    Dim sFic As String
    Dim ThePath As String
    Dim res As Boolean
    Dim i As Integer
    Dim sID As String

    On Local Error GoTo RutinaError

    NumDirs = Dir1.ListCount
    Do While NumDirs > 0
        DoEvents
        If Cancelar Then
            RecorrerDir = True
            Exit Function
        End If
        OldPath = NewPath
        If Dir1.ListCount > 0 Then
            Dir1.Path = Dir1.List(NumDirs - 1)
            'para crear la entrada de este directorio
            sID = Dir1.Path
            res = RecorrerDir((Dir1.Path), OldPath, col)
        End If
        If res Then
            RecorrerDir = True
            Exit Function
        End If
        NumDirs = NumDirs - 1
    Loop
    'Asignar la extensión que queremos mostrar
    File1.Pattern = Ext
    If File1.ListCount Then
        If Len(NewPath) <= 3 Then
            ThePath = NewPath
        Else
            ThePath = NewPath & "\"
        End If
        For i = 0 To File1.ListCount - 1
            DoEvents
            If Cancelar Then
                RecorrerDir = True
                Exit Function
            End If
            sFic = ThePath & File1.List(i)
            'Añadir a la colección
            col.Nuevo sFic
        Next
    End If
    If Len(PathAnt) Then
        Dir1.Path = PathAnt
    End If
    RecorrerDir = False
    Exit Function

RutinaError:
    If Err = 7 Then
        MsgBox "No hay suficiente memoria para completar la tarea", vbCritical, "RecorrerDir"
    Else
        MsgBox "ERROR: " & CStr(Err) & ", " & Error$, vbCritical, "RecorrerDir"
    End If
    RecorrerDir = True
End Function

Bueno, estos son los listados. El resto del formulario, tiene los botones y los text-box para la entrada de datos, etc.
Lo que hay de novedad es el método Nuevo de la clase cNombres.
Es muy simple su funcionamiento, con la variable estática NumEntradas, lleva la cuenta de los elementos asignados a la colección, asigna el valor para la clave y llama a la función Nombres para añadir el nuevo elemento.
Veamos por último la parte del código que se encarga de llamar a la función RecorrerDir, leer los elementos de la colección y añadirlos a un listbox:

    t1 = Timer
    If RecorrerDir(Dir1.Path, "", colDir) = False Then
        t3 = Timer - t1
        'Mostrar los datos
        List2.Clear
        For Each tNombre In colDir.Nombres
            DoEvents
            If Cancelar Then Exit For
            List2.AddItem tNombre.Nombre
        Next
        t2 = Timer - t1
        Label3 = colDir.Nombres.Count & " archivos, procesados en: " & Format(t3, ".00") & " seg, listados en: " & Format(t2, ".00") & " seg"
        Label3.Visible = True
    End If

Como puedes ver, usando las clases se simplifican las cosas. Aunque para decir la verdad, podríamos haber usado el ListBox directamente en la función recursiva y hubiesemos obtenido el mismo resultado, pero mucho más lento!!!
En las pruebas que he realizado, asignar 365 archivos de 24 directorios, tardó en procesarlo 6.64 segundos y en asignarlo al ListBox 21.91.


 

5.- Y más todavía, con y sin librería OLE (11/Mar)


Revisión del 8 de Abril: Si quieres ver los listados de la nueva revisión, pásate por este link.
En él encontrarás la explicación y los listados, tanto de la librería OLE como la forma de usarlo sin librería.

¡LOS HE QUITADO DE ESTA PÁGINA, PORQUE LOS DE AQUÍ YA ESTABAN OBSOLETOS!

MUY IMPORTANTE: Los listados y las imágenes están en la utilidad SelDir


Es que las mentes "calenturientas" nunca paran! Y además, me gusta complicarme la vida.
Lo que viene a continuación es una revisión del caso anterior, pero mejorado y más funcional.

En líneas generales lo que tenemos aquí es lo siguiente:
Una librería OLE que permite seleccionar un directorio de una caja de diálogo. Sólo el directorio.
Y además permite que se procesen todos los archivos de una determinada extensión que estén en ese directorio.
Y para "liar" más la cosa, también hay una función/método que puede leer todos los archivos de la extensión especificada y los que estén en todos los directorios que haya por "debajo" del indicado.
Por supuesto con ejemplos: dos ejemplos. El primero usando la librería (se incluye sólo el listado de la librería, para usarla como componente OLE, deberás compilarla). El segundo usando las clases en el propio proyecto.
Incluyo por tanto dos archivos VBP cada uno con una definición distinta de la constante para compilación condicional.

Veamos primero los archivos de prueba. Realmente es un archivo, pero que se puede cargar como programa independiente, es decir, no necesitando ningún objeto OLE o bien usando la librería creada con: OLE_SelDir.vbp

Para el primer ejemplo deberás usar el proyecto: test_SelectDir.vbp
Para el segundo, una vez instalada la DLL o bien ejecutandose en otra instancia de VB4, usa: test_oSelDir.vbp

¿Te lias? No importa, vamos a ver las cosas y saldrás de dudas.
Veamos un gráfico con el form de prueba y ahora veremos los procedimientos y las explicaciones:

(!!!)

Fijate en la declaración de las clases/objetos para manejar la clase/objeto/librería OLE.
Si usamos el primer proyecto, (test_SelectDir.vbp), entonces estará definida la constante esOLE como esOLE = 0
Pero si el proyecto cargado es: test_oSelectDir.vbp, estará definida como esOLE = -1
Con la compilación condicional, podemos usar un mismo módulo, para varias cosas distintas, en este ejemplo para usar o no unos objetos de una librería OLE.

Bien, ahora veamos los componentes de la librería OLE. Es decir el "tinglado" este para Seleccionar los Directorios.
Si quieres usarlo como librería OLE, deberás usar el proyecto OLE_SelDir.vbp. Está compuesto por los siguientes archivos:

cNombre.Cls	La clase básica, ya la vimos antes, en el ejemplo anterior.
cNombresA.cls	Una colección de la clase cNombre.Cls,
		igual que en el ejemplo anterior, sólo cambia el nombre, ¡creo!
cSelectDir.Cls	Esta clase es la encargada de procesar los directorios y archivos
SelectDir.Frm	El form que muestra los directorios a seleccionar y el que hace el trabajo duro
		En él está la función RecorrerDir que es "casi" igual que el del ejemplo anterior
OLEMain.bas	El módulo que uso siempre para crear las librerías OLE

(!!!)

Resumiendo, si quieres usar la clase desde una librería OLE, debes declarar las variables de la siguiente forma:

    'Si usamos la librería OLE
    Dim tNombre As New oSelDir.cNombre
    Dim cSelDir As New oSelDir.SelDir

Para usar las clases junto on el programa que las vaya a utilizar, declara las variables así:

    'Si usamos las clases cargadas
    Dim tNombre As New cNombre
    Dim cSelDir As New SelDir

Para "procesar" todos los archivos de una Extensión y en todos los directorios que estén por debajo del indicado, úsalo de esta forma:

    Set colDir = cSelDir.Directorios(sRuta, sExtension)

Si quieres procesar sólo los directorios, no indiques la extensión:

    Set colDir = cSelDir.Directorios(sRuta)

O bien, hazlo con una cadena vacía:

    Set colDir = cSelDir.Directorios(sRuta, "")

Y si la intención es la de procesar sólo un directorio, pues:

    'Los archivos del directorio especificado
    Set colDir = cSelDir.Directorio(sRuta, sExtension)

Espero que puedas sacarle rendimiento a todo esto.
Si aún tienes dudas de cómo crear librerías de objetos OLE y de cómo exponer las clases/objetos incluidas en la librería...
Pregúntame, pero antes te recomiendo que le eches un vistazo a los listados, que para eso los incluyo.
De todas formas me gustaría que me comentaras algo sobre todo esto. Entre otras cosas, me servirá para saber que no me estoy "hinchando" de escribir y hacer pruebas para "nada". Tu mismo!

Baja el listado con los ejemplos (seldir.zip 10.8 KB)

NOTA: Estos listados son los anteriores, para ver la nueva versión, pasate por este link.


10.- Más artículos sobre los objetos: publicados originalmente en VB Online edición USA. (14/Ago/98)

Pues eso, estos dos artículos me los publicaron en la edición norte americana de VB Online, en los meses de Mayo y Julio de este mismo año.

Pero no te preocupes, que no están en inglés; estos son los originales que le envié a mi colega Joe LeVasseur para que me los tradujera, aprovecho la ocasión para agradecerle públicamente las molestias que le haya podido producir el hacer de traductor.
¡Gracias Joe!

Aquí tienes los links a los dos artículos mencionados:

  1. Objetos en Visual Basic
  2. Objetos en Visual Basic: Piensa objetivamente

ir al índice del Guille