Está en la página 1de 29

Visual Basic Guía del Estudiante Capítulo 17

Funciones API de Windows


Las APIs de Windows son un serie de funciones que Windows tienen implementadas al servicio
del programador. Estas funciones se llaman API (Interfaz para Programación de Aplicaciones) y
están en las innumerables DLLs que tiene Windows. Las APIs tienen la virtud de acceder a
partes de la máquina o del sistema operativo a las que no podríamos acceder mediante Visual
Basic. Las APIs pertenecen como decíamos a Windows, por lo que pueden ser usadas por
programas escritos en cualquier lenguaje de programación. Es muy interesante utilizarlas, pues
reducen el tamaño del programa ejecutable frente a otras posibilidades con controles, aparte de
darle mayor rapidez de ejecución, al tratarse de código ya compilado.

Dado que este libro tiene lógicamente un alcance limitado, y el tema de APIs es enorme, se
recomienda recurrir a un libro específico de APIs. Este no puede ser otro que el siguiente :

TITULO API de Win32. Guía del Programador de Visual Basic


AUTOR Daniel Appleman. Editorial InforBooks - Barcelona
ISBN 84-89700-22-2
Citaremos este libro repetidas veces a lo largo de este capítulo.

Daniel Appleman es el fundador de Desaware Inc. http://www.desaware.com/ Su página es


visita obligada.

Nota introducida en el 2001 - Aparte de este libro, existe un recurso en Internet que incluso
le supera, y que tiene la gran ventaja de que se trata de un sistema informático donde puede
copiar y pegar código. Puede encontrarlo en http://www.allapi.net/
Desde que lo he descubierto he dejado el Libro de Appleman un poco aparcado. Sin
embargo las explicaciones aportadas en ese libro son difícilmente sustituibles.

Para usar una función API lo primeros que tenemos que hacer es declararla en nuestra
aplicación. La declaración debe hacerse en la sección de declaraciones de un formulario o
módulo. Si la declaramos en un formulario, necesariamente debemos declararla como privada.

Una declaración sencilla podría ser la de la función API Sleep :

Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)

Aquí se ha declarado como pública. Es lo mismo que decir:

Public Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)

En un formulario no se puede declarar como pública. Deberemos poner

Private Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)

En esta declaración lo que le estamos diciendo es que, en la librería kernel32 está escrita una
función llamada Sleep (Es el nombre que figura entre comillas en la declaración) y que le
tenemos que pasar un parámetro, el tiempo que queremos que se pare la ejecución de la
aplicación, expresado en milisegundos. Nos dice la declaración que el parámetro se le pasa
Por Valor (ByVal) y que ese dato debe ser un Long, es decir, si se lo pasamos como una
variable, esa variable debe ser del tipo Long. Una vez declarada esta función, en la sección de
declaraciones de un módulo o de un formulario, podremos acceder a ella en cualquier parte de
la aplicación (las partes de la aplicación donde se puede usar dependerá del ámbito de la
declaración, que es idéntica que para las variables) usando una línea de código como esta :

Sleep (500) y la aplicación se detendrá medio segundo cuando llegue a esa línea

ó Sleep (tiempo) donde tiempo es una variable tipo Long que contiene el tiempo (en
milisegundos) que queremos detener el programa.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 1


Esta API es muy sencilla. Por eso comenzamos por ella. La hay mas complicadas. Por ejemplo,
la que obtiene el número de serie del disco duro : GetVolumeInformation

Declare Function GetVolumeInformation Lib "kernel32" Alias "GetVolumeInformationA" (ByVal _


lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize _
As Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long, _
lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal _
nFileSystemNameSize As Long) As Long

Aquí ya se ha complicado un poco la cosa. Pero tras un análisis detenido veremos que esa
complicación es sólo aparente.

En primer lugar vemos que la librería donde está esta función es, como en la función Sleep, el
kernel32 . Esto quiere decir que la librería kernel32 contiene varias funciones. Pero ¿qué es la
librería kernel32 ? Ni mas ni menos que una DLL llamada kernel32.dll que puede encontrar en
el directorio C :\WINDOWS\SYSTEM, y que es el alma de Windows. (Kernell significa, como
muy bien sabrá, núcleo)

En segundo lugar, vemos que el nombre de esta función dentro de la DLL kernel32.dll es
GetVolumeInformationA, que es lo que figura entre comillas en la declaración. El nombre
GetVolumeInformation que figura como nombre de la función, al principio de la declaración,
es el nombre por el que nos vamos a referir a la función en nuestra aplicación. Ese nombre
puede cambiarse, cambiando también el nombre con el que vamos a llamar a esta función a lo
largo de nuestra aplicación. Esto se lo digo solamente a nivel informativo. No lo haga. Su
aplicación no podría ser interpretada por otra persona. No es profesional y quien mas
perderá por ello es Vd. Le hago especial hincapié en esto, porque es una forma de proteger
sus programas por parte de algunos programadores. Pero un analista experto encuentra
enseguida el truco. Y algunos no perdonan. Seamos profesionales

En tercer lugar, vemos que la declaración de esta función termina con la expresión As Long.
Esto significa que esta función devuelve un dato, y es concretamente, un Long. Por lo tanto, si
ese dato nos sirve para algo, podemos obtenerlo. Verá que no es necesario, pero en muchas
ocasiones, ese dato nos va a indicar si la función se ejecutó correctamente. Concretamente,
esta función devuelve un 0 si ha existido algún problema para obtener el número del disco, o un
número distinto de 0 si lo ha obtenido. Las demás constantes deberemos declararlas en el
procedimiento donde vamos a usar la función (o en otro lugar, si así lo exige el ámbito que les
queramos dar, pero generalmente, en el mismo procedimiento), e invocar la función pasándole
los parámetros correctos.

La sintaxis de las Apis va a ser distinta si deseamos obtener el valor que devuelve o no. Por
ejemplo, para la función anterior podemos poner perfectamente estas dos expresiones

Dim Respuesta as Long


Respuesta = GetVolumeInformation("C:\", volbuf, 255, serialnum, componentlength, sysflags, _
sysname, 255)

GetVolumeInformation("C:\", volbuf, 255, serialnum, componentlength, sysflags, _


sysname, 255)

En el ejercicio realizado para hacer estos apuntes, este código se metió en el procedimiento
click de un botón de comando.

‘Declaramos las variables. Observe que no tienen por qué tener el mismo nombre que en la
‘declaración de la función.

Dim volbuf As String


Dim sysname As String

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 2


Dim serialnum As Long ‘esta variable será la que contenga el número del disco
Dim sysflags As Long ‘lpFileSystemFlags
Dim componentlength As Long ‘lpMaximumComponentLength
Dim res As Long
volbuf = String(256, 0)
sysname = String(256, 0)

Estas variables son las que se van a pasar como parámetros a la función. La correspondencia
entre el nombre del parámetro y cada una de las variables es la siguiente :

lpRootPathName Se lo metemos directamente : “C :\” - no olvidar la barra \


lpVolumeNameBuffer volbuf Label del disco
nVolumeNameSize Tamaño del buffer anterior. Directamente 255
lpVolumeSerialNumber serialnum. Contendrá el número del disco
lpMaximumComponentLength componentlength
lpFileSystemFlags sysflags
lpFileSystemNameBuffer sysname
nFileSystemNameSizeTamaño buffer anterior. Directo, 255

res = GetVolumeInformation("C:\", volbuf, 255, serialnum, componentlength, sysflags, _


sysname, 255)

If res = 0 Then ' ha ocurrido un error y no puede leer el VOL


MsgBox ("Ha ocurrido un error al intentar arrancar la aplicación.")
Else 'lo ha leído perfectamente
VOLUM = Trim(Str(serialnum)) 'convertimos un Long en String
'si tiene menos de 12 caracteres, le añadimos los ceros necesarios por la izquierda
If Len(VOLUM) < 12 Then VOLUM = String(12 - Len(VOLUM), "0") & VOLUM
' lo presentamos en el TextBox TBVOL
TBVOL.Text = VOLUM
End If

Repasemos la declaración y veamos que es cada una de sus partes

Función GetVolumeInformation
Declare Function GetVolumeInformation Lib "kernel32" Alias "GetVolumeInformationA" (ByVal
lpRootPathName As String, ByVal lpVolumeNameBuffer As String, ByVal nVolumeNameSize As
Long, lpVolumeSerialNumber As Long, lpMaximumComponentLength As Long,
lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As String, ByVal
nFileSystemNameSize As Long) As Long

lpRootPathName
Es un string que contiene el directorio raíz del disco a analizar. Si es te parámetro es Null, se
toma el directorio raíz del directorio actual. Esta parámetro puede indicar el disco de un
servidor, en cuyo caso debe indicarse con dos backslash. (\\Servidor\Disco)

lpVolumeNameBuffer
Apunta a una variable que va a recibir el nombre del Label del disco. Hay que declararla con un
tamaño predeterminado, siempre mayor que el que va a tener el dato
p.e. Dim VolBuf As String * 255

nVolumeNameSize
Especifica la longitud de la variable anterior. No importa que la hayamos declarado ya con
determinado tamaño. Hay que poner aquí otra vez ese tamaño, que será el mismo que tenía
declarado la variable.

lpVolumeSerialNumber
Apunta a una variable tipo Long, donde se va a meter el número del disco.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 3


lpMaximumComponentLength
Apunta a una variable Long (En realidad se mete en esta variable el resultado de la
concatenación de dos bytes) donde se va a poner el número máximo de caracteres permitido
por el sistema de ficheros. Eeste número de caracteres corresponde con los caracteres
comprendidos entre dos backslahs. Si ese valor es 255 indica que el sistema de ficheros
soporta nombres largos. Si indica 8.3 solamente acepta nombres cortos

