viernes, 30 de mayo de 2014

2da Parte Microsoft Visual Basic

Los métodos de la clase de acceso a la base de datos utilizarán el motor Jet para acceder a la base de datos, a través de las funciones que DAO proporciona.

Ejemplo resuelto

Utilizaremos la clase ClaFicha ya definida.


ModDefiniciones

'_______________________________________________________________________
' Ejemplo 6 del Curso de Visual Basic Avanzado
''  Archivo con el estándar de devolución de las funciones.
'_______________________________________________________________________

Option Explicit

Public Enum CodigoRetorno
    Error = 0
    Ok = 1
End Enum


FrmAgenda

'_______________________________________________________________________
' Ejemplo 6 del Curso de Visual Basic Avanzado
''  Interfaz del usuario con la base de datos, aquí están los métodos de mantenimiento de
' la base de datos.
'_______________________________________________________________________

Option Explicit

Dim ObjAgenda As New ClaAgenda
Dim IntNumFichaActual As Integer

Private Sub ButBorrarFicha_Click()
   
    ' Confirmamos el borrado
    If MsgBox("¿Desea eliminar la ficha actual?", vbYesNo, "Borrado de ficha") = vbNo Then
        Exit Sub
    End If
   
    ' Borramos la ficha actual
    ObjAgenda.BorrarFicha IntNumFichaActual
   
    ' Actualizamos el número de elementos de la barra de scroll
    If ObjAgenda.IntNumFichas > 0 Then
        HScroll1.Max = ObjAgenda.IntNumFichas - 1
    Else
        HScroll1.Max = 0
    End If
       
    ' Actualizamos la barra de scroll
    ActualizarFormulario
   
End Sub

Private Sub ButCrearFicha_Click()
   
    ' Creamos una nueva ficha con los datos introducidos por el
    ' usuario, y obtenemos el código de la ficha creada
    IntNumFichaActual = ObjAgenda.CrearFicha(TxtNombre, TxtApellidos, TxtDireccion, _
        TxtTelefono)
   
    ' Comprobamos si no ha podido insertar una nueva ficha, lo que
    ' sucederá si IntNumFichaActual=0
    If IntNumFichaActual = 0 Then
        ' Decrementamos el número de elementos de la barra de
        ' scroll
        HScroll1.Max = HScroll1.Max - 1
       
        ' Actualizamos el formulario
        ActualizarFormulario
        Exit Sub
    End If
   
    ' Actualizamos el título del frame
    Frame1.Caption = "Ficha " & IntNumFichaActual
   
    ' Deshabilitamos el botón de crear ficha y el frame
    ButCrearFicha.Enabled = False
    Frame1.Enabled = False
   
    ' Habilitamos los botones de nueva ficha y borrar
    ' ficha, así como la barra de scroll
    ButNuevaFicha.Enabled = True
    ButBorrarFicha.Enabled = True
    HScroll1.Enabled = True
End Sub

Private Sub ButNuevaFicha_Click()
    Dim IntNumFichas As Integer
   
    ' Actualizamos la barra de scroll
    IntNumFichas = ObjAgenda.IntNumFichas
    If IntNumFichas = 0 Then
        ' Con la primera ficha creada, habilitamos
        ' la barra de scroll
        HScroll1.Enabled = True
        Frame1.Enabled = True
    Else
        ' Establecemos el número de elementos de
        ' la barra de scroll
        HScroll1.Max = HScroll1.Max + 1
        HScroll1.Value = HScroll1.Max
    End If
   
    ' Habilitamos el botón de crear ficha y el frame
    ButCrearFicha.Enabled = True
    Frame1.Enabled = True
   
    ' Deshabilitamos los botones de nueva ficha y borrar
    ' ficha, así como la barra de scroll
    ButNuevaFicha.Enabled = False
    ButBorrarFicha.Enabled = False
    HScroll1.Enabled = False
End Sub

Private Sub Form_Load()
    Dim IntNumFichas As Integer
   
    ' Comprobamos si no hay datos en la agenda para
    ' deshabilitar las opciones
    IntNumFichas = ObjAgenda.IntNumFichas
    If IntNumFichas = 0 Then
        ' Deshabilitamos el frame, la barra de scroll,
        ' el botón de crear ficha y el botón de borrar ficha
        Frame1.Enabled = False
        HScroll1.Enabled = False
        ButCrearFicha.Enabled = False
        ButBorrarFicha.Enabled = False
       
        ' Establecemos los límites de la barra de scroll
        HScroll1.Max = 0
    Else
        ' Establecemos el máximo de la barra de scroll
        HScroll1.Max = IntNumFichas - 1
       
        ' Actualizamos el formulario
        ActualizarFormulario
    End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
    ' Eliminamos la agenda
    Set ObjAgenda = Nothing
End Sub

Private Sub HScroll1_Change()
    ' Actualizamos la barra de scroll
    ActualizarFormulario
End Sub

Private Function ActualizarFormulario()
    Dim ObjFicha As ClaFicha
   
    ' Comprobamos si no hay fichas o estamos creando una ficha nueva
    If HScroll1.Max = ObjAgenda.IntNumFichas Then
        ' Limpiamos los datos del frame
        TxtNombre = vbNullString
        TxtApellidos = vbNullString
        TxtDireccion = vbNullString
        TxtTelefono = vbNullString

        ' Actualizamos el título del frame
        Frame1.Caption = vbNullString
       
        ' Deshabilitamos el botón de borrado
        ButBorrarFicha.Enabled = False
    Else
        ' Obtenemos la ficha correspondiente de la agenda
        ' (buscamos por índice, no por código de ficha)
        Set ObjFicha = New ClaFicha
        Set ObjFicha = ObjAgenda.Ficha(HScroll1.Value + 1)
       
        ' Sacamos los datos de la ficha
        If Not ObjFicha Is Nothing Then
            TxtNombre = ObjFicha.StrNombre
            TxtApellidos = ObjFicha.StrApellidos
            TxtDireccion = ObjFicha.StrDireccion
            TxtTelefono = ObjFicha.StrTelefono
           
            ' Actualizamos el código de la ficha actual
            IntNumFichaActual = ObjFicha.IntNumFicha
           
            ' Eliminamos el objeto ficha creado
            Set ObjFicha = Nothing
           
            ' Actualizamos el frame
            Frame1.Caption = "Ficha " & IntNumFichaActual
        End If
    End If
End Function

ClaAgenda

'_______________________________________________________________________
' Ejemplo 6 del Curso de Visual Basic Avanzado
''  Métodos para acceder a los métodos de la clase, que serán los que tienen acceso a la
' base de datos.
'_______________________________________________________________________

Option Explicit

Private ObjAccesoBD As New ClaDAO_Jet   'Objeto de acceso a la base de datos

' Método para obtener el número de fichas de la agenda
Public Property Get IntNumFichas() As Integer
    IntNumFichas = ObjAccesoBD.NumFichas
End Property

' Método para crear una ficha nueva en la agenda
Public Function CrearFicha(ByVal StrNombre As String, _
    ByVal StrApellidos As String, ByVal StrDireccion As String, _
    ByVal StrTelefono As String) As Integer
    Static IntNumFicha As Integer
    Dim ObjFicha As ClaFicha
   
    ' Creamos un nuevo objeto de la clase ficha
    Set ObjFicha = New ClaFicha
   
    ' Establecemos las propiedades de la nueva ficha
    ObjFicha.StrNombre = StrNombre
    ObjFicha.StrApellidos = StrApellidos
    ObjFicha.StrDireccion = StrDireccion
    ObjFicha.StrTelefono = StrTelefono
   
    ' Insertamos la nueva ficha en la base de datos
    If ObjAccesoBD.GuardarFicha(ObjFicha, IntNumFicha) = Ok Then
        ' Devolvemos el código de la ficha creada
        CrearFicha = IntNumFicha
    Else
        ' Devolvemos 0, porque es un valor que nunca aparecerá
        ' en un campo autonumérico
        CrearFicha = 0
        MsgBox "Imposible agregar una nueva ficha"
    End If
   
    ' Eliminamos el objeto Ficha creado
    Set ObjFicha = Nothing
End Function

' Método para borrar una ficha de la agenda
Public Function BorrarFicha(ByVal IntNumFicha As Integer)
    Dim ObjFicha As ClaFicha
   
    If ObjAccesoBD.BorrarFicha(IntNumFicha) = Error Then
        MsgBox "Imposible borrar la ficha nº " & IntNumFicha
    End If

End Function

' Al eliminar la agenda, cerraremos la base de datos y
' eliminaremos el objeto de acceso a la base de datos
Private Sub Class_Terminate()
    If ObjAccesoBD.CerrarAgenda() = Error Then
        MsgBox "Imposible cerrar la agenda"
    End If
    Set ObjAccesoBD = Nothing
End Sub

' Método para obtener una ficha específica de la agenda, ya sea
' por índice o por número de ficha
'
' NOTA: la función crea un nuevo objeto Ficha, que habrá que
' eliminar en la función de llamada
Public Function Ficha(Optional ByVal IntIndice As Integer, _
    Optional ByVal IntNumFicha As Integer) As ClaFicha
    Dim ObjFicha As New ClaFicha
   
    ' Comprobamos si se busca por índice
    If Not IsMissing(IntIndice) Then
        If ObjAccesoBD.ObtenerFicha(ObjFicha, IntIndice) = Error Then
            MsgBox "Imposible obtener la ficha de la posición nº " & IntIndice
            Exit Function
        End If
    ElseIf Not IsMissing(IntNumFicha) Then
        If ObjAccesoBD.ObtenerFicha(ObjFicha, , IntNumFicha) = Error Then
            MsgBox "Imposible obtener la ficha nº " & IntNumFicha
            Exit Function
        End If
    End If
    Set Ficha = ObjFicha
    Set ObjFicha = Nothing
End Function

ClaDAO_Jet

'_______________________________________________________________________
' Ejemplo 6 del Curso de Visual Basic Avanzado
'' Clase de acceso a bases de datos utilizando DAO y
' funciones EXCLUSIVAS de Jet
'_______________________________________________________________________

Option Explicit

Private VarBD As Database
Private VarPathBD As String
Private Const ConNombreTabla = "Agenda"

Public Property Get NumFichas() As Integer
    On Error GoTo TratamientoError
    Dim RsetTabla As Recordset
   
    ' Abrimos un recordset de tipo dbOpentable
    Set RsetTabla = VarBD.OpenRecordset(ConNombreTabla, dbOpenTable)

    ' Obtenemos el número de elementos
    NumFichas = RsetTabla.RecordCount

    ' Cerramos el Recordset
    RsetTabla.Close
   
    Exit Property
   
TratamientoError:
    NumFichas = -1
End Property

Public Function AbrirAgenda() As CodigoRetorno
    On Error GoTo TratamientoError
   
    ' Abrimos la base de datos, utilizando el Workspace
    ' por defecto
    Set VarBD = DBEngine.Workspaces(0).OpenDatabase(VarPathBD)
   
    AbrirAgenda = Ok
    Exit Function
   
TratamientoError:
    AbrirAgenda = Error
End Function

Public Function CerrarAgenda() As CodigoRetorno
    On Error GoTo TratamientoError
   
    ' Cerramos la base de datos
    VarBD.Close
    
    CerrarAgenda = Ok
    Exit Function
   
TratamientoError:
    CerrarAgenda = Error
End Function

Public Function GuardarFicha(ByVal ObjFicha As ClaFicha, _
    Optional ByRef IntNumFicha As Integer) As CodigoRetorno
    On Error GoTo TratamientoError
    Dim RsetTabla As Recordset
   
    ' Abrimos un recordset de tipo dbOpenTable (sólo funciona
    ' en Jet)
    Set RsetTabla = VarBD.OpenRecordset(ConNombreTabla, dbOpenTable)

    With RsetTabla
        ' Añadimos una nueva ficha
        .AddNew
        !Nombre = ObjFicha.StrNombre
        !Apellidos = ObjFicha.StrApellidos
        !Direccion = ObjFicha.StrDireccion
        !Telefono = ObjFicha.StrTelefono
       
        ' Obtenemos el campo NumFicha
        IntNumFicha = !NumFicha
        .Update
    End With

    ' Cerramos el recordset
    RsetTabla.Close
   
    GuardarFicha = Ok
    Exit Function
   
