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) ó
LSB
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.
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 NIM_ADD NIM_MODIFY NIM_DELETE
Valor 0 1 2
Operación que realiza Añade un icono al System Tray Cambia el icono actual por otro 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 NIF_MESSAGE
Valor 1
NIF_ICON
2
NIF_TIP
4
Resultado El identificador del mensaje de retorno será el especificado en uCallBackMessage El icono que se pone en el System Tray es el que se le indica en hIcon. 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
LSB
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
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 WM_MOUSEMOVE WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCLK WM_RBUTTONDOWN WM_RBUTTONUP WM_RBUTTONDBLCLK HIcon
SzTip
Valor &H200
Procedimiento del formulario MouseMove &H201 Mouse Down (botón izquierdo) &H202 MouseUp (botón izquierdo) &H203 Doble click (botón izquierdo) &H204 MouseDown (botón derecho) &H205 Mouse Up (botón derecho) &H206 Doble click (botón derecho)
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 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 HKEY_CURRENT_USER HKEY_LOCAL_MACHINE HKEY_USERS
(= Hex 80000000) (= Hex 80000001) (= Hex 80000002) (= 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 Var_lpData = TbValorClave Var_cbData = Len(Var_lpData)
‘TbNombreDato y TbValorClave son dos TextBox ‘donde se introducen el nombre del dato y su ‘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:
[email protected] 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