lpFileSystemFlags
Apunta a una variable tipo Long, (Igual que la anterior, concatenación de dos bytes) que
especifican los Flags asociados al sistema de ficheros. Puede ser la combinación de dos de los
siguientes parámetros (Excepción: FS_FILE_COMPRESSION y FS_VOL_IS_COMPRESSED
son mutuamente excluyentes).
FS_CASE_IS_PRESERVED
If this flag is set, the file system preserves the case of filenames when it places a name on disk.
FS_CASE_SENSITIVE
El sistema de ficheros diferencia mayúsculas y minúsculas.
FS_UNICODE_STORED_ON_DISK
FS_PERSISTENT_ACLS
FS_FILE_COMPRESSION
FS_VOL_IS_COMPRESSED
El disco especificado se trata de un disco comprimido. Por ejemplo, es un disco al que se le ha
aplicado DoubleSpace.

lpFileSystemNameBuffer
Apunta a una variable tipo string, donde se mete el sistema de ficheros soportado (FAT or
NTFS). Esta variable debe declararse con un número prefijado de caracteres, siempre superior
al que vaya a tener realmente (p.e. Dim SysName As String * 255)

nFileSystemNameSize
Especifica el número de caracteres de la variable anterior. Debe introducirse el mismo número
con el que se ha declarado la longitud de esa variable. (255 en el ejemplo).

Ya vamos viendo que las APIs no son tan difíciles de entender. Vamos a ver otra, la inversa de
la anterior, que pone el valor al parámetro Label del disco. Observe que el resto de los
parámetros no pueden variarse ya que vienen marcados en el disco (número) o implícitos en el
sistema operativo.

Función SetVolumeLabel
Private Declare Function SetVolumeLabel Lib "kernel32" Alias "SetVolumeLabelA" (ByVal
lpRootPathName As String, ByVal lpVolumeName As String) As Long

lpRootPathName
Variable tipo string donde se introduce el directorio raíz del disco al que se le va a poner o
cambiar el Label. Si este parámetro es Null, se entiende que es el raíz del directorio actual.

lpVolumeName
Variable tipo string que contienen el Label a poner. Si es Null, borra el Label actual.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 4


Vemos que no es tan complicado operar con funciones API. Para trabajar con APIs solamente
es necesario conocer la sintaxis exacta de la declaración. Pero parece en principio un poco
difícil, a sabiendas de que deben existir varios cientos de APIs. SOLUCION : Que VB nos
aporte un chuleta con todas las declaraciones.

Esta chuleta no es otro que el Visor de Texto API (API Text Wiever en Inglés). Este es un
programa que se distribuye con Visual Basic y que se instala al tiempo que este, formando
parte del mismo grupo de programas. Haciendo clic en su icono aparece esta ventana :

Haciendo Click sobre la palabra Archivo de la Barra de Menú, aparecen unos ficheros que
contienen las declaraciones de las funciones API :

Win32Api.txt
Winmmsys.txt

Estos dos ficheros son los que suministra Microsoft con VB6. El primero contiene las
declaraciones de las funciones API no relacionadas con el tema multimedia. El segundo
contiene las declaraciones de las API relacionadas con este tema de multimedia.

Si ha adquirido el libro de Appleman puede tener otro fichero : Api32.txt. El autor de este libro
asegura que es mucho mas completo que el fichero que entrega Microsoft. De hecho contiene
bastantes mas declaraciones.

API-Guide presenta también la declaración del API para ser copiada directamente en el
portapapeles y pegada en nuestra aplicación. Últimamente es la única que uso, ya que al
tiempo, se obtienen una explicación de cada uno de los parámetros.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 5


Estos ficheros están en ASCII. Puede convertirlos a una base de datos ACCESS y el acceso
será un poco más rápido. No olvide que también le ocupará un sitio respetable en el disco duro
de su ordenador.

Para obtener una o varias declaraciones, seleccione las funciones en la ventana de arriba del
visor, haga click en Agregar y esa función le pasará para la ventana de abajo. Una vez que
tenga en esa ventana todas las funciones que necesita, haga click en el botón Copiar y las
declaraciones completas le pasarán al portapapeles.

Una vez que ya sabemos donde se pueden copiar las declaraciones de las APIs, veamos una
que nos permitirá obtener la hora desde el sistema operativo :

Declare Sub GetSystemTime Lib "kernel32" Alias "GetSystemTime" (lpSystemTime _


As SYSTEMTIME)

Ahora nos surge una duda ¿Qué es SYSTEMTIME ? Es una variable que hay que declararla
con la instrucción Type, igual que hacíamos con las variables con varios campos en los ficheros
tipo Random. Repase este capítulo si no lo tiene claro.

Para poder declarar esta variable, podemos obtener su declaración del mismo Visor de Texto
API

Para ello, en la ventana Tipo API en vez de figurar Declaraciones debe poner Tipos. Busque
esta opción desplegando la ventana con la flecha que tiene a la derecha. Busque ahora la
variable cuya declaración quiere conocer. Repitiendo el proceso anterior, se llevará en el
portapapeles la declaración de la variable :

Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Haga un pequeño ejercicio para obtener la fecha y hora usando un API :

Para ello debemos introducir un módulo donde definiremos la variable SYSTEMTIME y donde
podemos declarar la función GetSystemTime :

Módulo 1 Declaraciones

Option Explicit
Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type

Declare Sub GetSystemTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 6


Ahora en un Formulario, ponemos 8 TextBox (uno para cada datos) y un botón de comando,
donde ponemos el siguiente código:

Private Sub Command1_Click()


Dim Pepe As SYSTEMTIME ‘Pepe es una variable del tipo SYSTEMTIME
GetSystemTime Pepe
Text1 = Str(Pepe.wYear)
Text2 = Str(Pepe.wMonth)
Text8 = (Pepe.wDayOfWeek)
Text3 = Str(Pepe.wDay)
Text4 = Str(Pepe.wHour)
Text5 = (Pepe.wMinute)
Text6 = (Pepe.wSecond)
Text7 = (Pepe.wMilliseconds)
End Sub

Este programa nos mostrará la hora (hasta milésimas de segundo), la fecha y el día de la
semana.

Aún queda otro apartado en la ventana Tipo API : Las constantes.

En muchas declaraciones de funciones API se utilizan constantes, bien numéricas o


expresiones. El Visor de testo API nos muestra también las constantes que nos podemos
encontrar en las declaraciones. Vayan un par de Ejemplos

Public Const SCROLLLOCK_ON = &H40 ' The scrolllock light is on.


Public Const SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege"

Verá más adelante mucho más profusamente las declaraciones de constantes.

Vamos a entretenernos a lo largo de este capítulo, en el estudio de varias APIs, relacionadas


con el registro de Windows y con la presentación del icono en el System Tray. Se han puesto
estas porque son interesantes unas y llamativas las otras. Pero no son las más importantes, ni
las más difíciles. La más importante es la del día a día, que es la que nos va a proporcionar la
solución de nuestro programa. La más difícil siempre está por llegar.

Cuando necesite un API nuevo para su programa no se conforme con salir del paso. Eso se
logra con el libro de Appelman o con el API-Guide. Por experiencia propia, le recomiendo que
haga una pequeña aplicación para emplear esa o esas APIS que necesita. Estúdielas,
documéntelas y guárdelas donde guarda sus tesoros más preciados. Hágalo, pues al cabo de
una año sin volver a usarlas se le van a olvidar, y tendrá que ponerse nuevamente al día. Hay
APIs caprichosas, declaraciones que tienen que ser así, sin ninguna razón ni criterio para ello.
No duplique su trabajo. Créese una libreta de APIs y con el tiempo, llegará a dominarlas como
el mismísimo Appleman. Por mi parte, dejo como botón de muestra las que presento a
continuación.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 7


APIS DE WINDOWS Función Shell_NotifyIcon
Cómo presentar el icono de una aplicación en la zona de acceso rápido de la barra de tareas
(System Tray)

La zona de acceso rápido de la barra de tareas es la parte derecha de esta barra en la que se
encuentra el reloj. En el inglés original se denomina System Tray, y no encuentro en español
una palabra del argot informático que la defina.

Es muy cómodo, sobre todo para los programas que se deben estar ejecutando continuamente,
poner su icono en esta parte de la barra de tareas, ya que ocupan menos espacio que un
programa minimizado normalmente. Solamente se ve el icono representativo del programa.
Por ello es necesario elegir un icono que defina de forma sencilla e inequívoca la aplicación.
Generalmente se le pone un PopUp menú que muestra las opciones de restaurar (Muestra la
aplicación en su tamaño normal) y Salir. También se suele poner un TextToolTip para mostrar
de forma literal el nombre del programa minimizado en ese icono.

Fig. 1 - Icono del programa Wsk colocado en el System Tray con su ToolTipText y PopupMenú

Este sistema permite comunicar la aplicación con el icono creado en el System Tray.
Lógicamente, primero debe crearse el icono (Se crea normalmente en el Load del formulario
inicial de la aplicación), y permanece ahí mientras dura la ejecución de la aplicación. Si se
minimiza la aplicación, no aparece su icono en la barra de tareas. Por lo tanto se necesitará
algún artilugio para poder poner otra vez la aplicación en su estado normal. Esto se hace
mediante una comunicación entre el icono del System Tray y la aplicación. Esa comunicación
es lo que llamaremos mensaje de retorno. Al recibir ese mensaje de retorno, el formulario va a
tratarlo en uno de sus procedimientos. El procedimiento donde lo va a tratar se le especifica en
la llamada a la API como se verá mas adelante. El mensaje de retorno va a depender de lo que
se haga sobre el icono del System Tray, (Tecnología Windows) y es en principio un poco
complicado, pero verá también más adelante una breve explicación sobre este valor devuelto.
Para que el icono no siga en el System Tray una vez hayamos salido de la aplicación, es
necesario eliminarlo en el procedimiento Unload del formulario inicial.