TratamientoError:
    GuardarFicha = Error
End Function

Public Function ObtenerFicha(ByRef ObjFicha As ClaFicha, _
    Optional ByVal IntIndice As Integer, _
    Optional ByVal IntNumFicha As Integer) As CodigoRetorno
    On Error GoTo TratamientoError
    Dim RsetTabla As Recordset
   
    ' Comprobamos si hay que buscar por índice
    If Not IsMissing(IntIndice) Then
        ' Abrimos un recordset de tipo dbOpenForwardOnly, para poder
        ' movernos con MoveNext (lo abrimos ForwardOnly porque sólo
        ' necesitamos desplazarnos hacia delante)
        Set RsetTabla = VarBD.OpenRecordset(ConNombreTabla, dbOpenForwardOnly)
      
        ' Recorremos el Recordset hasta encontrar la ficha indicada
        With RsetTabla
            Do Until .EOF
                If .RecordCount = IntIndice Then
                    Exit Do
                End If
                .MoveNext
            Loop
        End With
    ' Comprobamos si hay que buscar por número de ficha
    ElseIf Not IsMissing(IntNumFicha) Then
        ' Abrimos un recordset de tipo dbOpenSnapshot, para poder
        ' buscar con FindFirst
        Set RsetTabla = VarBD.OpenRecordset(ConNombreTabla, dbOpenSnapshot)
        With RsetTabla
            ' Llenamos el Recordset
            .MoveLast
           
            ' Encontramos el primer registro con ese número
            .FindFirst "NumFicha=" & IntNumFicha
        End With
    End If
          
    ' Comprobamos si la hemos encontrado, o hemos llegado al
    ' final del Recordset sin encontrarla
    With RsetTabla
        If .EOF Or .NoMatch = True Then
            ObtenerFicha = Error
        Else
            ' Guardamos los datos obtenidos
            ObjFicha.StrNombre = !Nombre
            ObjFicha.StrApellidos = !Apellidos
            ObjFicha.StrDireccion = !Direccion
            ObjFicha.StrTelefono = !Telefono
            ObjFicha.IntNumFicha = !NumFicha
            ObtenerFicha = Ok
        End If
    End With
   
    ' Cerramos el Recordset
    RsetTabla.Close
   
    Exit Function
   
TratamientoError:
    ObtenerFicha = Error
End Function

Public Function BorrarFicha(ByVal IntNumFicha) As CodigoRetorno
    On Error GoTo TratamientoError
   
    Dim RsetTabla As Recordset
   
    ' Abrimos un recordset de tipo dbOpenDynaset, para poder
    ' buscar con FindFirst y borrar con Delete
    Set RsetTabla = VarBD.OpenRecordset(ConNombreTabla, dbOpenDynaset)

    ' Buscamos la ficha correspondiente
    With RsetTabla
        ' Llenamos el Recordset
        .MoveLast
       
        ' Encontramos el primer registro con ese número
        .FindFirst "NumFicha=" & IntNumFicha
       
        ' Comprobamos si hemos encontrado algún registro
        If .NoMatch Then
            BorrarFicha = Error
        Else
            ' Borramos la ficha obtenida
            .Delete
            BorrarFicha = Ok
        End If
       
        ' Cerramos el Recordset
        .Close
    End With
   
    BorrarFicha = Ok
    Exit Function
   
TratamientoError:
    BorrarFicha = Error
End Function

Private Sub Class_Initialize()
     ' Establecemos el path donde está el archivo de
     ' la base de datos
     VarPathBD = App.Path & "\bdAgenda.mdb"
    
     ' Abrirmos la base de datos
     AbrirAgenda
End Sub

7. Acceso a bases de datos (DAO y ODBCDirect)

Introducción

Hasta ahora hemos explicado cómo podemos acceder a una base de datos en local a través del motor Jet. En este capítulo y en el siguiente mostraremos cómo se puede acceder a un servidor de bases de datos.
Al trabajar con bases de datos localizadas en un servidor podemos diferenciar dos métodos de trabajo, dependiendo de la localización del motor de base de datos:
·    cliente-servidor: el motor de base de datos está en el servidor.
·    remota: el motor está en el cliente


Normalmente se utiliza el modelo cliente-servidor para acceder a bases de datos localizadas en un servidor, ya que aporta bastantes ventajas:
·      Operaciones más fiables y robustas, puesto que existe un único servidor de base de datos que interactúa con todos los clientes.
·      Notable aumento del rendimiento de algunas operaciones, especialmente cuando las estaciones de trabajo de los usuarios son equipos de gama baja.
·      Reducción del tráfico de la red gracias a una transmisión de datos más eficiente. Sólo se transfieren los datos que la aplicación necesita.
·      Características críticas como los registros de transacciones, las capacidades de copia de seguridad complejas, las matrices de discos redundantes y las herramientas de recuperación de fallos.

La forma más común de acceder a un servidor de bases de datos es a través de ODBC. ODBC es una capa intermedia entre las aplicaciones que se ejecutan en el cliente y el servidor de bases de datos. El controlador ODBC del cliente recibe peticiones de la aplicación, las traduce a peticiones ODBC y las envía al servidor. El servidor responde al controlador ODBC del cliente, y éste pasa la respuesta a la aplicación. La ventaja de usar ODBC es la independencia del SGBD (Sistema Gestor de Bases de Datos) utilizado, pudiendo cambiar éste realizando cambios mínimos en el código de la aplicación.

Conocimientos teóricos

Opciones de utilización de ODBC con DAO

Existen dos formas de acceder a orígenes de datos remotos utilizando DAO:

·       Jet-ODBC: utiliza el motor Jet para acceder al SGBD.

·       ODBCDirect: no utiliza el motor Jet. En realidad no es más que un interfaz DAO con las bibliotecas de RDO, lo que permite un rendimiento mayor que al utilizar Jet-ODBC con la pega de que no puede utilizar todas las características de Jet.

Estructura de una aplicación de bases de datos en Visual basic con DAO y ODBCDirect

Una aplicación de bases de datos en Visual Basic que utiliza DAO y ODBCDirect se comunica con el SGBD a través del controlador ODBC correspondiente, como se muestra en la figura:


Programación con DAO y ODBCDirect

En las siguientes líneas vamos a comentar cómo programar una aplicación que acceda a una base de datos a través de DAO y ODBCDirect, explicando las acciones más comunes que deberemos implementar. Al igual que en el capítulo anterior, existen muchas posibilidades que no comentaremos.

Si programamos con Visual Basic 5.0, es necesario incluir en el proyecto una referencia a Microsoft DAO 3.5 Object Library.

Como ejemplo utilizaremos la base de datos descrita en el capítulo anterior. Para acceder a dicha base de datos necesitamos crear un DSN (Data Source Name) desde Panel de ControlàODBC 32 bits, que llamaremos MiDSN.
Variables utilizadas:

En los siguientes ejemplos utilizaremos dos variables, VarWks y VarConexion, para definir el espacio de trabajo y la conexión con el SGBD, respectivamente. Además, para establecer la cadena de conexión ODBC utilizaremos la constante ConCadenaConexion:

Dim VarWks As Workspace
Dim VarConexion As Connection
Dim Const ConCadenaConexion = "ODBC;DATABASE=MiBD;UID=;PWD=;DSN=MiDSN"

Conexión con el SGBD:

Primero necesitamos crear un espacio de trabajo específico para ODBC utilizando la sentencia CreateWorkspace. Una vez creado, estableceremos la conexión con el SGBD utilizando el método OpenConnection de dicho espacio de trabajo.

Set VarWks = CreateWorkspace("", "", "", dbUseODBC)
Set VarConexion = VarWks.OpenConnection("", , , ConCadenaConexion)

Utilización de Recordsets:

Los Recordsets creados con ODBCDirect son, en realidad, Resultsets de RDO. La forma de trabajar con estos Recordsets será crear la consulta SQL que genere el conjunto de registros buscado y pasársela al método OpenRecordset de la conexión creada.

Ejemplos de utilización de Recordsets:

Para sacar en la pantalla Debug los datos de cada registro de la tabla “MiTabla” utilizaremos el método OpenRecordset de la conexión, pasándole la consulta SQL que obtiene todos los datos de la tabla. Para acceder a cada uno de los campos del registro actual, utilizaremos la colección Fields del Recordset, que contiene todos los campos del registro actual.

Dim RsetDatos As Recordset

Set RsetDatos = VarConexion.OpenRecordset("select * from MiTabla")
      
With RsetDatos
Do While Not .EOF
            Debug.Print “DNI: ” & .Fields(0).Value & vbCRLF & _
                                   “Nombre: “ & .Fields(1).Value & vbCRLF & _
                                   “Apellidos: “ & .Fields(2).Value
            .MoveNext
            Loop
End With

Sentencias que no devuelven datos:

Cuando queremos realizar una acción sobre la base de datos que no devuelve ningún conjunto de registros (insert, update, delete, etc) utilizaremos el método Execute de la conexión, pasándole la sentencia SQL a ejecutar. Para determinar si la acción se ha realizado correctamente, consultaremos la propiedad RowsAfected de la conexión.

Ejemplos de ejecución de sentencias que no devuelven datos:

Por ejemplo, si queremos eliminar el registro con DNI=”12345678”:

VarConexion.Execute "delete from MiTabla where DNI=’12345678’”
If VarConexion.RecordsAffected = 1 Then
        MsgBox “Registro borrado”
Else
        MsgBox “Error al borrar el registro”
End If


Ejemplo propuesto

Objetivo

El objetivo de este ejemplo es cambiar la capa de acceso a la base de datos desarrollada en el ejemplo anterior para que utilice DAO y ODBCDirect en lugar de Jet para acceder a la misma base de datos, pero a través de ODBC.

Desarrollo del ejemplo

Crearemos un DSN para la base de datos (se llamará “Agenda”), y cambiaremos la clase ClaDAO_Jet por otra clase ClaDAO_ODBCDirect, que constará de los mismos métodos, pero utilizará ODBCDirect para acceder a través de ODBC a la base de datos.

Una vez creada esta clase, el único cambio a realizar en la aplicación es sustituir en la clase agenda (ClaAgenda) la sentencia

Private ObjAccesoBD As New ClaDAO_Jet

por
Private ObjAccesoBD As New ClaDAO_ODBCDirect

Ejemplo resuelto


Utilizaremos el formulario FrmAgenda, el módulo ModDefiniciones y las clase ClaFicha y ClaAgenda (con la modificación comentada).



8. Acceso a bases de datos (RDO)

Introducción

Como vimos en el ejemplo anterior, el acceso a servidores de bases de datos suele realizarse a través de ODBC. RDO (Remote Data Objects) es una fina capa de objetos sobre el API de ODBC, que permite utilizar llamadas al API de ODBC de forma sencilla.
RDO está diseñado para aprovechar al máximo la potencia de servidores de bases de datos inteligentes, especialmente SQL Server. Tiene multitud de opciones que permiten incrementar el rendimiento de las aplicaciones que utilicen RDO, por eso es el mas utilizado en cliente-servidor.

En realidad ya conocemos a grandes rasgos cómo funciona RDO, puesto que ODBCDirect es sólo una forma de utilizar RDO desde DAO.

Conocimientos teóricos

El modelo de objetos RDO


Los objetos y las colecciones de RDO proporcionan un marco para utilizar código con el fin de crear y manipular componentes de un sistema de base de datos remota de ODBC.
Al igual que DAO, las clases de los objetos de acceso a datos se organizan en una jerarquía, en la que la mayoría de las clases pertenecen a una clase de colección, que a su vez pertenece a otra clase en la jerarquía. La figura de la derecha muestra la jerarquía completa de RDO.

Estructura de una aplicación de bases de datos en Visual basic con RDO


La estructura de una aplicación de bases de datos en Visual Basic que utiliza RDO es casi idéntica a la de la aplicaciones que utilizan DAO con ODBCDirect. Los modelos DAO y RDO son equivalentes en cuanto a objetos, la diferencia es que están programados internamente para trabajar a través de ODBC o en local, pero para el programador, conociendo la manera de programar con uno, le debe ser fácil trabajar con el otro. En la siguiente tabla se recogen las correspondencias entre los objetos utilizados en RDO y sus objetos equivalentes en DAO:


Programación con RDO

En las siguientes líneas vamos a comentar cómo programar una aplicación que acceda a una base de datos a través de RDO, explicando las acciones más comunes que deberemos implementar. En los dos capítulos anteriores comentamos que existen muchas posibilidades que no hemos tratado. Esto se aplica especialmente a RDO, pues proporciona muchas posibilidades en función del SGBD con el que trabajemos (sobre todo con SQL Server), como consultas asíncronas, cursores del lado del servidor, ejecución de procedimientos en el servidor, etc.

Si programamos con Visual Basic 5.0, es necesario incluir en el proyecto una referencia a Microsoft Remote Data Object 2.0.

Utilizaremos la misma base de datos descrita en el capítulo anterior, así como el mismo DSN.

Variables utilizadas:

En los siguientes ejemplos utilizaremos una variable VarConexion que representará  la conexión con el SGBD y la misma constante para la cadena de conexión ODBC que utilizamos en el capítulo anterior:

Dim VarConexion As New rdoConnection
Dim Const ConCadenaConexion = "ODBC;DATABASE=MiBD;UID=;PWD=;DSN=MiDSN"

Conexión con el SGBD:

Estableceremos la conexión con el SGBD utilizando el método EstablishConnection de la conexión. Antes de abrir la conexión, tenemos que especificar el tipo de cursor a utilizar utilizando la propiedad CursorDriver de la conexión (especificaremos rdUseOdbc), así como la cadena de conexión usando la propiedad Connect:

With VarConexion
        .CursorDriver = rdUseOdbc
        .Connect = ConCadenaConexion
        .EstablishConnection rdDriverNoPrompt
End With

Utilización de Resultsets:

Como ya dijimos en el capítulo anterior, los Recordsets creados con ODBCDirect son en realidad Resultsets de RDO. Por lo tanto, la forma de trabajar con estos Resultsets será muy semejante a la que ya conocemos. Para abrir un Resultset, crearemos la consulta SQL que genere el conjunto de registros buscado y utilizaremos el método OpenResultset de la conexión para lanzarla.

Ejemplo de utilización de Resultsets:

Para sacar en la pantalla Debug los datos de cada registro de la tabla “MiTabla” utilizaremos el método OpenResultset de la conexión, pasándole la consulta SQL que obtiene todos los datos de la tabla. Para acceder a cada uno de los campos del registro actual, utilizaremos la colección rdoColumns del Resultset, que contiene todos los campos del registro actual.

Dim RsetDatos As rdoResultset

Set RsetDatos = VarConexion.OpenResultset("select * from MiTabla")
      
With RsetDatos
Do While Not .EOF
            Debug.Print “DNI: ” & .rdoColumns(0).Value & vbCRLF & _
                                   “Nombre: “ & . rdoColumns(1).Value & vbCRLF & _
                                   “Apellidos: “ & . rdoColumns(2).Value
            .MoveNext
            Loop
End With

Sentencias que no devuelven datos:

La forma de ejecutar sentencias que no devuelven datos en RDO es idéntica a la que ya hemos visto con ODBCDirect. 

Ejemplo propuesto

Objetivo


El objetivo de este ejemplo es cambiar la capa de acceso a la base de datos desarrollada en el ejemplo anterior para que utilice RDO  en lugar de DAO y ODBCDirect para acceder a la misma base de datos a través de ODBC.

Desarrollo del ejemplo


Utilizaremos el DSN creado en el ejemplo anterior y cambiaremos la clase ClaDAO_ODBCDirect por una nueva clase ClaRDO, que constará de los mismo métodos pero utilizará RDO para acceder a través de ODBC a la base de datos.

Una vez creada esta clase, el único cambio a realizar en la aplicación es sustituir en la clase agenda (ClaAgenda) la sentencia

Private ObjAccesoBD As New ClaDAO_ODBCDirect

por

Private ObjAccesoBD As New ClaRDO

Ejemplo resuelto

Utilizaremos el formulario FrmAgenda, el módulo ModDefiniciones y las clase ClaFicha y ClaAgenda (con la modificación comentada).

ClaRDO

'_______________________________________________________________________
' Ejemplo 8 del Curso de Visual Basic Avanzado
'' Clase de acceso a bases de datos utilizando RDO
'_______________________________________________________________________

Option Explicit

Private VarConexion As New rdoConnection
Private Const ConCadenaConexion = "ODBC;DATABASE=Agenda;UID=;PWD=;DSN=Agenda"

Public Property Get NumFichas() As Integer
    On Error GoTo TratamientoError
    Dim RsetDatos As rdoResultset
    Dim CadenaConexion As String
   
    ' Construimos la sentencia SQL
    CadenaConexion = "select count(*) from agenda"
   
    ' Ejecutamos la consulta
    Set RsetDatos = VarConexion.OpenResultset(CadenaConexion)
   
    ' Obtenemos el número de fichas
    NumFichas = RsetDatos.rdoColumns(0).Value
   
    ' Cerramos el rdoresultset
    RsetDatos.Close

    Exit Property
   
TratamientoError:
    NumFichas = -1
End Property

Public Function AbrirAgenda() As CodigoRetorno
    On Error GoTo TratamientoError
   
    ' Abrimos la conexión utilizando la cadena
    ' de conexión predefinida
    With VarConexion
        .CursorDriver = rdUseOdbc
        .Connect = ConCadenaConexion
        .EstablishConnection rdDriverNoPrompt
    End With
   
    AbrirAgenda = Ok
    Exit Function
   
TratamientoError:
    AbrirAgenda = Error
End Function

Public Function CerrarAgenda() As CodigoRetorno
    On Error GoTo TratamientoError
   
    ' Cerramos la base de datos
    VarConexion.Close
   
    CerrarAgenda = Ok
    Exit Function
   
TratamientoError:
    CerrarAgenda = Error
End Function

Public Function GuardarFicha(ByVal ObjFicha As ClaFicha, _
    Optional ByRef IntNumFicha As Integer) As CodigoRetorno
    On Error GoTo TratamientoError
    Dim RsetDatos As rdoResultset
    Dim CadenaSQL As String
   
    ' Construimos la sentencia SQL
    CadenaSQL = "insert into agenda " & _
        "(Nombre, Apellidos, Direccion, Telefono) " & _
        "values (" & _
        "'" & ObjFicha.StrNombre & "'," & _
        "'" & ObjFicha.StrApellidos & "'," & _
        "'" & ObjFicha.StrDireccion & "'," & _
        "'" & ObjFicha.StrTelefono & "')"
   
    ' Ejecutamos la consulta
    VarConexion.Execute CadenaSQL
   
    ' Comprobamos si se ha realizado la inserción
    If VarConexion.RowsAffected <> 1 Then
        GuardarFicha = Error
        Exit Function
    End If
   
    ' Obtenemos el número de ficha asignado por el servidor
    ' NOTA: en realidad, dado que el campo clave es el número
    '       de ficha, este sentencia puede devolver más de un
    '       registro, pero no lo tendremos en cuenta
    CadenaSQL = "select numficha from agenda " & _
        "where " & _
        "Nombre='" & ObjFicha.StrNombre & "' AND " & _
        "Apellidos='" & ObjFicha.StrApellidos & "' AND " & _
        "Direccion='" & ObjFicha.StrDireccion & "' AND " & _
        "Telefono='" & ObjFicha.StrTelefono & "'"
   
    Set RsetDatos = VarConexion.OpenResultset(CadenaSQL)
   
    If Not RsetDatos.EOF Then
        ' Obtenemos el código de la nueva ficha creada
        IntNumFicha = RsetDatos.rdoColumns(0).Value
        GuardarFicha = Ok
    Else
        GuardarFicha = Error
    End If
   
    ' Cerramos el rdoresultset
    RsetDatos.Close
    Exit Function
   
TratamientoError:
    GuardarFicha = Error
End Function

Public Function ObtenerFicha(ByRef ObjFicha As ClaFicha, _
    Optional ByVal IntIndice As Integer, _
    Optional ByVal IntNumFicha As Integer) As CodigoRetorno
    On Error GoTo TratamientoError
    Dim RsetDatos As rdoResultset
    Dim CadenaConexion As String
    Dim IntFichaActual As Integer
   
    ' Comprobamos si hay que buscar por índice
    If Not IsMissing(IntIndice) Then
        ' Abrimos un rdoresultset seleccionando todos los
        ' registros de la tabla
        CadenaConexion = "select * from agenda order by numficha"
        Set RsetDatos = VarConexion.OpenResultset(CadenaConexion)
      
        ' Recorremos el rdoresultset hasta encontrar la ficha indicada
        With RsetDatos
            Do While Not .EOF
                IntFichaActual = IntFichaActual + 1
                If IntFichaActual = IntIndice Then
                    Exit Do
                End If
                .MoveNext
            Loop
        End With
    ' Comprobamos si hay que buscar por número de ficha
    ElseIf Not IsMissing(IntNumFicha) Then
        ' Abrimos un rdoresultset seleccionando el registro
        ' directamente
        CadenaConexion = "select * from agenda " & _
            "where numficha=" & IntNumFicha & _
            "order by numficha"
        Set RsetDatos = VarConexion.OpenResultset(CadenaConexion)
    End If
          
    ' Comprobamos si la hemos encontrado, o hemos llegado al
    ' final del rdoresultset sin encontrarla
    With RsetDatos
        If .EOF Then
            ObtenerFicha = Error
        Else
            ' Guardamos los datos obtenidos
            ObjFicha.IntNumFicha = .rdoColumns(0).Value
            ObjFicha.StrNombre = .rdoColumns(1).Value
            ObjFicha.StrApellidos = .rdoColumns(2).Value
            ObjFicha.StrDireccion = .rdoColumns(3).Value
            ObjFicha.StrTelefono = .rdoColumns(4).Value
            ObtenerFicha = Ok
        End If
    End With
   
    ' Cerramos el rdoresultset
    RsetDatos.Close
   
    Exit Function
   
TratamientoError:
    ObtenerFicha = Error
End Function

Public Function BorrarFicha(ByVal IntNumFicha) As CodigoRetorno
    On Error GoTo TratamientoError
   
    Dim CadenaSQL As String
   
    ' Construimos la sentencia SQL
    CadenaSQL = "delete from agenda " & _
        "where numficha=" & IntNumFicha
   
    ' Ejecutamos la sentencia SQL
    VarConexion.Execute CadenaSQL
   
    ' Comprobamos cuantas filas han sido afectadas
    If VarConexion.RowsAffected = 1 Then
        BorrarFicha = Ok
    Else
        BorrarFicha = Error
    End If
    Exit Function
   
TratamientoError:
    BorrarFicha = Error
End Function

Private Sub Class_Initialize()
     ' Conectamos con la base de datos
     AbrirAgenda
End Sub

9. El registro de Windows

Introducción


El objetivo de este capítulo es introducir al lector en un tema complejo como es el tratamiento del registro de Windows. Se explicará cómo se estructura el registro de Windows, cómo podemos obtener valores almacenados en él y cómo crear nuevas claves y valores de diferentes tipos.
Pero todos los accesos al registro deben ser controlados, ya que del registro depende el correcto funcionamiento del S.O., por lo que cualquier cambio podría repercutir en el funcionamiento correcto del sistema.

El acceso al registro de Windows implica la utilización de las correspondientes funciones que proporciona el API de Windows, por lo que en este capítulo se explicará someramente qué es y cómo se utiliza dicho API (Application Programming Interface, o Interfaz para la Programación de Aplicaciones).

Conocimientos teóricos

Qué es el registro de Windows


En Windows 3.1 y en las versiones anteriores de Windows, la configuración de los programas se almacenaban normalmente en archivos .ini. En Windows NT, en Windows 95 y en las versiones posteriores de Windows, la configuración de los programas se almacena en el registro del sistema, todas las instalaciones realizadas, versiones de librerías, controles... quedan reflejados en el registro.