El icono puede cambiarse en tiempo de ejecución, y es una de las aplicaciones más vistosas
de este sistema. Puede, por ejemplo, cambiar el color del icono para indicar que se ha recibido
un mensaje, que se está conectado, etc.

El API encargada de realizar esta función es Shell_NotifyIcon, cuya declaración es:

Public Declare Function Shell_NotifyIcon Lib "shell32" _ Alias "Shell_NotifyIconA" _


(ByVal dwMessage As Long, pnid As NOTIFYICONDATA) As Boolean

Vemos que la declaración incluye una variable definida por el usuario: NOTIFYCONDATA. Se
define de esta forma

Public Type NOTIFYICONDATA


cbSize As Long
hwnd As Long
uId As Long
uFlags As Long
uCallBackMessage As Long
hIcon As Long
szTip As String * 64
End Type

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 8


El primer parámetro que se le pasa a la función (dwMessage) le va a indicar la operación que
debe realizar, y puede tomar uno de los tres siguientes valores (Se indica también el nombre de
la constante que suele sustituir a esos valores):

Constante Valor Operación que realiza


NIM_ADD 0 Añade un icono al System Tray
NIM_MODIFY 1 Cambia el icono actual por otro
NIM_DELETE 2 Quita el icono del System Tray

El segundo parámetro (pnid) es una variable tipo NOTIFYICONDATA tal como se definió más
atrás. Vemos a continuación cada componente de esta variable:

CbSize Contiene el tamaño de la variable pnid. Generalmente se le pasa este


valor mediante la función Len aplicada a la propia variable. (Vea ejemplo)

Hwnd Es el controlador de la ventana sobre la que se va a aplicar la función


Shell_NotifyIcon. Si el código donde se llama a esta función está en el
formulario que se va a minimizar o restaurar, en esta parte basta con poner
Me.Hwnd.

UId Es el identificador del icono del System Tray. Puede ser cualquier
número Long. Solamente será necesario usar un número si hace falta
identificar ese icono para una operación posterior. Si no se va hacer ninguna
operación con él, caso más habitual, basta con poner vbNull como valor de esta
parte.

UFlags Un Long que va a indicar la validez de las tres partes siguientes de la


variable NOTIFYICONDATA. (uCallBackMessage, hIcon y szTip) Puede tomar
uno de estos valores (Se especifica el valor, la constante con la que se le suele
denominar y el resultado de usarla):

Constante Valor Resultado


NIF_MESSAGE 1 El identificador del mensaje de retorno será el
especificado en uCallBackMessage
NIF_ICON 2 El icono que se pone en el System Tray es el
que se le indica en hIcon.
NIF_TIP 4 El ToolTipText del icono del System Tray será
el especificado en szTip.

Uflags puede contener los tres valores, validando de esta forma las tres condiciones anteriores.
Es muy tipico ver que UFlags toma el valor:

.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE

Lo que estamos haciendo es que Uflags tome el valor de 2 Or 4 Or 1, siendo Or el operador


lógico Or. Esta operación nos lleva al resultado de 7.

Resumiendo: Uflags es en realidad un conjunto de tres banderas, que puede tomar el valor 1,
3 ó 7, según se haya aplicado NIF_MESSAGE (Pesa 1), NIF_ICON (Pesa 2) y NIF_TIP
(Pesa 4)

uCallBackMessage Identificador del mensaje de retorno. Este valor va determinar en que


procedimiento del formulario se va a procesar la información enviada
desde el icono. Si pone el valor Hexadecimal 200 (&H200) ese valor
enviado se procesará en el procedimiento MouseMove. Puede hacerlo
en otros. Puede ver en la siguiente lista el número que define a varios
procedimientos del formulario, y la constante con la que se definen
normalmente

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 9


El valor del mensaje de retorno va a depender de lo que se haga sobre
el icono del System Tray. Y es concretamente, el valor que
corresponde al evento que se realice sobre el icono (Mouse down,
Mouse Up, doble clic, etc) según los valores de la tabla siguiente. Eso
sí, estos valores se multiplican por el valor de la propiedad
TwipsPerPixelX del objeto Screen. Insisto, tecnología Windows que
queda fuera de este manual de Visual Basic.

Los valores de la siguiente tabla son aplicables para establecer en que


procedimiento va a procesar la información del mensaje de retorno,
como para conocer el valor de ese mensaje.

Constante Valor Procedimiento del formulario


WM_MOUSEMOVE &H200 MouseMove
WM_LBUTTONDOWN &H201 Mouse Down (botón izquierdo)
WM_LBUTTONUP &H202 MouseUp (botón izquierdo)
WM_LBUTTONDBLCLK &H203 Doble click (botón izquierdo)
WM_RBUTTONDOWN &H204 MouseDown (botón derecho)
WM_RBUTTONUP &H205 Mouse Up (botón derecho)
WM_RBUTTONDBLCLK &H206 Doble click (botón derecho)

HIcon Icono que presentará en el System Tray. Es muy normal poner el icono
del formulario inicial de la aplicación. Si el código de creación del icono
está es ese formulario (cosa muy normal) basta con poner aquí,
Me.Icon
SzTip Aquí debe poner el ToolTipText que quiere que aparezca cuando
mantiene el cursor del ratón unos instantes sobre el icono del System
Tray.

En la definición del la variable NOTIFYICONDATA se establecía que la longitud de SzTip era


de 64 caracteres (szTip As String * 64) No intente cambiar ese 64 por otro número porque no
funciona. Esto nos lleva a que si la cadena elegida para el ToolTipText no tiene 64 caracteres,
aparecerán unos espacios en blanco detrás de la cadena elegida. Para evitar este efecto,
añada a la cadena, la expresión & vbNullChar. Quedrá de esta forma:

.szTip = "Este es el ToolTipText" & vbNullChar

Una vez explicada la teoría de funcionamiento de esta función, vayamos directamente a un


ejemplo. Este ejemplo está metido en el mismo ejercicio del control Winsock (Wsk) y se ha
extraído aquí lo que se refiere a la aplicación de esta API. Puede cortarse y pegarse el código
a otra aplicación, con garantía total de funcionamiento.

EJERCICIO PRACTICO

En un módulo del programa, en la sección de declaraciones, debemos introducir la definición


de la variable NOTIFYICONDATA

Public Type NOTIFYICONDATA


cbSize As Long
hwnd As Long
uId As Long
uFlags As Long
uCallBackMessage As Long
hIcon As Long
szTip As String * 64
End Type

En el mismo módulo introducimos las constantes que va a requerir, tanto la llamada a la función
Shell_NotifyIcon como las que vamos a necesitar para analizar el mensaje de retorno:

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 10


Public Const NIM_ADD = &H0
Public Const NIM_MODIFY = &H1
Public Const NIM_DELETE = &H2
Public Const NIF_MESSAGE = &H1
Public Const NIF_ICON = &H2
Public Const NIF_TIP = &H4

Public Const WM_MOUSEMOVE = &H200


Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Public Const WM_LBUTTONDBLCLK = &H203
Public Const WM_RBUTTONDOWN = &H204
Public Const WM_RBUTTONUP = &H205
Public Const WM_RBUTTONDBLCLK = &H206

Y en ese mismo módulo declaramos la función Shell_NotifyIcon

Public Declare Function Shell_NotifyIcon Lib "shell32" Alias "Shell_NotifyIconA" _


(ByVal dwMessage As Long, pnid As NOTIFYICONDATA) As Boolean

Declaramos otra API, con la que podemos hacer que un formulario tome el foco, y se coloque
en primer plano: SetForegroundWindow

Public Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long

Ahora declaramos una variable llamada Nid, que será del tipo NOTIFYICONDATA

Public Nid As NOTIFYICONDATA

Ya hemos terminado con el código del módulo. Vayamos ahora al procedimiento Load del
formulario inicial. Además de todo lo que debamos hacer en ese procedimiento, le añadiremos
esta parte, correspondiente a la creación del icono en el System Tray

Procedimiento Load del formulario inicial:

Con estas dos líneas garantizamos que se va a presentar el formulario, si se hubiese abierto
con la instrucción Load

Me.Show
Me.Refresh

‘Damos los valores apropiados a cada una de las partes de la variable Nid

With Nid
.cbSize = Len(Nid)
.hwnd = Me.hwnd
.uId = vbNull
.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
.uCallBackMessage = WM_MOUSEMOVE
.hIcon = Me.Icon
.szTip = "SEAE - Conexión vía IP" & vbNullChar
End With

Llamamos a la función para crear el icono (Parámetro NIM_ADD) y le pasamos la variable Nid
con las características del icono

Shell_NotifyIcon NIM_ADD, Nid

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 11


Veamos una a una las líneas de esta variable tipo NOTIFYICONDATA

cbSize = Len(Nid) Tal como se dijo más atrás, cbSize contiene un Long con el tamaño de
la variable tipo NOTIFYICONDATA. Al poner cbSize = Len(Nid) se lo estamos pasando
mediante el cálculo que realiza la función Len.

hwnd = Me.hwnd Le pasa el .hwnd del formulario. Así Windows ya sabe a qué formulario
debe enviar el mensaje de retorno.

uId = vbNull Se le pasa un Null ya que no se va a necesitar el identificador del icono

uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE Se le está indicando que los


valores de las tres líneas siguientes son válidos. Pero esta línea merece una explicación más
detallada.

NIF_ICON es una constante que vale 2, NIF_TIP es otra constante que vale 4 y
NIF_MESSAGE es otra que vale 1. Por lo tanto, esta línea es exactamente igual a otra que
pusiese

uFlags = 2 Or 4 Or 1 Si realizamos esta operación matemática, el resultado es 7.

Por lo tanto, también sería igual poner

uFlags = 7

La razón de poner una constante en vez del valor es solamente a efectos didácticos durante la
programación. Es más sencillo acordarse de un neumónico que de un número. Es costumbre
de programación solamente. Y recuerde que esas constantes deben estar declaradas, cosa
que hicimos en el módulo, cuya declaración se repite aquí por facilidad de comprensión:

Public Const NIF_MESSAGE = &H1


Public Const NIF_ICON = &H2
Public Const NIF_TIP = &H4

uCallBackMessage = WM_MOUSEMOVE Con esta línea le estamos indicando el


Identificador del mensaje de retorno. Y concretamente le estamos diciendo que ese valor es
Hex 200 (Recuerde que declaramos la constante WM_MOUSEMOVE = &H200). Al ponerle
este valor, la información del mensaje de retorno la tratará en el procedimiento MouseMove del
formulario. Habría dado lo mismo poner uCallBackMessage = &H200 (O si lo prefiere
uCallBackMessage = 512 y así lo expresaríamos en decimal) Volvemos a lo de antes, es
costumbre utilizar constantes y no valores durante la programación. ¿Será porque parece que
eleva el nivel del programador?

hIcon = Me.Icon Con esta línea le decimos que el icono que debe presentar en el
System Tray es precisamente el icono de este formulario. Podríamos indicarle otro icono,
pasándole la propiedad Icon de otro objeto, o la propiedad picture de un picture box o control
image, siempre y cuando en esa propiedad hayamos puesto un icono.

szTip = "SEAE - Conexión vía IP" & vbNullChar Este es el ToolTipText que va a
aparecer cuando dejemos unos instantes el puntero del ratón encima del icono. Esta variable
tiene 64 caracteres, pues así se declaró – y no funciona si se declara de otro tamaño – en la
definición de la variable tipo NOTIFYICONDATA. Para evitar que aparezcan como espacios los
caracteres no usados, cosa que deja muy fea la presentación del ToolTipText, basta con poner
& vbNullChar tras el texto deseado.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 12


Con el código introducido en el Load del formulario inicial ya hemos puesto el icono en el
System Tray. Ahora ya podemos hacer clic sobre este icono (y otros eventos) para que el icono
envíe al formulario el mensaje de retorno. Ese mensaje será tratado en el procedimiento
MouseMove, puesto que así se lo hemos indicado a la función Shell_NotifyIcon con
uCallBackMessage = WM_MOUSEMOVE. En este procedimiento prevemos todos los
mensajes de retorno que puede recibir.

Evento MouseMove del formulario receptor de mensajes de retorno.

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

Este procedimiento espera recibir 4 parámetros, sin embargo solamente va a recibir 3. Por lo
tanto, el valor de Y será siempre nulo a lo largo de este procedimiento.

Dim Result As Long


Dim msg As Long
'El valor recibido en la posición X varía dependiendo del ScaleMode (Cosas de Windows)

If Me.ScaleMode = vbPixels Then


msg = X
Else
msg = X / Screen.TwipsPerPixelX
End If

El mensaje recibido del icono contiene un número. Si la propiedad ScaleMode del formulario
estuviese en Pixels, ese número coincidiría con el que genera el icono. Si ScaleMode no está
en Pixels, el valor generado lo multiplica por el valor de la propiedad TwipsPerPixelX. Las
líneas anteriores detectan el valor de la propiedad ScaleMode y actúan en consecuencia. Al
final tenemos un número que es el que metemos en la variable msg. Hacemos un Select Case
para obtener un resultado distinto en función del valor de msg

Select Case msg

Case WM_LBUTTONUP '514 (&H202) restaura el formulario


Me.WindowState = vbMaximized
Result = SetForegroundWindow(Me.hwnd)
Me.Show

Case WM_LBUTTONDBLCLK '515 (&H203) restaura el formulario


Me.WindowState = vbMaximized
Result = SetForegroundWindow(Me.hwnd)
Me.Show

Case WM_RBUTTONUP '517 (&H205) Presenta el PopUp Menú


Result = SetForegroundWindow(Me.hwnd)
Me.PopupMenu Me.mnuPopUpSys

End Select

End Sub

Solamente nos queda ver que números genera el icono para el mensaje de retorno. Son unos
valores que Windows ya tiene predispuestos, y que para mayor facilidad de programación ya
hemos introducido en unas constantes. Repetimos aquí el código de declaración de esas
constantes:

Public Const WM_MOUSEMOVE = &H200


Public Const WM_LBUTTONDOWN = &H201

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 13


Public Const WM_LBUTTONUP = &H202
Public Const WM_LBUTTONDBLCLK = &H203
Public Const WM_RBUTTONDOWN = &H204
Public Const WM_RBUTTONUP = &H205
Public Const WM_RBUTTONDBLCLK = &H206

(Recuerde que estos valores están en Hexadecimal). Por ejemplo, cuando hacemos clic en el
icono con el botón derecho (R), en el instante de bajar el botón (ButtonDown) se genera como
mensaje de retorno el número 204 en hexadecimal. Ese valor, que no depende de la
programación, sino que es un valor que Windows tiene prefijado, se lo asociamos a una
constante llamada WM_RBUTTONDOWN (La podríamos haber llamado de otra forma, pero
Microsoft la llama así, y si todos la llamamos así existirá cierta semejanza entre el código de
todos los programadores). Luego, en el procedimiento MouseMove del formulario, en vez de
preguntar si el mensaje de retorno vale &H204, preguntamos si vale WM_RBUTTONDOWN

Podemos hacer que el icono del System Tray cambie en determinadas circunstancias. Por
ejemplo, si el programa minimizado es un correo electrónico, podemos hacer que cambie de
color intermitentemente cuando se ha recibido un mensaje nuevo. Esto podemos hacerlo con
un control Timer, donde pondríamos un código parecido a esto:

Picture1 es un array de Pictures con Index del 0 al 3, y cada uno con una imagen del icono
distinta, para producir un efecto agradable. El código siguiente cambia el icono con 4 imágenes
distintas.

Private Sub Timer1_Timer()


If mnuOn.Checked Then
I = I + 1: If I > 4 Then I = 0
t.hIcon = Picture1(I).Picture
Shell_NotifyIcon NIM_MODIFY, t
End If
End Sub

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 14


VISUAL BASIC Y LAS APIS DE WINDOS – LAS APIS DEL
REGISTRO DE WINDOWS
El registro de Windows es sin duda el gran tabú de los programadores. Hay quien cree que si
se toca el registro, el PC nunca más va a volver a trabajar correctamente. Y desde luego no le
falta razón a quien así opina. No le falta razón, le falta matizar: Si se toca el registro de
Windows sin saber, es muy probable que el PC no vuelva a trabajar bien.

El único secreto que tiene el registro de Windows es NO TOCAR lo existente. Podemos


introducir cosas nuevas y borrar estas cosas nuevas. Y quédese tranquilo que Visual Basic,
solamente hará lo que le digamos que haga, por lo tanto podemos estar seguros que no tocará
nada de lo que nosotros no le hayamos indicado.

El registro puede manejarse mediante dos instrucciones de VB ya explicadas en capítulos


anteriores: SaveSettings y GetSettings. Y es lo que se dice en un curso básico de VB.
Cuando termine de leer este capítulo Vd. Mismo se convencerá que esas instrucciones son
solamente para empezar. Un programador profesional siempre usa APIS para leer y escribir el
Registro de Windows.

Va a encontrar en este capítulo una palabra muy repetida: Clave del registro. (Key) Una
clave del registro es una de las muchas informaciones que existen en el registro. Por
ejemplo:

HKEY_CURRENT_USER/Software/VB And VBA Program Settings/AgendaTel/Colores/Fondo

Esta clave contiene el color de fondo del formulario de la aplicación AgendaTel, y debe
contener un valor, concretamente el número que expresa el color de fondo citado. En el caso
del ejemplo, mi PC contenía el valor “8454016”, (contiene la cadena de caracteres 8454016,
no el número 8.454.016, por eso va entre comillas). Por eso, al tratar ese dato, debe hacerse
pensando que es un string.

Dim VarColor As String


VarColor = GetSetting(AppName:="AgendaTel", Section:="Colores", Key:="Fondo")
Me.BackColor = CLng(VarColor)

Lo mismo ocurre cuando se trate de una fecha.

Las claves se expresan con una estructura similar a la de las carpetas (directorios) del
explorador de Windows. No es que sea así, ya que el registro es un fichero único, pero su
estructura jerárquica es similar. Por eso, hablaremos de Claves y Subclaves, lo mismo que
hablamos de directorios y subdirectorios (Carpetas y Subcarpetas). Una clave tendrá
subclaves si hay más claves por debajo de su nivel jerárquico. En el ejemplo anterior, la clave

HKEY_CURRENT_USER/Software/VB And VBA Program Settings/AgendaTel

tiene la subclave Colores y esta a su vez tiene la subclave Fondo. La clave Fondo no tiene
subclaves.

Los datos de una clave pueden ser cadenas de


caracteres o numéricas. Puede diferenciar unas
de otras mediante el icono que aparece a su lado
al editar el registro. Una clave puede tener varios
datos. Cada dato tienen un nombre y un valor
(Nombre = fonts.fon y su valor vgafix.fon)