Cómo acceder al registro de Windows





Podemos acceder manualmente a los valores almacenados en el registro de Windows utilizando la aplicación Regedit:

También podemos acceder al registro de Windows desde Visual Basic utilizando las funciones que proporciona el API de Windows que veremos mas adelante, pero antes profundizaremos en la definición del registro.

Estructura del registro de Windows

Los elementos almacenados en el registro de Windows se dividen en dos tipos:
·       claves, que se estructuran en forma de directorios.
·       valores de cadena, binarios o DWORD.

Para acceder al valor contenido en una clave es necesario conocer el “path” completo de ésta (por ejemplo, para conocer el nº de versión de Windows hay que consultar la clave 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\VersionNumber).

Cómo utilizar el API de Windows


Para poder utilizar las funciones que proporciona el API de Windows es necesario declarar las funciones que éste implementa, así como los posibles tipos de datos y constantes que utilicen. Podemos obtener esta información utilizando el Visor de texto API que proporciona Visual Basic, abriendo el archivo Win32api.txt que estará en el directorio Winapi de la instalación de Visual Basic. Esta aplicación, como se ve en la figura, nos permite conocer el nombre de las funciones que implementa el API de Windows, así como la forma de declararlas, los argumentos que utilizan, los valores de retorno, etc.

Por ejemplo, si quisiéramos utilizar la función RegOpenKey, que nos permite abrir una clave del registro, buscaríamos el nombre de la función en el cuadro de lista superior, haríamos doble click sobre el nombre y copiaríamos la declaración en nuestro código de Visual Basic (en realidad, para utilizar esta función necesitamos obtener también ciertas constantes que indican las ramas del registro donde vamos a buscar, entre otras).



Pero el problema que tienen los visores API es que no proporcionan una breve explicación del contenido de el API seleccionada, por lo que muchas veces hay que usar la intuición para encontrar una que nos solucione el problema.

En el siguiente cuadro se muestran algunas de las funciones que proporciona el API de Windows para acceso al registro y su correspondiente declaración:

Para poder utilizar estas funciones es necesario declarar también varias constantes, que se especifican en el siguiente cuadroÄ:

Apertura de una clave existente:

Para abrir una clave existente debemos especificar la rama donde se encuentra (HKEY_CLASSES_ROOT, etc) y el “path” restante de la clave que queremos abrir. Una vez abierta, nos devolverá un valor de tipo long que identifica a esa clave, y nos permitirá trabajar con ella. Utilizaremos la función RegOpenKey, controlando si el valor de retorno es ERROR_SUCCESS, lo que indicaría que todo ha ido bien.

Por ejemplo, si queremos abrir la clave HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft utilizaríamos el siguiente código:

Dim hKey As Long

If OSRegOpenKey(HKEY_LOCAL_MACHINE, “SOFTWARE\Microsoft”, hKey) =
ERROR_SUCCESS Then
MsgBox “Clave abierta”
Else
MsgBox “Error al abrir la clave”
End If



Ä Existen más constantes de este tipo, pero sólo mencionamos las necesarias para el desarrollo del ejemplo correspondiente a este capítulo



Cerrado de una clave abierta:

Para cerrar una clave abierta, utilizaremos la función RegCloseKey, pasándole el identificador de la clave abierta. También en este caso debemos comprobar si el valor devuelto por la función es ERROR_SUCCESS. Por ejemplo, para cerrar la clave abierta en el ejemplo anterior, utilizaríamos el siguiente fragmento de código:

If OSRegCloseKey(hKey) = ERROR_SUCCESS Then
        MsgBox “Clave cerrada”
Else
        MsgBox “Error al cerrar la clave”
End If


Creación de una clave nueva:

Para crear una clave nueva en el registro utilizaremos la función RegCreateKey. Esta función creará la clave nueva (o la abrirá, si existe) y nos proporcionará el identificador de la clave recién creda/abierta. Como en los casos anteriores, hemos de comprobar si devuelve ERROR_SUCCESS. Por ejemplo, para crear la clave HKEY_LOCAL_MACHINE\SOFTWARE\MiClave utilizaríamos la siguiente sentencia:

If OSRegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\MiClave", hKeyNueva) =
ERROR_SUCCESS then
MsgBox “Clave creada”
Else
MsgBox “Error al crear la clave”
End If

Establecimiento del valor de una clave

Para establecer el valor de una clave existente utilizaremos la función RegSetValueEx, a la que pasaremos el identificador de la clave abierta, el nombre que vamos a darle a ese valor (si no se especifica, se entenderá que es el valor por defecto de dicha clave) y el propio valor. Es necesario concretar qué tipo de valor es (una cadena (REG_SZ), un valor binario (REG_BINARY) o un valor numérico (REG_DWORD)) y qué longitud tiene.

Por ejemplo, una vez creada la clave anterior, crearemos un valor nuevo dentro de ella que se llamará MiValor y contendrá la cadena “Esto es una prueba”:

If OSRegSetValueEx(hKeyNueva, “MiValor”, 0&, REG_SZ, “Esto es una prueba”,
Len(“Esto es una prueba”) + 1) = ERROR_SUCCESS then
            MsgBox “Valor creado”
Else
            MsgBox “Error al crear el valor”
End If

Dentro de la misma clave, crearemos un nuevo valor llamado MiNuevoValor, que contendrá el número 17 (como vamos a crearlo como REG_DWORD, el tamaño es 4):

If OSRegSetValueEx(hKeyNueva, “MiNuevoValor”, 0&, REG_DWORD, 17, 4) =
ERROR_SUCCESS then
            MsgBox “Valor creado”
Else
            MsgBox “Error al crear el valor”
End If


Obtención de alguno de los valores de una clave:

Para obtener alguno de los valores asociados a una clave utilizaremos la función RegQueryValueEx, a la que pasaremos el identificador de la clave abierta, el nombre del valor que queremos obtener (si no se especifica, se entenderá que es el valor por defecto de dicha clave), una variable donde se guardará el tipo del valor recogido, un buffer donde se guardará el valor recogido y una variable donde se guardará el tamaño del valor recogido.

Como es posible obtener valores de tipos y tamaños diferentes, esta función se llamará dos veces: la primera con un buffer vacío (0&) para obtener el tipo y el tamaño del valor; la segunda con el buffer correspondiente a este tipo y de tamaño correcto (¡OJO!: en el caso de REG_SZ debemos reservar expresamente espacio de memoria). Una vez obtenido el valor, hay que coger sólo el tamaño especificado del valor.

Por ejemplo, si queremos obtener el valor MiValor de la clave anterior, utilizaremos la siguiente sentencia (¡OJO!: el ByVal es importante)

Dim lValueType as Long
Dim StrBuf As String
Dim lDataBufSize As Long

If OSRegQueryValueEx(hKeyNueva, “MiValor”, 0&, lValueType,  ByVal 0&, lDataBufSize) =
ERROR_SUCCESS Then
            If lValueType = REG_SZ Then
                        StrBuf = String(lDataBufSize, " ")
            If OSRegQueryValueEx(hKey, strValueName, 0&, 0&, ByVal StrBuf,
                               lDataBufSize) = ERROR_SUCCESS Then
                                      RegQueryStringValue = True
                                               StrData = Left(StrBuf, lDataBufSize - 1)
                                   End If
            End If
End If

Si queremos obtener el valor MiNuevoValor de la clave anterior, utilizaremos la sentencia:

Dim lValueType As Long
Dim lBuf As Long
Dim lDataBufSize As Long
   
lDataBufSize = 4
If OSRegQueryValueEx(hKeyNueva, “MiNuevoValor”, 0&, lValueType, lBuf, lDataBufSize) =
ERROR_SUCCESS Then
                        If lValueType = REG_DWORD Then
lData = lBuf
                        End If
End If

Borrado de una clave:

Para borrar una clave existente utilizaremos la funcion RegDeleteKey, a la que pasaremos el identificador de la clave abierta y la subclave que queremos borrar. Por ejemplo, para borrar la clave HKEY_LOCAL_MACHINE\SOFTWARE\MiClave y todo lo que a partir de ella hemos creado, utilizaríamos la sentencia:

If OSRegOpenKey(HKEY_LOCAL_MACHINE, “SOFTWARE”, hKey) =
ERROR_SUCCESS Then
If OSRegDeleteKey(hKey, "MiClave") = ERROR_SUCCESS then
             MsgBox “Clave borrada”
End If
End If

Ejemplo propuesto

Objetivo


El objetivo de este ejemplo es crear un formulario de inicio de la aplicación que hemos desarrollado, de forma que nos permita 10 usos y luego no nos deje ejecutar más la aplicación, a no ser que la registremos. Para controlar esto utilizaremos el registro de Windows, en el llevaremos el control de veces que se ha ejecutado la aplicación y guardaremos el registro de la aplicación, tal y como hacen la mayoría de las aplicaciones del mercado.

Desarrollo del ejemplo


Crearemos un módulo ModRegistro que proporcione las siguientes funciones:

·       Function RegCreateKey(ByVal hKey As Long, ByVal lpszKey As String, phkResult As Long) As Boolean

·       Function RegSetStringValue(ByVal hKey As Long, ByVal strValueName As String, ByVal StrData As String) As Boolean

·       Function RegSetNumericValue(ByVal hKey As Long, ByVal strValueName As String, ByVal lData As Long, Optional ByVal fLog) As Boolean