El valor de las claves numéricas puede ponerse


como binario puro, o como un Long (32 bits), y en
este caso, con estructura Big Endian (El byte de mayor peso está en último lugar) o Little
Endian. (El byte de menor peso está en último lugar). En cualquier caso las claves numéricas

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 15


el editor del registro las presenta en Hexadecimal. En la Fig. anterior, Hex(00000060) = 96 en
decimal. Las claves que son cadenas de caracteres las presenta entre comillas dobles.

Se enumeran a continuación las APIS relacionadas con el registro de 32 bits. Se incluye la


declaración de cada una de ellas y la explicación de cada uno de sus parámetros. Se omiten
aquellas que solamente funcionan en Windows 3.11 o que existen por compatibilidad con
Windows 3.11.

Podríamos clasificarlas en dos grupos, uno la de aquellas que sirven para leer y guardar
valores, y otro, con aquellas Apis que sirven para mantenimiento del registro.

La primera operación que debemos hacer para trabajar sobre una clave es abrirla
(Exceptuando lógicamente la operación de crearla). Para abrir una clave se usa el API
RegOpenKeyEx. Al final, una vez realizadas todas las operaciones deseadas, hay que
cerrarla. Se cierra mediante el API RegCloseKey

Handle = Manejador en español, pero esta es una de estas palabras que es mejor no
traducirlas. Utilizaremos la expresión Handle durante todo este capítulo.

Función RegOpenKeyEx
Abre la clave especificada. Es la primera operación que hay que hacer para trabajar con una
clave. (Es similar, en ficheros, a Open NombreFichero For xxx As #n)

Declaración:
Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As
Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long,
phkResult As Long) As Long

HKey es el Handle de la clave de nivel superior. Hkey acepta cualquiera de los valores
predefinidos:

HKEY_CLASSES_ROOT (= Hex 80000000)


HKEY_CURRENT_USER (= Hex 80000001)
HKEY_LOCAL_MACHINE (= Hex 80000002)
HKEY_USERS (= Hex 80000003)

(Observe en la declaración que Hkey es un Long. ¿Cómo podemos poner la cadena


HKEY_LOCAL_MACHINE en vez de un Long? Es que esa cadena es el nombre de una
constante que habrá que declarar. Lo que ocurre es que en Windows, esa constante siempre
tiene el mismo valor (Concretamente Hex 80000002). No declare una constante con ese mismo
nombre con un valor distinto a ese.)

LpSubKey es una variable que contiene el nombre de la subclave a abrir. Esta


variable es una cadena de caracteres, que indica la ruta total de la clave a abrir, desde
la clave expresada en el parámetro anterior. Si este parámetro es nulo o la cadena
vacía, la función creará un nuevo handle de la clave definida por el parámetro Hkey .
En este caso, la función no cierra el handle creado previamente.

UlOptions Reservado. Debe ser 0.

samDesired Especifica la máscara de seguridad para el acceso a esa clave. Puede


ser una combinación de los siguientes parámetros:

KEY_ALL_ACCESS, que es una combinación de KEY_QUERY_VALUE,


KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, KEY_CREATE_SUB_KEY,
KEY_CREATE_LINK, y KEY_SET_VALUE.
KEY_CREATE_LINK, permiso para crear un enlace simbólico.
KEY_CREATE_SUB_KEY, permiso para crear subclaves
KEY_ENUMERATE_SUB_KEYS, permiso para enumerar subclaves

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 16


KEY_EXECUTE, permiso para acceso de lectura.
KEY_NOTIFY, permiso para cambiar la notificación
KEY_QUERY_VALUE, permiso para obtener el valor de una subclave
KEY_READ, combinación de KEY_QUERY_VALUE,
KEY_ENUMERATE_SUB_KEYS, y KEY_NOTIFY.
KEY_SET_VALUE, permiso para escribir el valor de una subclave.
KEY_WRITE, combinación de KEY_SET_VALUE y KEY_CREATE_SUB_KEY

PhkResult, es una variable (Long) que va a recibir el Handle de la clave abierta.

Función RegCloseKey
Cierra el Handle de la clave que estamos utilizando. Es la operación que finaliza cualquier
operación de lectura o escritura en el registro. Si hacemos un símil con los ficheros
secuenciales, sería el Close #n

Declaración
Declare Function RegCloseKey Lib "advapi32.dll" Alias "RegCloseKey" (ByVal hKey As Long)
As Long

HKey es el Handle de la clave abierta. Es un Long. Este número es el que nos


devuelve la función RegOpenKeyEx (y la función RegCreateKeyEx) en el parámetro
PhkResult. (cuando abrimos o creamos una clave). Hkey acepta también cualquiera de
los valores predefinidos

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
(Estos valores son las constantes citadas en la función anterior.)

Función RegCreateKeyEx
Crea la clave especificada. Si existe ya esa clave, la abre.

Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" (ByVal hKey As


Long, ByVal lpSubKey As String, ByVal Reserved As Long, ByVal lpClass As String, ByVal
dwOptions As Long, ByVal samDesired As Long, lpSecurityAttributes As
SECURITY_ATTRIBUTES, phkResult As Long, lpdwDisposition As Long) As Long

hKey es el handle visto en las funciones anteriores.

LpSubKey es una variable tipo string que contiene el nombre de la subclave a crear.
La subclave no debe empezar por el carácter \. Este parámetro no puede ser nulo.

Reserved Reservado. Debe ser cero.

LpClass Variable tipo string que especifica el tipo de esta clave. Puede poner una
cadena vacía y el tipo de la clave se especificará cuando le introduzca el valor. Este
parámetro se ignora si la clave ya existe.

dwOptions Especifica las opciones especiales de la clave. Puede ser uno de los
siguientes valores:

REG_OPTION_NON_VOLATILE La clave es no volátil. Esta clave y su valor se


guardarán en el disco (al invocar la función RegSaveKey)
REG_OPTION_VOLATILE (Windows NT) Esta clave es volátil, es decir, no se
guarda en el disco. Solamente existe en la memoria RAM y una vez que se apaga el
ordenador se extingue. Este valor se ignora si ya existe la clave. (Windows 95) En
W95 este parámetro se ignora.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 17


REG_OPTION_BACKUP_RESTORE (Windows NT) Si se pone este valor, la función
ignora el parámetro samDesired. Este valor se pone para imponer restricciones de
acceso a la clave, tanto para hacer backup como para modificarla. (Windows 95)
EnW95 este valor se ignora, ya que el registro de W95 no soporta esa seguridad.

SamDesired Igual que en la función RegOpenKeyEx ya vista.

LpSecurityAttributes Es una variable del tipo SECURITY_ATTRIBUTES que


determina si las propiedades pueden ser heredadas por un proceso hijo. Si este valor
es Null (0) las propiedades no pueden ser heredadas. Este parámetro es típico verlo a
0.
La variable SECURITY_ATTRIBUTES tienen esta forma:
Public Type SECURITY_ATTRIBUTES
Length As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type

PhkResult Apunta a una variable que devuelve un Long con el Handle de la clave
creada o abierta, en caso de que ya existiera.

lpdwDisposition Apunta a una variable que devuelve un Long con uno de los
siguientes valores:

REG_CREATED_NEW_KEY ( = 1) si la clave no existía y se ha creado


REG_OPENED_EXISTING_KEY ( = 2) La clave ya existía y ha sido abierta
(Si este valor es 0 es que la operación no se ha completado con éxito)

Función RegDeleteKey
Borra una clave del registro de Windows. Funciona de forma distinta en W95 que en WNT. En
W95 borra esa clave y todas sus subclaves. En WNT no permite borrar una clave que tenga
subclaves.

Declare Function RegDeleteKey Lib "advapi32.dll" Alias "RegDeleteKeyA" (ByVal hKey As


Long, ByVal lpSubKey As String) As Long

HKey Es el Handle de una clave abierta. Puede ser también una de las siguientes
constantes:

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS

LpSubKey Es la subclave a borrar. Esta subclave debe estar dentro de la clave


especificada en el parámetro Hkey . Este parámetro no puede ser nulo. En WNT, la
clave a borrar no puede tener subclaves.

Aplicación práctica de lo visto hasta aquí: Crear una clave y eliminarla

Para crear una clave, primero hay que abrir todas las claves jerárquicamente superiores a esa
clave a crear. En este ejemplo vamos a crear una clave de la siguiente forma:

HKEY_CURRENT_USER/Software/GuiadelEstudiante/Colores

La clave HKEY_CURRENT_USER existe, ya que es una de las de más alto nivel. La clave
Software también existe, ya que vienen predeterminada. La que posiblemente no existe es la
de GuiadelEstudiante, y tampoco la de Colores dentro de ella. Estas claves no existirán en
principio, pero una vez que las hayamos creado, ya pueden existir. Por lo tanto deberemos

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 18


comprobar si existen. Esta clave va a tener varios valores. Se refiere a los valores del color de
un programa, y tendrá los colores de Fondo, Etiquetas, Letras y Desplegables. Aunque el color
se expresa con un número, aquí lo vamos a introducir de momento como una cadena de
caracteres.

El código para crear la clave lo introducimos en un botón de nombre cmdCrearClave

Private Sub cmdCrearClave_Click()


Dim LpClass As String
Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long, Disposicion As Long
'Abre la clave HKEY_CURRENT_USER/Software y obtiene el handle (Manejador1)
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
'Crea la clave GuiadelEstudiante como subclave de HKEY_CURRENT_USER/Software. Si ya
‘existe. la abre
RegCreateKeyEx Manejador1, "GuiadelEstudiante", 0, LpClass, _
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, Manejador2, Disposicion
'Si ha ocurrido algún error, Manejador2 sería 0
If Manejador2 = 0 Then
MsgBox "Error durante la creación de la clave!"
Exit Sub
End If
'Si no ha fallado (Manejador2 <> 0) creamos la clave Colores dentro de la clave
‘GuiadelEstudiante
RegCreateKeyEx Manejador2, "Colores", 0, LpClass, REG_OPTION_NON_VOLATILE, _
KEY_ALL_ACCESS, 0, Manejador3, Disposicion
'Si ha ocurrido algún error, Manejador3 sería 0
If Manejador2 = 0 Then
MsgBox "Error durante la creación de la clave!"
Exit Sub
End If
'Cierra la clave HKEY_CURRENT_USER/Software/GiadelEstudiante/Colores
RegCloseKey Manejador3
'Cierra la clave HKEY_CURRENT_USER/Software/GiadelEstudiante
RegCloseKey Manejador2
'Cierra la clave HKEY_CURRENT_USER/Software
RegCloseKey Manejador1
End Sub

Para eliminar una clave debemos utilizar la función RegDeleteKey. La clave inmediatamente
superior a la clave a borrar debe abrirse previamente con RegOpenKey. Y para abrir una clave,
debemos abrir previamente todas las claves jerárquicamente superiores. Para borrar una clave
con subclaves, borramos primero las subclaves. Deberemos ir borrando en orden ascendente.
Una vez borradas las claves, cerramos las claves superiores a la borrada. En el siguiente
ejemplo se borra la clave GuiadelEstudiante y su subclave, Colores

Private Sub cmdEliminarClave_Click()


Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long, Disposicion As Long
'Abre la clave HKEY_CURRENT_USER/Software y obtiene el handle (Manejador1)
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
'comprueba que la función se ha ejecutado correctamente. Si no es así, sale del
‘procedimiento
If Manejador1 = 0 Then Exit Sub
'Abre la clave GuiadelEstudiante que es subclave de la anterior
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
If Manejador2 = 0 Then Exit Sub
'Borra ya la clave Colores
RegDeleteKey Manejador2, "Colores"
'Cerramos la clave GuiadelEstudiante. Podríamos no cerrarla
RegCloseKey Manejador2
'Borramos la clave GuiadelEstudiante

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 19


RegDeleteKey Manejador1, "GuiadelEstudiante"
'Cerramos la clave HKEY_CURRENT_USER
RegCloseKey Manejador1
End Sub

Para que todas estas funciones puedan trabajar es necesario haberlas declarado previamente.
La declaración puede hacerse, o bien en la sección de declaraciones de un módulo, donde las
declararemos como Públicas, o en la sección de declaraciones de un formulario, donde deben
declararse como privadas. Las constantes también se declararán en el mismo sitio que las
funciones. En este caso, se declararon en el formulario:

Option Explicit
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003
Const HKEY_CURRENT_CONFIG = &H80000005
Const ERROR_NO_MORE_ITEMS = 259&
Const REG_OPTION_NON_VOLATILE = 0
Const KEY_SET_VALUE = &H2
Const REG_OPTION_BACKUP_RESTORE = 4
Const REG_OPTION_VOLATILE = 1
Const STANDARD_RIGHTS_ALL = &H1F0000
Const SYNCHRONIZE = &H100000
Const READ_CONTROL = &H20000
Const STANDARD_RIGHTS_READ = (READ_CONTROL)
Const STANDARD_RIGHTS_WRITE = (READ_CONTROL)
Const KEY_CREATE_LINK = &H20
Const KEY_CREATE_SUB_KEY = &H4
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const KEY_QUERY_VALUE = &H1
Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or _
KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) And (Not SYNCHRONIZE))
Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE Or _
KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE))
Const KEY_EXECUTE = (KEY_READ)
Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE Or _
KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or KEY_ENUMERATE_SUB_KEYS Or _
KEY_NOTIFY Or KEY_CREATE_LINK) And (Not SYNCHRONIZE))

Private Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal _


hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As _
Long, phkResult As Long) As Long
Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long _
Private Declare Function RegOpenKey Lib "advapi32.dll" Alias "RegOpenKeyA" (ByVal hKey _
As Long, ByVal lpSubKey As String, phkResult As Long) As Long
Private Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" (ByVal _
hKey As Long, ByVal dwIndex As Long, ByVal lpName As String, lpcbName As Long, ByVal _
lpReserved As Long, ByVal LpClass As String, lpcbClass As Long, lpftLastWriteTime As Any) _
As Long
Private Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" (ByVal _
hKey As Long, ByVal dwIndex As Long, ByVal lpValueName As String, lpcbValueName As _
Long, ByVal lpReserved As Long, lpType As Long, lpData As Any, lpcbData As Long) As Long
Private Declare Function RegCreateKeyEx Lib "advapi32.dll" Alias "RegCreateKeyExA" _
(ByVal hKey As Long, ByVal lpSubKey As String, ByVal Reserved As Long, ByVal LpClass _ As
String, ByVal dwOptions As Long, ByVal samDesired As Long, lpSecurityAttributes As _ Long,
phkResult As Long, lpdwDisposition As Long) As Long
Private Declare Function RegDeleteKey Lib "advapi32.dll" Alias "RegDeleteKeyA" (ByVal _
hKey As Long, ByVal lpSubKey As String) As Long

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 20


Vamos a ver ahora como se introducen los datos en las claves. Para introducir cada dato,
debemos introducir el nombre y el valor de cada uno de ellos.

Función RegSetValueEx

Guarda un dato en una clave previamente abierta. Con esta función, puede también modificar
el valor y el tipo de información que almacena ese dato.

Declaración
Declare Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As
Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData
As Any, ByVal cbData As Long) As Long

(Si declara el parámetro lpData como String, debe pasarlo por valor - By Val lpData As String)

hKey es el Handle de la clave. Será el valor devuelto por la función RegOpenKeyEx


en el parámetro phkResult.

lpValueName es el nombre del dato que va a introducir o modificar

Reserved debe ponerse a 0

dwType Tipo de información almacenada. Acepta estos valores

REG_BINARY ( = 3) Cualquier tipo de datos binarios


REG_DWORD ( = 4) Un número de 32 Bits (Long)
REG_DWORD_LITTLE_ENDIAN (= 4) Un número de 32 bits, en formato Little-endian.
(El byte más significativo es el byte de mayor orden) Este es el formato utilizado
por los ordenadores que utilizan W 95 o W NT. Por eso el valor es el mismo (4)
que para un Long
REG_DWORD_BIG_ENDIAN ( = 5) Un número de 32 bits, ordenado según el formato
Big-endian. (El byte más significativo es el byte de menor orden)
REG_EXPAND_SZ ( = 2) Una cadena de caracteres que representa variables de
entorno (Por ejemplo, “%PATH%”). Debe terminar con el carácter nulo.
REG_LINK ( = 6) Un link simbólico Unicode.
REG_MULTI_SZ ( = 7) Un array de cadenas de caracteres. Debe terminar con el
carácter nulo.
REG_NONE ( = 0) Valor sin definir
REG_SZ ( = 1) Una cadena de caracteres

lpData Variable que contiene el valor del dato a almacenar

cbData Especifica el número de caracteres que tiene el dato pasado en el


parámetro lpData. Cuando el dato termina en el carácter nulo, cbData
debe incluir este carácter nulo.

Función RegQueryValueEx
Devuelve el tipo y el valor de un dato almacenado en una clave abierta.

Declaración
Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey
As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As
Any, lpcbData As Long) As Long
(Si se declara el parámetro lpData como String, debe pasarse por Valor -By Val).

hKey Es el handle de una clave abierta.

lpValueName Variable que contienen el nombre del dato a obtener

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 21


lpReserved Reservado. Debe ser Null

lpType Apunta a una variable que va a recibir el tipo de dato que contiene. Es un Long,
y devolverá uno de los valores descritos para el valor dwType de la función
RegQueryValueEx. Este valor debe ponerse a Null si no se necesita conocer el tipo de
dato.

lpData Apunta a una variable que recibirá el valor del dato. Esta variable, cuando es
tipo string, hay que declararla con un tamaño prefijado, siempre mayor que el tamaño
del dato que va a recibir (Por ejemplo, Dim MiVariable as String * 100 Se entiende que
el dato que se va a meter en MiVariable nunca será superior a 100 caracteres) También
puede declararse como String sin tamaño, y ponerle posteriormente el tamaño igual a
lpcbData. Vea la explicación de esto en el ejemplo. Si no se necesita conocer el valor
del dato, debe ponerse Null

lpcbData Apunta a una variable que guardará el tamaño del copiado en lpData.
Si la variable lpData no tienen tamaño suficiente para almacenar el valor del dato,
lpcbData devolverá el valor ERROR_MORE_DATA ( = 234)

NOTA: Esta función tiene un comportamiento un poco irregular. Cuando se ejecuta la


primera vez no lee el valor de lpData. Sí lee los demás parámetros. Hay que ejecutarla 2
veces seguidas para que pueda leer ese parámetro.

Sigamos con el ejemplo práctico. Vamos a introducir y leer valores en una clave.

Para poner el valor a un dato de una clave, esa clave debe estar abierta. Se irán abriendo las
claves de forma jerárquica hasta llegar a la clave deseada. Una vez abierta esa clave, se
utilizará la función RegSetValueEx. Luego se cierran todas las claves en orden inverso a la
apertura.

Private Sub cmdPonerValor_Click()

Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long


Dim Var_lpValueName As String, Var_lpData As String, Var_cbData As Long

Var_lpValueName = TbNombreDato ‘TbNombreDato y TbValorClave son dos TextBox