·       Function RegOpenKey(ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Boolean

·       Function RegDeleteKey(ByVal hKey As Long, ByVal lpszSubKey As String) As Boolean

·       Function RegCloseKey(ByVal hKey As Long) As Boolean

·       Function RegQueryStringValue(ByVal hKey As Long, ByVal strValueName As String, StrData As String) As Boolean

·       Function RegQueryNumericValue(ByVal hKey As Long, ByVal strValueName As String, lData As Long) As Boolean

Estas funciones utilizaran sus correspondientes funciones del API de Windows, tal y como se ha explicado antes en este capítulo.

Crearemos un formulario FrmPresentacion de inicio de la aplicación como el que aparece en la siguiente figura:


Cada vez que se ejecute la aplicación se lanzararía este formulario (para este ejemplo no habrá aplicación que lanzar: tanto si la aplicación se registra como si es está en período de prueba o se supera éste, el formulario se descargará y nada más).

La primera vez que se ejecute la aplicación, este formulario creará una clave HKEY_LOCAL_MACHINE\SOFTWARE\Agenda en el registro, y en esta clave dos valores: MaxIntentos=10 y NumIntentos=1. Actualizará el caption del label inferior para mostrar el número máximo de ejecuciones permitidas y el número actual de ejecuciones.

Cada vez que se ejecute la aplicación se incrementará NumIntentos y se comprobará si es menor que MaxIntentos, de forma que si se llega al número máximo de intentos no se permita la ejecución del programa. En cada caso se actualizará el caption.

En todo momento se permitirá registrar la aplicación, en cuyo caso se lanzará un formulario como el siguiente:


Al pulsar el botón aceptar, crearemos un nuevo valor DatosRegistro en nuestra clave, que contendrá el nombre y clave de registro separados por un guión.

La aplicación debe comprobar cada vez que se ejecuta si existe el valor DatosRegistro, en cuyo caso se supone que está registrada y no se tomará en cuenta el valor de NumIntentos y MaxIntentos del registro, permitiendo siempre lanzar la aplicación (en nuestro caso siempre se descargará el formulario).

Ejemplo resuelto

FrmPresentacion

'_______________________________________________________________________
' Ejemplo 9 del Curso de Visual Basic Avanzado
'
' Formulario de presentación, en él comprobamos los usos, la registración de la aplicación...
'_______________________________________________________________________


Option Explicit

Const ConMaxIntentos = 10

Private Sub ButAceptar_Click()
    ' Aquí comprobaríamos si se lanza la aplicación
    Unload Me
End Sub

Private Sub ButRegistrar_Click()
    Dim StrClave As String
    Dim StrCadenaRegistro As String
    Dim hkeyExistente As Long
   
    ' Mostramos el formulario de registro
    frmRegistro.Show vbModal

    ' Abrimos la clave del registro
    If RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Agenda", hkeyExistente) Then
       
        ' Comprobamos si está registrado
        StrClave = "DatosRegistro"
        If RegQueryStringValue(hkeyExistente, StrClave, StrCadenaRegistro) Then
            LabTextoPresentacion.Caption = "Versión registrada, completamenta operacional." & vbCrLf & "¡¡Disfrútela!!"
            LabNumUsos.Visible = False
            ButRegistrar.Enabled = False
            Exit Sub
        End If
       
        ' Cerramos la clave del registro
        RegCloseKey hkeyExistente
    End If
End Sub

Private Sub Form_Load()
    Dim hKey As Long
    Dim hkeyExistente As Long
    Dim StrClave As String
    Dim StrCadenaRegistro As String
    Dim LngNumIntentos As Long
    Dim LngMaxIntentos As Long
       
    ' Abrimos la clave del regiStro
    If RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Agenda", hkeyExistente) Then
       
        ' Comprobamos si está registrado
        StrClave = "DatosRegistro"
        If RegQueryStringValue(hkeyExistente, StrClave, StrCadenaRegistro) Then
            LabTextoPresentacion.Caption = "Versión registrada, completamenta operacional." & vbCrLf & "¡¡Disfrútela!!"
            RegCloseKey hkeyExistente
            ButRegistrar.Enabled = False
            Exit Sub
        End If
       
        ' Como no está registrado, obtenemos el número máximo de intentos
        StrClave = "MaxIntentos"
        If RegQueryNumericValue(hkeyExistente, StrClave, LngMaxIntentos) Then
                   
            ' Comprobamos cuántos intentos van
            StrClave = "NumIntentos"
            If RegQueryNumericValue(hkeyExistente, StrClave, LngNumIntentos) Then
                                    
                ' Comprobamos si se ha llegado al número máximo de intentos
                If LngNumIntentos < LngMaxIntentos Then
                    ' Incrementamos el número de usos y lo guardamos en el registro
                    LngNumIntentos = LngNumIntentos + 1
                    RegSetNumericValue hkeyExistente, "NumIntentos", LngNumIntentos
                   
                    ' Actualizamos el formulario
                    LabNumUsos.Caption = "La agenda ha sido utilizada " & LngNumIntentos & " veces (máximo " & LngMaxIntentos & ")"
                Else
                    LabNumUsos.Caption = "La agenda ha sido utilizada " & LngNumIntentos & " veces (máximo " & LngMaxIntentos & "). Registre la versión antes de continuar."
                End If
            End If
        End If
        RegCloseKey hkeyExistente
    Else
   
        ' Como no existe, la creamos
        If RegCreateKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Agenda", hKey) Then
            
             ' Número máximo de intentos
             RegSetNumericValue hKey, "MaxIntentos", ConMaxIntentos
            
             ' Número de intentos realizado
             RegSetNumericValue hKey, "NumIntentos", 1
                
             RegCloseKey hKey
            
             ' Actualizamos el formulario
             LabNumUsos.Caption = "La agenda ha sido utilizada 1 vez (máximo " & ConMaxIntentos & ")"
         End If
    End If
   
End Sub

FrmRegistro

'_______________________________________________________________________
' Ejemplo 9 del Curso de Visual Basic Avanzado
'
' Formulario para introducir los datos del registro de la aplicación.
'_______________________________________________________________________

Option Explicit

Private Sub ButAceptar_Click()
    Dim hkeyExistente As Long
    Dim StrClave As String
    Dim StrCadenaRegistro As String
   
    ' Registramos la aplicación (podríamos comprobar si es auténtico)
    If RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\Agenda", hkeyExistente) Then
       
        ' Lo registramos
        StrCadenaRegistro = Text1.Text & "-" & Text2.Text
        StrClave = "DatosRegistro"
        If RegSetStringValue(hkeyExistente, StrClave, StrCadenaRegistro) Then
            MsgBox "La aplicación ha sido registrada con éxito.", , "Registro"
            Unload Me
        End If
    End If
End Sub

Private Sub ButRegistrar_Click()
    Unload Me


End Sub



ModRegistro

'_______________________________________________________________________
' Ejemplo 8 del Curso de Visual Basic Avanzado
'
' Módulo donde están las funciones de acceso al registro y las declaraciones de las API.
'_______________________________________________________________________


Option Explicit

' API de manipulación del regiStro (32 bits)
Declare Function OSRegCloseKey Lib "advapi32" Alias "RegCloseKey" (ByVal hKey As Long) As Long
Declare Function OSRegCreateKey Lib "advapi32" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Long
Declare Function OSRegDeleteKey Lib "advapi32" Alias "RegDeleteKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String) As Long
Declare Function OSRegOpenKey Lib "advapi32" Alias "RegOpenKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Long
Declare Function OSRegQueryValueEx Lib "advapi32" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpszValueName As String, ByVal dwReserved As Long, lpdwType As Long, lpbData As Any, cbData As Long) As Long
Declare Function OSRegSetValueEx Lib "advapi32" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpszValueName As String, ByVal dwReserved As Long, ByVal fdwType As Long, lpbData As Any, ByVal cbData As Long) As Long

Global Const HKEY_CLASSES_ROOT = &H80000000
Global Const HKEY_CURRENT_USER = &H80000001
Global Const HKEY_LOCAL_MACHINE = &H80000002
Global Const HKEY_USERS = &H80000003
Const ERROR_SUCCESS = 0&

Const REG_SZ = 1
Const REG_BINARY = 3
Const REG_DWORD = 4

' Crea (o abre si ya existe) una clave en el registro del sistema
Function RegCreateKey(ByVal hKey As Long, ByVal lpszKey As String, phkResult As Long) As Boolean
    On Error GoTo 0
   
    If OSRegCreateKey(hKey, lpszKey, phkResult) = ERROR_SUCCESS Then
        RegCreateKey = True
    Else
        RegCreateKey = False
    End If
End Function

' Asocia un valor con nombre (StrValueName = nombre) o sin nombre (StrValueName = "")
' con una clave del regiStro.
Function RegSetStringValue(ByVal hKey As Long, ByVal strValueName As String, ByVal StrData As String) As Boolean
    On Error GoTo 0
   
    If hKey = 0 Then Exit Function
   
    If OSRegSetValueEx(hKey, strValueName, 0&, REG_SZ, ByVal StrData, _
        Len(StrData) + 1) = ERROR_SUCCESS Then
        RegSetStringValue = True
    Else
        RegSetStringValue = False
    End If
End Function

' Asocia un valor con nombre (strValueName = nombre) o sin nombre (strValueName = "")
'   con una clave del registro.
Function RegSetNumericValue(ByVal hKey As Long, ByVal strValueName As String, ByVal lData As Long, Optional ByVal fLog) As Boolean
    On Error GoTo 0
    
    If OSRegSetValueEx(hKey, strValueName, 0&, REG_DWORD, lData, 4) = ERROR_SUCCESS Then
        RegSetNumericValue = True
    Else
        RegSetNumericValue = False
    End If
End Function

' Abre una clave existente en el registro del sistema.
Function RegOpenKey(ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Boolean
    On Error GoTo 0

    If OSRegOpenKey(hKey, lpszSubKey, phkResult) = ERROR_SUCCESS Then
        RegOpenKey = True
    Else
        RegOpenKey = False
    End If
End Function

' Elimina una clave existente del regiStro del sistema.
Function RegDeleteKey(ByVal hKey As Long, ByVal lpszSubKey As String) As Boolean
    On Error GoTo 0
   
    If OSRegDeleteKey(hKey, lpszSubKey) = ERROR_SUCCESS Then
        RegDeleteKey = True
    Else
        RegDeleteKey = False
    End If
End Function

' Cierra una clave abierta del registro
Function RegCloseKey(ByVal hKey As Long) As Boolean
    On Error GoTo 0
   
    If OSRegCloseKey(hKey) = ERROR_SUCCESS Then
        RegCloseKey = True
    Else
        RegCloseKey = False
    End If
End Function

' Recupera los datos de cadena para un valor con nombre
' (StrValueName = nombre) o sin nombre (StrValueName = "")
' dentro de una clave del regiStro. Si el valor con
' nombre existe, pero sus datos no son una cadena, esta
' función fallará.
Function RegQueryStringValue(ByVal hKey As Long, ByVal strValueName As String, StrData As String) As Boolean
    On Error GoTo 0
   
    Dim lValueType As Long
    Dim StrBuf As String
    Dim lDataBufSize As Long
   
    RegQueryStringValue = False
    ' Obtiene el tipo y longitud de los datos
    If OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, ByVal 0&, lDataBufSize) = ERROR_SUCCESS Then
        If lValueType = REG_SZ Then
            StrBuf = String(lDataBufSize, " ")
            If OSRegQueryValueEx(hKey, strValueName, 0&, 0&, ByVal StrBuf, lDataBufSize) = ERROR_SUCCESS Then
                StrData = Left(StrBuf, lDataBufSize - 1)
                RegQueryStringValue = True
            End If
        End If
    End If
End Function

' Recupera los datos enteros para un valor con nombre
' (StrValueName = nombre) o sin nombre (StrValueName = "")
' dentro de una clave del regiStro. Si el valor con nombre
' existe, pero sus datos no son de tipo REG_DWORD, esta
' función fallará.
Function RegQueryNumericValue(ByVal hKey As Long, ByVal strValueName As String, lData As Long) As Boolean
    On Error GoTo 0
   
    Dim lValueType As Long
    Dim lBuf As Long
    Dim lDataBufSize As Long
   
    RegQueryNumericValue = False
   
    ' Obtiene el tipo y longitud de los datos
    lDataBufSize = 4
    If OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, lBuf, lDataBufSize) = ERROR_SUCCESS Then
        If lValueType = REG_DWORD Then
            lData = lBuf
            RegQueryNumericValue = True
        End If
    End If
End Function

APÉNDICE A: Especificaciones, limitaciones y formatos de archivos de Visual Basic

En este apéndice se describen los requisitos de sistema, las limitaciones de un proyecto de Visual Basic, los tipos de archivos que se pueden incluir en el proyecto de Visual Basic y las descripciones de los archivos de formulario (.frm) y de proyecto (.vbp).

Requisitos del sistema para aplicaciones de Visual Basic

Para las aplicaciones de Visual Basic se requiere el siguiente hardware y software:
·     Microsoft Windows NT 3.51 o posterior, o Microsoft Windows 95 o posterior.
·     Microprocesador 80486 o superior.
·     Pantalla VGA o de resolución superior compatible con Microsoft Windows.
·     8 MB de RAM para aplicaciones. (Esto variará dependiendo de las bibliotecas de tipos o los archivos DLL específicos que incluya en sus aplicaciones.)
·     16 MB de RAM para el entorno de desarrollo de Visual Basic.

Limitaciones de los proyectos

Un único proyecto puede contener hasta 32.000 identificadores, que incluyen entre otros formularios, controles, módulos, variables, constantes, procedimientos, funciones y objetos. Los nombres de variables en Visual Basic no pueden tener más de 255 caracteres y los nombres de formularios, controles, módulos y clases pueden tener un máximo de 40 caracteres. Visual Basic no impone ningún límite en cuanto al número de objetos distintos de un proyecto.

Limitaciones de controles

Cada control no gráfico (todos los controles excepto ShapeLineImage Label) utiliza una ventana. Cada ventana utiliza recursos del sistema, limitando el número total de ventanas que pueden existir al mismo tiempo. El límite exacto depende de los recursos del sistema disponibles y el tipo de controles que se utilicen.
Para reducir el consumo de recursos, utilice controles ShapeLineLabel e Image en vez de los controles PictureBox para crear o presentar gráficos.

Número total de controles

El número máximo de controles permitidos en un único formulario depende del tipo de controles que se utilicen y de los recursos disponibles del sistema. No obstante, hay un límite fijo de 254 nombres de control por formulario. En este límite una matriz de controles sólo cuenta como uno, ya que todos los controles en la matriz comparten un único nombre de control.
El límite para índices de matrices de controles es de 0 a 32.767 para todas las versiones.
Si coloca controles uno encima de otro, como por ejemplo si utiliza varios controles de marco dentro de otros marcos, Visual Basic no aceptará más de seis niveles de controles anidados.