Var_lpData = TbValorClave ‘donde se introducen el nombre del dato y su
Var_cbData = Len(Var_lpData) ‘valor respectivamente.

RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1


If Manejador1 = 0 Then Exit Sub ‘Comprueba que se ha abierto correctamente
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
If Manejador2 = 0 Then Exit Sub
RegOpenKeyEx Manejador2, "Colores", 0, KEY_ALL_ACCESS, Manejador3
RegSetValueEx Manejador3, Var_lpValueName, 0, REG_SZ, ByVal Var_lpData, ByVal _
Var_cbData

RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1

End Sub

Para leer un dato hay que proceder de forma similar, abriendo jerárquicamente las claves hasta
llegar a la clave a leer, y una vez abierta, proceder con la función RegQueryValueEx.
Recuerde que esta función hay que ejecutarla 2 veces para conseguir el valor del dato.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 22


Private Sub cmdLeerValor_Click()
Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long
Dim Var_lpValueName As String, Var_lpType As Long, Var_lpData As String * 100,
Var_lpcbData As Long
Dim Resp As Long
Var_lpValueName = Trim(TbNombreDato)
Var_lpType = 1
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
If Manejador1 = 0 Then Exit Sub
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
If Manejador2 = 0 Then Exit Sub
RegOpenKeyEx Manejador2, "Colores", 0, KEY_ALL_ACCESS, Manejador3
If Manejador3 = 0 Then Exit Sub

RegQueryValueEx Manejador3, Var_lpValueName, 0, Var_lpType, ByVal Var_lpData, _


Var_lpcbData
'Esto es la irregularidad de este API. La primera vez no lo lee. Hay que repetir la función
RegQueryValueEx Manejador3, Var_lpValueName, 0, Var_lpType, ByVal Var_lpData, _
Var_lpcbData

RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1

LValorClaveRet = Trim(Var_lpData)
LTamanoValor = Var_lpcbData

End Sub

Podemos aprovechar la primera ejecución de la función para leer el dato lpcbData que nos
indica el tamaño de lpData. A continuación hacemos que la variable Var_lpData tenga ese
tamaño, rellenando tantos caracteres con espacios. Es otra forma de hacer lo mismo.

Private Sub cmdLeerValor2_Click()


Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long
Dim Var_lpValueName As String, Var_lpType As Long, Var_lpData As String, Var_lpcbData As
Long
Dim Resp As Long
Var_lpValueName = Trim(TbNombreDato)
Var_lpType = 1
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
If Manejador1 = 0 Then Exit Sub
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
If Manejador2 = 0 Then Exit Sub
RegOpenKeyEx Manejador2, "Colores", 0, KEY_ALL_ACCESS, Manejador3
If Manejador3 = 0 Then Exit Sub
'Leemos el valor de Var_lpcbData
RegQueryValueEx Manejador3, Var_lpValueName, 0, Var_lpType, ByVal Var_lpData,
Var_lpcbData
'Hacemos que Var_lpData tenga el tamaño obtenido en Var_lpcbData
Var_lpData = String(Var_lpcbData, " ")
RegQueryValueEx Manejador3, Var_lpValueName, 0, Var_lpType, ByVal Var_lpData,
Var_lpcbData
RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1
LValorClaveRet = Var_lpData
LTamanoValor = Var_lpcbData
End Sub

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 23


Vamos a ver ahora como podemos borrar un valor. Se usa para ello la función RegDeleteValue

Función Function RegDeleteValue


Elimina un valor dentro de una clave.

Declaración
Declare Function RegDeleteValue Lib "advapi32.dll" Alias "RegDeleteValueA" (ByVal hKey As
Long, ByVal lpValueName As String) As Long

HKey Es el handle de una clave abierta. (Es el valor phkResult obtenido en la


función RegOpenKeyEx)

LpValueName Es una variable que contienen el nombre del valor a eliminar.

En este ejemplo, se elimina el valor cuyo nombre esté escrito en TbNombreDato

Private Sub cmdEliminarValor_Click()


Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long
Dim Var_lpValueName As String
Var_lpValueName = TbNombreDato
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
RegOpenKeyEx Manejador2, "Colores", 0, KEY_ALL_ACCESS, Manejador3
RegDeleteValue Manejador3, Var_lpValueName
RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1
End Sub

RegEnumValue
Esta función devuelve el nombre y el valor de cada uno de los datos contenidos dentro de una
clave previamente abierta. Esta función devuelve el nombre y valor de uno solo de los datos,
por lo que será necesario recurrir a una serie de llamadas en un bucle para obtenerlos todos.

Declaración
Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" (ByVal hKey As
Long, ByVal dwIndex As Long, ByVal lpValueName As String, lpcbValueName As Long,
lpReserved As Long, lpType As Long, lpData As Any, lpcbData As Long) As Long

hKey Handle de la clave abierta.

DwIndex Es una variable donde le indicamos el Indice correspondiente al valor a


devolver. El primero debe ser el 0, y se irá incrementando en 1 en cada lectura. Dado
que los datos no están ordenados, puede devolverlos en cualquier orden.

lpValueName Apunta a una variable que recibe el nombre del dato, incluido el
carácter nulo de terminación.

lpcbValueName Apunta a una variable Long que va a recibir el dato con el


tamaño del parámetro lpValueName. Este dato incluye el carácter nulo de terminación.

lpReserved Reservado. Debe ser Null

lpType Apunta a una variable que va a contener el tipo de dato que se va a


obtener. Los valores que se pueden obtener son los mismos que los
indicados para el parámetro dwType de la función RegSetValueEx
lpData Apunta a una variable que va a recibir el valor del dato.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 24


lpcbData Apunta a una variable Long que va a recibir el tamaño del dato a
devolver en lpData.

Esta función devuelve el valor 0 cuando se ha desarrollado correctamente, y el valor 259


cuando no hay valores que mostrar en el índice indicado. Aprovecharemos el valor devuelto
para saber cuándo debemos dejar el bucle de lectura.

En el siguiente ejemplo, se muestran en un ListBox (List1) el índice, el nombre del dato y el


valor del dato. Esta función tienen algunas particularidades (Hay muchas APIs con
particularidades similares). Las variables que van a contener datos tipo string hay que
declararlas con un tamaño superior al que van a tener (P.e. Var_lpValueName As String * 255) o
rellenarlas previamente con un número de caracteres superior al tamaño que van a tener. El
carácter ideal son los espacios, ya que luego se pueden eliminar con Trim [Se realiza en las
líneas Var_lpValueName = Space(255) y Var_lpData = Space(255)] y las variables Long que
almacenan el tamaño de las variables string hay que darles un valor superior al que van a tener
en la función (Var_lpcbValueName = 255 y Var_lpcbData = 255) Observe que se usó la sintaxis
de VarLong = Función (Parámetros) para que pueda devolvernos el número 0 ó 259,
dependiendo de si hay o no hay elementos con ese índice.

Private Sub cmdVerValores_Click()


Dim Manejador1 As Long, Manejador2 As Long, Manejador3 As Long
Dim I As Long, Var_lpValueName As String, Var_lpcbValueName As Long
Dim Var_lpType As Long, Var_lpData As String, Var_lpcbData As Long
Dim Resp As Long
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
RegOpenKeyEx Manejador2, "Colores", 0, KEY_ALL_ACCESS, Manejador3

Do While Resp = 0
Var_lpValueName = Space(255)
Var_lpData = Space(255)
Var_lpcbValueName = 255
Var_lpcbData = 255
Resp = RegEnumValue(Manejador3, I, Var_lpValueName, Var_lpcbValueName, 0, Var_lpType,
ByVal Var_lpData, Var_lpcbData)
Var_lpValueName = Left(Var_lpValueName, Var_lpcbValueName)
Var_lpData = Left(Var_lpData, Var_lpcbData)
List1.AddItem I & " - " & Trim(Var_lpValueName) & " - " & Trim(Var_lpData)
I=I+1
Loop

RegCloseKey Manejador3
RegCloseKey Manejador2
RegCloseKey Manejador1
End Sub

Función RegEnumKeyEx

Devuelve los nombres de las subclaves de una clave del registro previamente abierta. Esta
función obtiene el nombre de una única subclave cada vez que se le llama, por lo que para
leerlos todos, es necesario recurrir a llamadas en bucle.

Declaración
Declare Function RegEnumKeyEx Lib "advapi32.dll" Alias "RegEnumKeyExA" (ByVal hKey As
Long, ByVal dwIndex As Long, ByVal lpName As String, lpcbName As Long, lpReserved As
Long, ByVal lpClass As String, lpcbClass As Long, lpftLastWriteTime As FILETIME) As Long

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 25


hKey Manejador de la clave bajo la cual están las subclaves a enumerar

dwIndex Es el índice de la subclave a investigar. Como esta función obtiene


solamente el valor de una de las subclaves, será necesario recurrir a un bucle para
investigarlas todas. El índice pasado para la primera llamada debe ser el 0, ya que los
índices van de 0 a n-1. El orden de los índices dentro de las subclaves es arbitrario,
por lo que también será arbitrario el orden con el que nos va a devolver esta función los
valores de las subclaves.

lpName Es el buffer donde la función nos devolverá el nombre de la subclave.


(Incluye un carácter nulo como terminación) Este buffer va a contener solamente el
nombre de la clave, no su dirección jerárquica completa.

lpcbName Es el buffer donde se le indica a la función el tamaño del buffer lpName, y


una vez que la función se ha ejecutado, contendrá el número de caracteres real (sin el
carácter nulo de terminación) del valor almacenado en lpName.

LpReserved Reservado. Debe ser siempre Nulo.

LpClass Es el buffer que contiene la clase del subclave a devolver. Este es el