Limitaciones para determinados controles

La siguiente tabla muestra las limitaciones de propiedades aplicables a determinados controles de Visual Basic:

Limitaciones de código

La cantidad de código que se puede cargar en un formulario, clase o módulo estándar está limitada a 65.534 líneas. Una única línea de código puede constar de 1.023 bytes como máximo. Puede haber hasta 256 espacios en blanco delante del texto en una única línea y no se pueden incluir más de veinticinco caracteres de continuación de línea ( _) en una única línea lógica.

Procedimientos, tipos y variables

No hay límite en cuanto al número de procedimientos por módulo. Cada procedimiento puede contener hasta 64 KB de código. Si un procedimiento o un módulo excede este límite, Visual Basic genera un error en tiempo de compilación. Si se encuentra con este error, puede evitarlo dividiendo los procedimientos extremadamente grandes en varios procedimientos más pequeños o trasladando las declaraciones de nivel de módulo a otro módulo.
Visual Basic utiliza tablas para almacenar los nombres de los identificadores (variables, procedimientos, constantes, etc.) en el código. Cada tabla está limitada a 64 KB.

Tabla de entradas de módulo

Esta tabla acepta hasta 125 bytes por módulo, con un límite total de 64 KB, que da como resultado unos 400 módulos por proyecto. 

Limitaciones de los datos

Las siguientes limitaciones son aplicables a las variables en el lenguaje Visual Basic


Datos de formulario, módulos estándar y módulos de clase

Cada formulario, módulo estándar y módulo de clase tiene su propio segmento de datos que puede ser como máximo de 64 KB. Este segmento de datos contiene los siguientes datos:
·     Variables locales declaradas con Static.
·     Variables a nivel de módulo que no sean matrices y cadenas de longitud variable.
·     4 bytes para cada matriz a nivel de módulo y cadena de longitud variablez

Procedimientos, tipos y variables

Si un procedimiento o un módulo excede el límite de 64 KB, Visual Basic generará un error de tiempo de compilación. Si se encuentra con este error, puede evitarlo dividiendo los procedimientos extremadamente grandes en varios procedimientos más pequeños o trasladando las declaraciones a nivel de módulo a otro módulo.

Tipos definidos por el usuario

Ninguna variable de un tipo definido por el usuario puede exceder los 64 KB, aunque la suma de las cadenas de longitud variable en un tipo definido por el usuario puede exceder de 64 KB (las cadenas de longitud variable sólo ocupan 4 bytes cada una en el tipo definido por el usuario; el contenido real de una cadena se almacena por separado). Los tipos definidos por el usuario se pueden definir en términos de otros tipos definidos por el usuario, pero el tamaño total de los tipos no puede exceder los 64 KB.

Espacio de pila

Los argumentos y las variables locales en los procedimientos ocupan espacio de pila en tiempo de ejecución. Las variables estáticas y a nivel de módulo no ocupan espacio de pila porque se encuentran en el segmento de datos para los formularios o los módulos. Todos los procedimientos de DLL a los que se llame utilizan esta pila mientras se están ejecutando.
Visual Basic utiliza parte de la pila para sus propios usos, como el almacenamiento de valores intermedios al evaluar expresiones.

Limitaciones de los recursos del sistema

Algunas limitaciones de Visual Basic y las aplicaciones creadas con él están impuestas por Microsoft Windows. Estas limitaciones pueden cambiar cuando se instala una versión diferente de Microsoft Windows.

Recursos de Windows

Cada ventana abierta usa recursos del sistema (áreas de datos utilizadas por Microsoft Windows). Si se agotan los recursos del sistema, se producirá un error en tiempo de ejecución. Puede comprobar el porcentaje de recursos del sistema que quedan si elige Acerca de en el menú Ayuda del Administrador de programas o del Administrador de archivos en Windows NT 3.51 o, en Windows 95 y Windows NT 4.0, si elige Acerca de en el menú Ayuda del Explorador de Windows. Las aplicaciones también pueden llamar a la función GetFreeSystemResources de el API de Windows para reclamar recursos del sistema, cerrar ventanas (como formularios abiertos y ventanas de código, así como ventanas de otras aplicaciones) y finalizar la ejecución de aplicaciones.

Formatos de archivos de proyecto

Microsoft Visual Basic utiliza y crea una serie de archivos tanto en tiempo de diseño como en tiempo de ejecución. Los archivos que el proyecto o la aplicación requerirán dependen de su alcance y funcionalidad.

Extensiones de archivos de proyecto

Visual Basic crea varios archivos cuando se crea y compila un proyecto. Estos se pueden dividir como sigue: tiempo de diseño, otros desarrollos y tiempo de ejecución.
Los archivos de tiempo de diseño son los ladrillos de su proyecto: por ejemplo, módulos de Basic (.bas) y módulos de formulario (.frm).
Otros procesos y funciones del entorno de desarrollo de Visual Basic crean diversos archivos: por ejemplo, archivos de dependencias del Asistente para instalación (.dep).

Archivos varios y de tiempo de diseño

La siguiente tabla muestra todos los archivos de tiempo de diseño y otros archivos que se pueden crear al desarrollar una aplicación:

Archivos de tiempo de ejecución

Al compilar la aplicación, todos los archivos necesarios de tiempo de ejecución se incluyen en los archivos ejecutables de tiempo de ejecución. La siguiente tabla muestra los archivos de tiempo de ejecución:

Estructuras de formularios

A pesar de que muchos de los archivos de un típico proyecto de Visual Basic están en formato binario y sólo los pueden leer determinados procesos y funciones de Visual Basic o de su aplicación, los archivos de formulario (.frm) y de proyecto (.vbp) se guardan como texto ASCII. Estos archivos se pueden leer en un visor de texto (por ejemplo, el Bloc de notas).
En las siguientes secciones se describen los archivos de tiempo diseño y de tiempo de ejecución en un proyecto típico de Visual Basic y el formato de los archivos de formulario (.frm) y de proyecto (.vbp).
Los archivos de formulario de Visual Basic (.frm) se crean y se guardan en formato ASCII. La estructura de un formulario consiste en lo siguiente:
·     El número de versión del formato de archivo.
·     Un bloque de texto que contiene la descripción del formulario.
·     Un conjunto de atributos del formulario.
·     El código Basic del formulario.

La descripción del formulario contiene los valores de las propiedades del formulario. Los bloques de texto que definen las propiedades de los controles del formulario están anidados en el formulario. Los controles contenidos en otros controles tienen sus propiedades anidadas en el texto del contenedor. La figura A.1 ilustra la estructura de la descripción del formulario.

Número de versión

El número de versión para los formularios creados con Visual Basic 5.0 para Windows es 5.00. Si en un formulario se omite el número de versión, se generará un error. Al cargar una aplicación de Visual Basic que tiene un número de versión menor que 5.00, aparece un cuadro de diálogo de advertencia para informar que el archivo se guardará en el nuevo formato.

Figura A.1   Estructura de la descripción del formulario

Descripción del formulario

La descripción de un formulario empieza con una instrucción Begin y termina con una instrucción End. La sintaxis de la instrucción Begin es la siguiente:
Begin VB.{Form|MDIFormnombre_formulario

La instrucción End indica dónde termina la descripción del formulario y dónde empieza el conjunto de atributos del formulario. Sin la instrucción End, Visual Basic intentaría leer los atributos como si se estuvieran describiendo los controles y las propiedades del formulario, por lo que se producirían errores.
Entre las instrucciones Begin Form y End están las propiedades del formulario propiamente dicho, seguidas de las descripciones de cada control del formulario. La figura A.2 muestra la estructura anidada de la descripción del formulario con más detalle.

Bloques de control



Un bloque de control consta del texto de la descripción del formulario que define las propiedades de un control individual. Al igual que la descripción del formulario, los bloques de control empiezan con una instrucción Begin y terminan con una instrucción End. La sintaxis para una instrucción Begin de un bloque de control es la siguiente:



Begin clase_control.tipo_control nombre_control

Las propiedades para el control aparecen entre la instrucción Begin y la instrucción End.

Orden de los bloques de control

El orden de los bloques de control determina el orden z de los controles. El orden z es una ordenación relativa que determina la manera en la que los controles se solapan entre sí en un formulario. El primer control de la descripción del formulario establece la parte inferior del orden z. Los controles que aparecen posteriormente en la descripción del formulario están más arriba en el orden z y, por tanto, solapan a los que están más abajo en dicho orden.

Bloques de control incrustados

Algunos controles pueden contener otros controles. Cuando un control está contenido en otro, su bloque de control está incrustado en el bloque de control del contenedor. Puede incrustar bloques de control dentro de:
·     Marcos
·     Cuadros de imágenes
·     Menús
·     Controles personalizados, dependiendo de su objetivo

Los controles incrustados se utilizan normalmente para colocar botones de opción dentro de un marco. Visual Basic debe tener toda la información necesaria para el contenedor antes de que se agregue ningún control incrustado, por lo que las propiedades para un control deben ir antes de cualquier bloque de control incrustado. Visual Basic pasa por alto todas la propiedades dentro de un bloque de control que aparezcan después de los bloques de control incrustados.

Figura A.2   Estructura anidada de la descripción del formulario

Controles de menú

Los controles de menú deben aparecer juntos al final de la descripción del formulario, justo antes de que empiece la sección de atributos. Cuando Visual Basic encuentra un control de menú durante la carga de un formulario ASCII, espera encontrar todos los controles de menú juntos. Una vez que detecta un control que no es de menú a continuación de uno o más controles de menú, supone que no hay más controles de menú en el formulario y pasa por alto cualquier control de menú que encuentre durante la carga de ese formulario.

Teclas de método abreviado

Las teclas de método abreviado son teclas que se utilizan para activar un control de menú. Los formularios ASCII usan la misma sintaxis que la instrucción SendKeys para definir las combinaciones de teclas: “+” = MAYÚSCULAS, “^” = CTRL y “{Fn}” = tecla de función, donde n es el número de la tecla. Los caracteres alfabéticos se representan a sí mismos. La sintaxis de las teclas de método abreviado es la siguiente:
Método abreviado = ^{F4}      ' <CTRL><F4>


Comentarios en la descripción del formulario

Puede agregar comentarios a la descripción del formulario. Las comillas simples (') son los delimitadores de los comentarios.

 Propiedades de la descripción de un formulario

Cuando Visual Basic guarda un formulario, organiza las propiedades en un orden predeterminado. No obstante, al crear un formulario las propiedades se pueden organizar en cualquier orden.
Cualquier propiedad que no enumere se establece a su valor predeterminado cuando se carga. Cuando Visual Basic guarda un formulario, sólo incluye aquellas propiedades que no usan sus valores predeterminados. Cada control determina si se guardan o no todas sus propiedades o solamente aquellas cuyos valores son distintos de los valores predeterminados.

Sintaxis

Use esta sintaxis para definir las propiedades en la descripción del formulario:
propiedad = valor

Los valores de propiedad de texto deben aparecer entre comillas dobles. Las propiedades booleanas tienen un valor – 1 para True y 0 para False. Visual Basic interpreta todos los valores distintos de – 1 o 0 como True. Las propiedades con valores enumerados incluyen sus valores numéricos con la descripción del valor incluida como comentario. Por ejemplo, la propiedad BorderStyle aparece así:
BorderStyle = 0           ' Ninguno

Valores binarios de propiedades

Algunos controles disponen de propiedades cuyos valores son datos binarios, como la propiedad Picture del cuadro de imagen y los controles de imagen o ciertas propiedades de los controles personalizados. Visual Basic guarda todos los datos binarios de un formulario en un archivo de datos binario aparte del formulario.


Visual Basic guarda el archivo de datos binario en el mismo directorio que el formulario. El archivo de datos binario tiene el mismo nombre que el formulario, pero con la extensión .frx. Visual Basic lee el archivo de datos binario cuando carga el formulario, por lo que el archivo de datos binario (.frx) debe estar disponible para el formulario cuando Visual Basic lo carga. Si comparte formularios con otras personas que usan un archivo de datos binario, asegúrese de proporcionar el archivo de datos binario (.frx), además del formulario (.frm).





Las propiedades que tengan datos binarios como valores aparecen en el formulario como una referencia a un desplazamiento de byte en el archivo de datos binario. Por ejemplo, el valor de una propiedad Picture aparece de la siguiente manera en la descripción de un formulario:



Begin VB.Image imgDemo
     Picture = "Miform.frx":02EB
End

La enumeración de la propiedad significa que los datos binarios que definen la propiedad Picture de este control empiezan en el byte 2EB (hex) del archivo Miform.frx.

Propiedad Icon

El valor de la propiedad Icon de un formulario depende de qué icono se utilice para el formulario. La siguiente tabla muestra los valores de la propiedad Icon y la manera en que esas propiedades aparecen en un formulario.

Propiedad TabIndex

Si no se especifica la propiedad TabIndex, Visual Basic asigna el control a la primera ubicación posible en el orden de tabulación una vez que estén cargados todos los demás controles.


Unidades de medida

El tamaño de los controles, las coordenadas x e y, así como otros valores de propiedades que utilicen unidades de medida se expresan en twips. Cuando un control usa un modo de escala diferente a los twips y Visual Basic carga el formulario, convierte los valores en twips del formulario ASCII a las unidades de medida especificadas por la propiedad ScaleMode.

Valores de colores

Los valores de colores aparecen como valores RGB (rojo, verde y azul). Por ejemplo, la propiedad ForeColor aparece de esta manera:
ForeColor = &H00FF0000&

Visual Basic también puede leer valores de QBColor, convirtiéndolos a RGB cuando carga el formulario. Los formularios ASCII que utilicen valores de QBColor deben usar esta sintaxis:
ForeColor = QBColor(qbcolor)

donde qbcolor es un valor de 0 a 15.
Observe que el argumento qbcolor corresponde a los valores de color utilizados por las instrucciones gráficas en otras versiones de Basic, como Visual Basic para MS-DOS, Microsoft QuickBasic y el Sistema de desarrollo profesional Microsoft Basic.

Objetos de propiedades

Algunos objetos de propiedades, como por ejemplo el objeto Font, aparecen como un bloque distinto y muestran todos los valores de las diferentes propiedades del objeto. Estos bloques están delimitados por las instrucciones BeginProperty y EndProperty de la siguiente forma:
BeginProperty nombre_propiedad
    propiedad1 = valor1
    propiedad2 = valor2
    .
    .
    .
EndProperty

Código de Basic

El código de Basic aparece en el formulario inmediatamente después de la sección de atributos, a continuación de la última instrucción End en la descripción del formulario. Las instrucciones en la sección Declaraciones de un formulario aparecen primero, seguidas de los procedimientos de evento, los procedimientos generales y las funciones.


Cuando Visual Basic carga un formulario en memoria, primero convierte el formulario al formato binario. Al realizar cambios en el formulario y guardar los cambios, Visual Basic vuelve a escribir el archivo en formato ASCII.
Cuando Visual Basic se encuentra con un error mientras está cargando un formulario, crea un archivo de registro e informa del error en el archivo de registro. Visual Basic agrega mensajes de error al archivo de registro cada vez que se encuentra con un error en el formulario. Cuando se ha terminado de cargar el formulario, Visual Basic presenta un mensaje que indica que se creó un archivo de registro de errores.
El archivo de registro tiene el mismo nombre que el archivo de formulario, pero con la extensión .log. Por ejemplo, si se producen errores mientras se carga Miform.frm, Visual Basic creará un archivo de registro llamado Miform.log. Si posteriormente vuelve a cargar Miform.frm y continúan produciéndose errores mientras se carga el formulario, Visual Basic reemplazará el archivo Miform.log anterior.


Mensajes de registro de error en la carga de un formulario

Los siguientes mensajes de error pueden aparecer en un archivo de registro de errores. Observe que estos mensajes de error sólo tratan problemas que pueden ocurrir cuando Visual Basic carga la descripción del formulario. No indican ningún problema que pueda existir en procedimientos de evento, en procedimientos generales o en cualquier otra parte del código de Basic.
Imposible cargar el menú nombre_menú.
Este mensaje aparece si Visual Basic encuentra un control de menú cuyo menú primario esté definido como un separador de menú. Los controles de menú que actúan como primarios para los controles de menú en un submenú no pueden ser separadores de menú. Visual Basic no carga el control de menú.


Este mensaje también aparece si Visual Basic encuentra un control de menú cuyo menú primario tiene su propiedad Checked establecida a True. Los controles de menú que actúan como primarios para los controles de menú en un submenú no pueden tener esta propiedad activada. Visual Basic no carga el control de menú.



Imposible establecer la propiedad Checked en el menú nombre_menú.
Este mensaje aparece si Visual Basic encuentra un control de menú de nivel superior con su propiedad Checked establecida a True. Los menús de nivel superior no pueden tener una marca de verificación. Visual Basic carga el control del menú, pero no establece su propiedad Checked.
Imposible establecer la propiedad Shortcut en nombre_menú.
Este mensaje aparece si Visual Basic encuentra un control de menú de nivel superior con una tecla de método abreviado definida. Los menús de nivel superior no pueden tener una tecla de método abreviado. Visual Basic carga el control del menú, pero no establece la propiedad Shortcut.
La clase nombre_clase del control nombre_control no es una clase de control cargada.
Este mensaje aparece si Visual Basic encuentra un nombre de clase que no reconoce.


Imposible cargar el control nombre_control.



Este mensaje aparece si Visual Basic encuentra un tipo de control desconocido en la descripción del formulario. Visual Basic crea un cuadro de imagen para representar el control desconocido, dando a ese cuadro de imagen todas las propiedades válidas de la descripción del control desconocido. Cuando aparece este mensaje, es probable que le sigan varios errores de propiedades no válidas.
El control nombre_control tiene una cadena entre comillas donde debería estar el nombre de la propiedad.
Este mensaje aparece si Visual Basic encuentra texto entre comillas en vez de un nombre de propiedad que no se coloca entre comillas. Por ejemplo:
"Caption" = "Comenzar la demostración"

En este caso, el nombre de propiedad Caption no debería estar entre comillas. Visual Basic pasa por alto la línea de la descripción del formulario que produce este error.
El nombre de control nombre_control no es válido.


Este mensaje aparece si el nombre de un control no es una cadena válida en Visual Basic. Visual Basic no cargará el control.



El nombre de control es demasiado largo; truncado a nombre_control.
Este mensaje aparece si Visual Basic encuentra un nombre de control con más de 40 caracteres. Visual Basic carga el control, truncando el nombre.
No se encontró una propiedad de índice y el control nombre_control ya existe. Imposible crear este control.
Este mensaje aparece si Visual Basic encuentra un control sin un índice que tiene el mismo nombre que un control cargado anteriormente. Visual Basic no carga el control.
Imposible cargar el formulario nombre_formulario.


Este mensaje aparece si Visual Basic encuentra inesperadamente el final del archivo o si falta la primera instrucción Begin.



El nombre del formulario o de MDIForm nombre_formulario no es válido; imposible cargar este formulario.
Este mensaje aparece si el nombre de un formulario no es una cadena válida en Visual Basic. Visual Basic no cargará el formulario.
Las cadenas válidas deben empezar con una letra, sólo pueden incluir letras, números y signos de subrayado, y deben tener como máximo 40 caracteres.
El nombre nombre_propiedad de la propiedad del control nombre_control no es válido.
Este mensaje aparece si el nombre de una propiedad no es una cadena válida en Visual Basic o tiene más de 30 caracteres. Visual Basic no establecerá la propiedad.
Imposible cargar la propiedad nombre_propiedad del control nombre_control.
Este mensaje aparece si Visual Basic encuentra una propiedad desconocida. Visual Basic pasa por alto esta propiedad cuando carga el formulario.

Imposible establecer la propiedad nombre_propiedad del control nombre_control.


Este mensaje aparece si Visual Basic no puede establecer la propiedad del control especificado como se indica en la descripción del formulario. 



La propiedad nombre_propiedad del control nombre_control tiene un valor no válido.
Este mensaje aparece si Visual Basic encuentra un valor no válido para una propiedad. Visual Basic cambia el valor de la propiedad al valor predeterminado para esa propiedad.
La propiedad nombre_propiedad del control nombre_control tiene una referencia de archivo no válida.


Este mensaje aparece si Visual Basic no pudo usar una referencia de nombre de archivo. Esto ocurrirá si el archivo al que se refiere (probablemente un archivo de datos binario para el formulario) no se encuentra en el directorio especificado.



La propiedad nombre_propiedad del control nombre_control tiene un índice de propiedad no válido.
Este mensaje aparece si Visual Basic encuentra un nombre de propiedad con un índice de propiedad mayor de 255. Por ejemplo:
Prop300 = 5436

Visual Basic pasa por alto la línea de la descripción del formulario que produjo este error.
La propiedad nombre_propiedad del control nombre_control tiene un valor no válido.
Este mensaje aparece si Visual Basic encuentra una propiedad con un valor que no es correcto para ese control. Por ejemplo:
Top = Cahr(22)       ' En realidad se deseaba Char(22).

Visual Basic establece la propiedad a su valor predeterminado.
La propiedad nombre_propiedad del control nombre_control debe ser una cadena entre comillas.
Este mensaje aparece si Visual Basic encuentra un valor de propiedad sin comillas que debería aparecer entre comillas. Por ejemplo:
Caption = Comenzar la demostración

Visual Basic pasa por alto la línea de la descripción del formulario que produjo este error.
Error de sintaxis: la propiedad nombre_propiedad del control nombre_control no tiene un '='.
Este mensaje aparece si Visual Basic encuentra un nombre y un valor de propiedad sin un signo igual entre ellos. Por ejemplo:
Text        "Comenzar la demostración"

Visual Basic no carga la propiedad.

Formato del archivo de proyecto (.vbp)

Visual Basic siempre guarda los archivos de proyecto (.vbp) en formato ASCII. El archivo de proyecto contiene entradas que reflejan los valores de su proyecto. Entre estos se incluyen los formularios y módulos del proyecto, referencias, opciones varias que ha elegido para controlar la compilación, etc.
Esta es la apariencia que debe tener un archivo .vbp. Este proyecto incluye módulos guardados con los nombres de clase y de archivo que se muestran en la siguiente tabla.





Se agregan entradas al archivo .vbp cuando agrega formularios, módulos, componentes, etc. al proyecto. También se agregan entradas cuando establece opciones para el proyecto. Muchas de estas opciones se establecen mediante el cuadro de diálogo Propiedades del proyecto.

Apéndice B: Convenciones de codificación




Este apéndice presenta un conjunto de convenciones de codificación que sugerimos para los programas de Visual Basic.



Las convenciones de codificación son pautas de programación que no están enfocadas a la lógica del programa, sino a su estructura y apariencia física. Facilitan la lectura, comprensión y mantenimiento del código. Las convenciones de codificación pueden incluir:
·     Convenciones de nombres para objetos, variables y procedimientos.
·     Formatos estandarizados para etiquetar y comentar el código.
·     Instrucciones de espaciado, formato y sangría.

En las secciones siguientes se explica cada una de estas áreas y se dan ejemplos de su uso correcto.

¿Por qué existen las convenciones de codificación?

La razón principal de usar un conjunto coherente de convenciones de código es estandarizar la estructura y el estilo de codificación de una aplicación de forma que el autor y otras personas puedan leer y entender el código fácilmente.
Las convenciones de codificación correctas dan como resultado un código fuente preciso, legible y sin ambigüedad, que es coherente con otras convenciones del lenguaje y lo más intuitivo posible.

Convenciones de codificación mínimas

Un conjunto de convenciones de codificación de propósito general debe definir los requisitos mínimos necesarios para conseguir los objetivos explicados anteriormente, dejando libertad al programador para crear la lógica y el flujo funcional del programa.
El objetivo es hacer que el programa sea fácil de leer y de entender sin obstruir la creatividad natural del programador con imposiciones excesivas y restricciones arbitrarias.
Por tanto, las convenciones sugeridas en este apéndice son breves y sugerentes. No muestran todos los objetos y controles posibles, ni especifican todos los tipos de comentarios informativos que podrían ser útiles. Dependiendo del proyecto y de las necesidades específicas de la organización, quizás desee ampliar estas instrucciones para que incluyan elementos adicionales como:
·     Convenciones para objetos específicos y componentes desarrollados internamente o comprados a otros proveedores.
·     Variables que describan las actividades comerciales o instalaciones de la organización.
·     Cualquier otro elemento que el proyecto o la empresa considere importante para conseguir mayor claridad y legibilidad.

Convenciones de nombres de objetos

Los objetos deben llevar nombres con un prefijo coherente que facilite la identificación del tipo de objeto. A continuación se ofrece una lista de convenciones recomendadas para algunos de los objetos permitidos por Visual Basic.

Prefijos sugeridos para los objetos de acceso a datos (DAO)

Use los prefijos siguientes para indicar Objetos de acceso a datos (DAO).


Algunos ejemplos:


Prefijos sugeridos para menús

Las aplicaciones suelen usar muchos controles de menú, lo que hace útil tener un conjunto único de convenciones de nombres para estos controles. Los prefijos de controles de menús se deben extender más allá de la etiqueta inicial "mnu", agregando un prefijo adicional para cada nivel de anidamiento, con el título del menú final en la última posición de cada nombre. En la tabla siguiente hay algunos ejemplos.
Cuando se usa esta convención de nombres, todos los miembros de un grupo de menús determinado se muestran uno junto a otro en la ventana Propiedades de Visual Basic. Además, los nombres del control de menú documentan claramente los elementos de menú a los que están adjuntos.

Selección de prefijos para otros controles

Para los controles no mostrados arriba, debe intentar establecer un estándar de prefijos únicos de dos o tres caracteres que sean coherentes. Solamente se deben usar más de tres caracteres si proporcionan más claridad.
Para controles derivados o modificados, por ejemplo, amplíe los prefijos anteriores para que no haya dudas sobre qué control se está usando realmente. Para los controles de otros proveedores, se debe agregar al prefijo una abreviatura del nombre del fabricante en minúsculas. Por ejemplo, una instancia de control creada a partir  del marco 3D incluido en la Edición profesional de Visual Basic podría llevar el prefijo fra3d para evitar confusiones sobre qué control se está usando realmente.

Convenciones de nombres de constantes y variables




Además de los objetos, las constantes y variables también requieren convenciones de nombres bien compuestas. En esta sección se muestran las convenciones recomendadas para las constantes y variables permitidas por Visual Basic. También se explican cuestiones relacionadas con la identificación del tipo de datos y su alcance.



Las variables se deben definir siempre con el menor alcance posible. Las variables globales (públicas) pueden crear máquinas de estado enormemente complejas y hacer la lógica de una aplicación muy difícil de entender. Las variables globales también hacen mucho más difícil mantener y volver a usar el código.
En Visual Basic las variables pueden tener el alcance siguiente:



En una aplicación de Visual Basic, las variables globales se deben usar sólo cuando no exista ninguna otra forma cómoda de compartir datos entre formularios. Cuando haya que usar variables globales, es conveniente declararlas todas en un único módulo agrupadas por funciones y dar al módulo un nombre significativo que indique su finalidad, como Public.bas.





Una práctica de codificación correcta es escribir código modular siempre que sea posible. Por ejemplo, si la aplicación muestra un cuadro de diálogo, coloque todos los controles y el código necesario para ejecutar la tarea del diálogo en un único formulario. Esto ayuda a tener el código de la aplicación organizado en componentes útiles y minimiza la sobrecarga en tiempo de ejecución.



A excepción de las variables globales (que no se deberían pasar), los procedimientos y funciones deben operar sólo sobre los objetos que se les pasan. Las variables globales que se usan en los procedimientos deben estar identificadas en la sección Declaraciones al principio del procedimiento. Además, los argumentos se deben pasar a los procedimientos Sub y Function mediante ByVal, a menos que sea necesario explícitamente cambiar el valor del argumento que se pasa.

Prefijos de alcance de variables

A medida que aumenta el tamaño del proyecto, también aumenta la utilidad de reconocer rápidamente el alcance de las variables. Esto se consigue escribiendo un prefijo de alcance de una letra delante del tipo de prefijo, sin aumentar demasiado la longitud del nombre de las variables.


Una variable tiene alcance global si se declara como Public en un módulo estándar o en un módulo de formulario. Una variable tiene alcance de nivel de módulo si se declara como Private en un módulo estándar o en un módulo de formulario, respectivamente.

Constantes

El cuerpo del nombre de las constantes se debe escribir en mayúsculas y minúsculas, con la letra inicial de cada palabra en mayúsculas. Aunque las constantes estándar de Visual Basic no incluyen información de tipo de datos y el alcance, los prefijos como i, s, g y m pueden ser muy útiles para entender el valor y el alcance de una constante. Para los nombres de constantes, se deben seguir las mismas normas que para las variables. Por ejemplo:





Variables

Declarar todas las variables ahorra tiempo de programación porque reduce el número de errores debidos a erratas (por ejemplo, aNombreUsuarioTmp frente a sNombreUsuarioTmp frente a sNombreUsuarioTemp). En la ficha Editor del cuadro de diálogo Opciones, active la opción Declaración de variables requerida. La instrucción Option Explicit requiere que declare todas las variables del programa de Visual Basic.
Las variables deben llevar un prefijo para indicar su tipo de datos. Opcionalmente, y en especial para programas largos, el prefijo se puede ampliar para indicar el alcance de la variable.

Tipos de datos de variables

Use los prefijos siguientes para indicar el tipo de datos de una variable

Nombres descriptivos de variables y procedimientos

El cuerpo de un nombre de variable o procedimiento se debe escribir en mayúsculas y minúsculas y debe tener la longitud necesaria para describir su funcionalidad. Además, los nombres de funciones deben empezar con un verbo, como IniciarNombreMatriz o CerrarDiálogo.
Para nombres que se usen con frecuencia o para términos largos, se recomienda usar abreviaturas estándar para que los nombres tengan una longitud razonable. En general, los nombres de variables con más de 32 caracteres pueden ser difíciles de leer en pantallas VGA.
Cuando se usen abreviaturas, hay que asegurarse de que sean coherentes en toda la aplicación. Alternar aleatoriamente entre Cnt y Contar dentro de un proyecto provoca una confusión innecesaria.


Tipos definidos por el usuario

En un proyecto grande con muchos tipos definidos por el usuario, suele ser útil dar a cada uno de estos tipos un prefijo de tres caracteres sólo suyo. Si estos prefijos comienzan con "u", será fácil reconocerlos cuando se esté trabajando con tipos definidos por el usuario. Por ejemplo, "ucli" se podría usar como prefijo para las variables de un tipo Cliente definido por el usuario.

Convenciones de codificación estructurada

Además de las convenciones de nombres, las convenciones de codificación estructurada, como comentarios al código y sangrías coherentes, pueden mejorar mucho la legibilidad del código.

Convenciones de comentarios al código

Todos los procedimientos y funciones deben comenzar con un comentario breve que describa las características funcionales del procedimiento (qué hace). Esta descripción no debe describir los detalles de implementación (cómo lo hace), porque a veces cambian con el tiempo, dando como resultado un trabajo innecesario de mantenimiento de los comentarios o, lo que es peor, comentarios erróneos. El propio código y los comentarios de líneas necesarios describirán la implementación.
Los argumentos que se pasan a un procedimiento se deben describir cuando sus funciones no sean obvias y cuando el procedimiento espera que los argumentos estén en un intervalo específico. También hay que describir, al principio de cada procedimiento, los valores de retorno de funciones y las variables globales que modifica el procedimiento, en especial los modificados a través de argumentos de referencia.
Los bloques del comentario de encabezado del procedimiento deben incluir los siguientes encabezados de sección. En la sección siguiente, "Dar formato al código", hay algunos ejemplos.


Recuerde los puntos siguientes:
·     Cada declaración de variable importante debe incluir un comentario de línea que describa el uso de la variable que se está declarando.
·     Las variables, controles y procedimientos deben tener un nombre bastante claro para que los comentarios de línea sólo sean necesarios en los detalles de implementación complejos.
·     Al principio del módulo .bas que contiene las declaraciones de constantes genéricas de Visual Basic del proyecto, debe incluir un resumen que describa la aplicación, enumerando los principales objetos de datos, procedimientos, algoritmos, cuadros de diálogo, bases de datos y dependencias del sistema. Algunas veces puede ser útil un pseudocódigo que describa el algoritmo.

Dar formato al código

Como muchos programadores usan todavía pantallas VGA, hay que ajustarse al espacio de la pantalla en la medida de lo posible y hacer que el formato del código siga reflejando la estructura lógica y el anidamiento. Estos son algunos indicadores:
·     Los bloques anidados estándar, separados por tabuladores, deben llevar una sangría de cuatro espacios (predeterminado).
·     El comentario del esquema funcional de un procedimiento debe llevar una sangría de un espacio. Las instrucciones de nivel superior que siguen al comentario del esquema deben llevar una sangría de un tabulador, con cada bloque anidado separado por una sangría de un tabulador adicional. Por ejemplo:


'*****************************************************
' Finalidad:   Ubica el primer caso encontrado de un
'                   usuario especificado en la matriz
'                   ListaUsuario.
' Entradas:
'    strListaUsuario():    lista de usuarios para buscar.
'    strUsuarioDest: nombre del usuario buscado.
' Resultados:     Índice del primer caso de rsUsuarioDest
'                   encontrado en la matriz rasListaUsuario.
'                   Si no se encuentra el usuario de destino, devuelve -1.
'*****************************************************

Function intBuscarUsuario (strListaUsuario() As String, strUsuarioDest As _
     String)As Integer
     Dim i As Integer               ' Contador de bucle.
     Dim blnEncontrado As Integer         ' Indicador de
                                               ' destino encontrado.
     intBuscarUsuario = -1
     i = 0
     While i <= Ubound(strListaUsuario) and Not blnEncontrado
          If strListaUsuario(i) = strUsuarioDest Then
               blnEncontrado = True
               intBuscarUsuario = i
          End If
     Wend
End Function

Agrupación de constantes

Las variables y constantes definidas se deben agrupar por funciones en lugar de dividirlas en áreas aisladas o archivos especiales. Las constantes genéricas de Visual Basic se deben agrupar en un único módulo para separarlas de las declaraciones específicas de la aplicación. 

Operadores & y +

Use siempre el operador & para unir cadenas y el operador + cuando trabaje con valores numéricos. El uso del operador + para concatenar puede causar problemas cuando se opera sobre dos variables Variant. Por ejemplo:

vntVar1 = "10.01"
vntVar2 = 11
vntResult = vntVar1 + vntVar2   'vntResult = 21.01
vntResult = vntVar1 & vntVar2              'vntResult = 10.0111

Crear cadenas para MsgBox, InputBox y consultas SQL

Cuando esté creando una cadena larga, use el carácter de subrayado de continuación de línea para crear múltiples líneas de código, de forma que pueda leer o depurar la cadena fácilmente. Esta técnica es especialmente útil cuando se muestra un cuadro de mensaje (MsgBox) o un cuadro de entrada (InputBox), o cuando se crea una cadena SQL. Por ejemplo:

Dim Msj As String
Msj = "Esto es un párrafo que estará en un" _
& " cuadro de mensajes. El texto está separado en" _
& " varias líneas de código en el código de origen, " _
& "lo que facilita al programador la tarea de leer y depurar."
MsgBox Msj

Dim CTA As String
CTA = "SELECT *" _
& " FROM Título" _
& " WHERE [Fecha de publicación] > 1988"
ConsultaTítulos.SQL = CTA

Espero LES SEA DE UTILIDAD  y les guste!! 

Visiten YoutubeProgramación, ScribdTaringa, mis paginas
PURAVIDA desde COSTA RICA
 


No hay comentarios:

Publicar un comentario