mismo valor introducido en el parámetro LpClass en la función RegCreateKeyEx. Si no
se necesita que el valor devuelto sea una clase, este valor debe ponerse Null.

LpcbClass Es el buffer que contiene el tamaño (en caracteres) previsto para LpClas.
Una vez ejecutada la función, contiene el tamaño exacto de LpClas sin contar el
carácter nulo de terminación. Este parámetro debe ser Null solamente cuando
LpClass sea Null

LpftLastWriteTime Es el buffer donde devolverá la fecha y hora del último cambio de


la subclave. Sale un número incomprensible. No es fácil ver que sea la fecha del
cambio de la clave

Esta función devuelve 0 si se ha ejecutado perfectamente, y 259 si la posición correspondiente


al índice no tienen clave alguna.

En el siguiente ejemplo se ve como obtener en nombre de las subclaves de una clave.

Private Sub cmdVerClaves_Click()


Dim Manejador1 As Long, Manejador2 As Long
Dim I As Long, Val_lpName As String, Val_lpcbName As Long
Dim Var_lpftLastTime As Long, Var_LpClass As String, Var_lpcbClass As Long
Dim Resp As Long
RegOpenKeyEx HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, Manejador1
RegOpenKeyEx Manejador1, "GuiadelEstudiante", 0, KEY_ALL_ACCESS, Manejador2
Do While Resp = 0
Val_lpName = Space(255)
Var_LpClass = Space(255)
Val_lpcbName = 255
Var_lpcbClass = 255
Resp = RegEnumKeyEx(Manejador2, I, Val_lpName, Val_lpcbName, 0, Var_LpClass,
Var_lpcbClass, Var_lpftLastTime)
Val_lpName = Left(Val_lpName, Val_lpcbName)
List1.AddItem I & " - " & Trim(Val_lpName)
I=I+1
Loop
RegCloseKey Manejador2
RegCloseKey Manejador1
End Sub

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 26


Hay más APIS para el control del registro. En este curso creemos que ya hay materia suficiente
para tener una idea del manejo del registro (Creación de claves, eliminación, Lectura y
escritura) Con los ejemplos descritos en este capítulo hay materia suficiente para poder
realizar todas las funciones que una aplicación normal pueda hacer sobre el registro.

El uso de Apis en una aplicación la hace generalmente más rápida ya que aprovecha recursos
ya existentes en Windows y además compilados. Tiene un pequeño inconveniente cuando se
hace un desarrollo que va a funcionar sobre distintas versiones de Windows. No es normal que
ocurra, pero hay que comprobar que un programa diseñado en W95 corre perfectamente en
W2000. Y es que algunas Apis no son exactamente iguales.

APIS PARA EL CONTROL DE LA IMPRESORA. EnumPrinters


Por otra parte, programar con Apis, aunque es muy elegante y aporta mucho caché al
programador, tampoco hay que pasarse. En el ejemplo que sigue se utilizan Apis para el
control de la impresora (Hay varias) y una de ellas nos permite conocer las impresoras
disponibles en el sistema: EnumPrinters

Función EnumPrinters

Declaración
Declare Function EnumPrinters Lib "winspool.drv" Alias "EnumPrintersA" (ByVal flags As Long,
ByVal name As String, ByVal Level As Long, pPrinterEnum As Long, ByVal cdBuf As Long,
pcbNeeded As Long, pcReturned As Long) As Long

Vamos a necesitar definir un tipo de variable:


Private Type PRINTER_INFO_1
flags As Long
pDescription As String
pName As String
pComment As String
End Type

Y declarar una constante


Const PRINTER_ENUM_LOCAL = &H2

El procedimiento donde ensayamos esta función permite presentar en un ListBox (LbPrinters)


todas las impresoras disponibles:

Esta rutina ha sido copiada, traducida, y ligeramente modificada de:


KPD-Team 1999
URL: http://www.allapi.net/
E-Mail: KPDTeam@Allapi.net
Gracias

Private Sub cmbListarPrintersLocales_Click()

Dim LongBuffer() As Long


' Array que almacena los punteros del búffer donde se encuentra la información de la impresora
Dim PrintInfo() As PRINTER_INFO_1
'Array con estructura tipo PRINTER_INFO_1 donde se guardarán los valores de cada
parámetro de las impresoras
Dim NumBytes As Long
'Este es el tamaño que le asignamos al buffer donde la API almacenará los datos. Debe ser
mayor que los bytes que vamos a obtener
Dim NumNecesario As Long
'Variable donde se introducirá el número exacto de bytes necesario para el buffer

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 27


Dim NumPrinters As Long
'Variable donde se introducirá el número de impresoras encontradas
Dim C As Integer, RetVal As Long
'C = Contador variable, RetVal = Valor de retorno de la función
'Obtiene la información de las impresoras locales (PRINTER_ENUM_LOCAL)
NumBytes = 3000
'Debe ser suficientemente largo para contener la información de todas las impresoras (Sin
pasarse)
ReDim LongBuffer(0 To NumBytes / 4) As Long
'ReDimensiona el array. Observe que un Long son 4 bytes
RetVal = EnumPrinters(PRINTER_ENUM_LOCAL, "", 1, LongBuffer(0), NumBytes,
NumNecesario, NumPrinters)
If RetVal = 0 Then
'RetVal será 0 si la función no se ha podido realizar correctamente. o más probable es que
hayamos puesto un número muy pequeño para NumBytes. Ahora ya conocemos el número de
Bytes necesario para contener toda la información ese número es NumNecesario. Hacemos
que NumBytes sea igual a NumNecesario
NumBytes = NumNecesario
ReDim LongBuffer(0 To NumBytes / 4) As Long 'Ahora ya es suficientemente largo
RetVal = EnumPrinters(PRINTER_ENUM_LOCAL, "", 1, LongBuffer(0), NumBytes,
NumNecesario, NumPrinters)
If RetVal = 0 Then ' Si vuelve a fallar ya es por otro motivo!
MsgBox "No se pueden enumerar las impresoras"
Exit Sub ' Se sale de este procedimiento
End If
End If
' Ahora mete el contenido de LongBuffer() en PrintInfo()
If NumPrinters <> 0 Then ReDim PrintInfo(0 To NumPrinters - 1) As PRINTER_INFO_1
'PrintInfo contiene a su vez 4 variables que almacenan toda la información de la impresora
'Recordamos la definición de PrintInfo
'Private Type PRINTER_INFO_1
' flags As Long
' pDescription As String
' pName As String
' pComment As String
'End Type

For C = 0 To NumPrinters - 1 'Bucle que coloca cada juego de información de LongBuffer() en


cada elemento de PrintInfo.Longbuffer(4 * c) = .flags, longbuffer(4 * c + 1) = .pDescription, etc.
'Para los valores tipo String, previamente se rellenan de espacios con la función Space y luego
les introduce el valor obtenido mediante la función lstrcpy.
PrintInfo(C).flags = LongBuffer(4 * C)
PrintInfo(C).pDescription = Space(lstrlen(LongBuffer(4 * C + 1)))
RetVal = lstrcpy(PrintInfo(C).pDescription, LongBuffer(4 * C + 1))
PrintInfo(C).pName = Space(lstrlen(LongBuffer(4 * C + 2)))
RetVal = lstrcpy(PrintInfo(C).pName, LongBuffer(4 * C + 2))
PrintInfo(C).pComment = Space(lstrlen(LongBuffer(4 * C + 3)))
RetVal = lstrcpy(PrintInfo(C).pComment, LongBuffer(4 * C + 3))

Next C
' Presenta el nombre de las impresoras
For C = 0 To NumPrinters - 1
LbPrinters.AddItem PrintInfo(C).pName
Next C
End Sub

El código no está nada mal. Aunque lleva comentarios con


el fin de entender el funcionamiento de esta función, hay
que reconocer que es largo. El resultado puede verse en la
figura. Están todas las impresoras disponibles.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 28


Pero vamos a ver otro código que hace lo mismo sin usar Apis.

Private Sub cmdBuscarPrinters_Click()


Dim I As Integer
For I = 0 To Printers.Count - 1
LbBuscaPrinters.AddItem Printers(I).DeviceName
Next I
End Sub

El resultado es el de la figura de la izquierda. Seis


líneas frente a dos páginas. El buen programador usa
la sencillez de código como su mejor arma. Y si se
pueden usar 6 líneas en vez de 30, hay que usar 6
líneas.

La razón de que la propiedad DeviceName del objeto


Printer nos dé el nombre de las impresoras es que ese
código largo de la página anterior ya está
implementado en Visual Basic.

Solamente nos falta satisfacer la curiosidad de cómo


se selecciona una impresora (botón inferior) ¿Con Apis? Se puede. ¿Desea escribir otras dos
páginas de código, o le es suficiente con este?

Private Sub cmdSeleccionarPrinter_Click()


If LbBuscaPrinters = "" Then
MsgBox "Debe elegir una impresora"
Exit Sub
End If

Dim sImpresora As String, pPrinter As Printer


sImpresora = LbBuscaPrinters.Text
' Buscamos la impresora selecionada en la lista entre todas las impresoras existentes
For Each pPrinter In Printers
If UCase(pPrinter.DeviceName) = UCase(sImpresora) Then
Set Printer = pPrinter
'A partir de ahora, pPrinter será la impresora elegida a la que VB dirigirá la
‘impresión cada vez que citemos Printer.Print.
Exit For
End If
Next
End Sub

De las APIs queda mucho por estudiar. Pero ya debe ser el alumno quien ahonde en ello según
sus propias necesidades. Creo que con lo expuesto hay suficiente para empezar.

LSB Visual Basic Guía del Estudiante Cap. 17 Pág. 29