Uso del API de Windows: Manejo del Registro (y VI)

27/03/2020

 

Índice

 

Ya llegamos al final de esta serie de artículos sobre el API de Windows. Para terminar hemos elegido casi el mismo tema con el que empezamos, la forma de guardar información sobre nuestra aplicación u obtener información sobre otras aplicaciones. En los meses de febrero y marzo vimos como hacer esto por medio de ficheros .INI, este mes veremos como utilizar el Registro (registry) de Windows.

¿Que es el Registro de Windows?

Es una base de datos con una estructura jerárquica donde las aplicaciones pueden guardar información sobre su configuración y el sistema operativo almacena información sobre si mismo.

Antes, las aplicaciones y el sistema operativo utilizaba los archivos .INI, pero su proliferación y su escasa estructuración hicieron que Microsoft se plantease dar un sistema mucho más práctico para guardar los datos de configuración de las aplicaciones.

El primer Registro se incluye en Windows 3.1, pero es en Windows NT y Windows 95 cuando se nos ofrece un auténtico Registro de Configuraciones.

El Registro de Windows 3.1 está muy limitado y es necesaria la utilización de los ficheros .INI. Por su parte Windows 95 y Windows NT siguen soportando los ficheros .INI, duplicando en muchos casos los datos del Registro en este tipo de ficheros, pero se basan en el Registro de Configuraciones.

Vamos a realizar una aproximación a las funciones que disponemos para acceder a los Registros de Configuración de Windows.

 

Estructura del Registro

Tal y como hemos dicho, el Registro tiene una estructura jerárquica. Está jerarquía se basada en una serie de claves (keys) que pueden contener valores u otras claves.

En Windows 3.1 las claves sólo pueden contener un valor, pero en Windows 95 y Windows NT las claves contienen tantos valores como se quiera, identificados cada uno de ellos por una etiqueta.

Si quiere ver el Registro de Windows 3.1 debe ejecutar REGEDIT /V. Es importante la inclusión del parámetro, pues nos permite ver el Registro con su estructurar jerárquica. En Windows 95 deberá ejecutar REGEDIT y en Windows NT REGEDIT32.

Como vemos los Registros de Windows 3.1, Windows 95 y Windows NT varían en cuanto a su formato, y como veremos ahora, también varían en cuanto a su contenido.

 

Claves principales

En Windows 3.1 sólo se dispone de una entrada principal al Registro, identificada por el nombre HKEY_CLASSES_ROOT. A partir de esta raíz cuelgan el árbol con todas las entradas del Registro.

Windows 3.1 guarda información sobre los ficheros registrados y sobre OLE, pero las aplicaciones pueden hacer uso de él para guardar su configuración.

En Windows 95 y Windows NT el Registro tiene una conjunto de entradas principales colgando de una raíz, denominada ROOT. La lista de las entradas de Windows 95 y Windows NT se pueden ver en la figura 1.

Fig. 1 - Entradas del Registro

Win16 y Win32s

HKEY_CLASSES_ROOT - ficheros registrados y OLE - ficheros registrados y OLE

Win95

HKEY_CLASSES_ROOT - por compatibilidad con Win16 - por compatibilidad con Win16

HKEY_CURRENT_USER - configuración del usuario actual - configuración del usuario actual

HKEY_LOCAL_MACHINE - configuración de esta máquina - configuración de esta máquina

HKEY_USERS - configuración por defecto de usuarios - configuración por defecto de usuarios HKEY_CURRENT_CONFIG - configuración actual de esta máquina - configuración actual de esta máquina

HKEY_DYN_DATA - estado dinámico de esta máquina - estado dinámico de esta máquina

WinNT

HKEY_CLASSES_ROOT - por compatibilidad con Win16 - por compatibilidad con Win16

HKEY_CURRENT_USER - configuración del usuario actual - configuración del usuario actual

HKEY_LOCAL_MACHINE - configuración de esta máquina - configuración de esta máquina

HKEY_USERS - configuración por defecto de usuarios - configuración por defecto de usuarios

 

Como podemos ver, existen dos entradas en el Registro que sólo están disponibles en Windows 95: HKEY_CURRENT_CONFIG y HKEY_DYN_DATA. Estas entradas están especialmente pensadas para obtener información sobre dispositivos Plug&Play y para gestionar la conexión y desconexión de dispositivos en portátiles.

Debemos saber que algunas entradas del Registro están `mapeadas', por ejemplo, HKEY_CLASSES_ROOT corresponde internamente a la entrada HKEY_LOCAL_MACHINE\Software\Classes, pues esta entrada está soportada sólo por compatibilidad con Windows 3.1.

Si estamos trabajando en Windows 3.1 con un producto Win32s como es Visual FoxPro 3.0, debemos ser consciente de que aun cuando disponemos de parte de las funciones del API de Win32, el Registro es el de Windows 3.1 y por lo tanto las entradas que hagamos o consultemos deben ser compatibles con este sistema operativo.

No vamos a describir aquí la interesante información que podemos encontrar en las distintas entradas del Registro, pues nos centraremos en las funciones de acceso al Registro. Para obtener información sobre la información a la que podemos acceder lo mejor es consultar los “Resource Kit” de Windows 95 y Windows NT.

 

Tipos de información

Todos los datos que pueden almacenarse en las distintas entradas de este Registro de Windows 3.1 son de tipo carácter y, como hemos dicho, sólo puede haber uno por clave.

En Windows 95 y Windows NT la información almacenada en el Registro pueden ser de los siguientes tipos:

    *** Tipo no definido
  	#define REG_NONE 0

  	*** Cadena de caracteres
  	#define REG_SZ 1

  	*** Binario
  	#define REG_BINARY 3

  	*** Número de 32 bits
  	#define REG_DWORD 4

  	*** Número de 32 bits
  	*** con formato Little-Endian
  	#define REG_DWORD_LITTLE_ENDIAN 4

  	*** Número de 32 bits
  	*** con formato Big-Endian
  	#define REG_DWORD_BIG_ENDIAN 5

  	**** Enlace simbólico de Unicode
  	#define REG_LINK 6

  	*** Matriz de cadenas
  	#define REG_MULTI_SZ 7

  	*** Lista de recursos de dispositivo
  	#define REG_RESOURCE_LIST 8

Los tipos más utilizados son los de cadenas de caracteres y de número, pero debemos estar preparados para encontrarnos datos de cualquier tipo si consultamos información de entradas del Registro no creadas por nosotros mismos.

 

Donde guardar nuestra configuración

Debemos pensar en que tipo de información queremos guardar y en que plataforma nos encontramos. La información relacionada con la instalación de nuestro programa, por ejemplo, ubicación de directorios, si estamos en Windows 95 y Windows NT, deben almacenarse dentro de HKEY_LOCAL_MACHINE\Software.

Si la configuración que queremos guardar está relacionada con un determinado usuario, por ejemplo, la posición donde dejó una ventana o su última configuración del menú, y seguimos en Windows 95 o Windows NT, deberemos trabajar con HKEY_CURRENT_USER\Software para el usuario actual y HKEY_USERS\.Default\

Software para definir los valores por defecto de todos los usuarios que se puedan crear.

Por convención, dentro de la clave \Software cada empresa inscribe una clave con el nombre de su empresa, de la que cuelgan las claves de cada programa registrado y a partir de aquí la claves con la versión del mismo.

Si nuestra empresa se llama “XXXX”, nuestra aplicación “ZZZZ” y está en la versión 1.0 deberemos hacer unas entradas como estas:

     HKEY_CURRENT_USER\Software\Xxxx\Zzzz\1.0
     HKEY_LOCAL_MACHINE\Software\Xxxx\Zzzz\1.0
     HKEY_USERS\Software\Xxxx\Zzzz\1.0

Si nos encontramos en Windows 3.1, bien desde FoxPro 2.x o en Visual FoxPro 3.0 con Win32s, deberemos usar siempre HKEY_CLASSES_ROOT. Normalmente, después de esta clave crearemos una entrada con el nombre del producto y una subclave con su versión.

También es posible crear una estructura con una las entradas Software\Empresa\Producto\Version, pero es poco habitual usar una entrada tan larga en el Registro de Windows 3.1.

 

Como obtener información

Las funciones del API de Windows se utilizan siempre en una secuencia similar:

  1. - se realiza la apertura de la clave
  2. - se opera sobre ella o sus valores
  3. - se cierra.

Para abrir una la clave podemos utilizar la siguiente función:

     DECLARE ;
  	   Integer RegOpenKeyEx ;
  	   IN WIN32API ;
  	   Integer nKey, ;
  	   String cSubKey, ;
  	   Integer nReserved, ;
  	   Integer nSamDesired, ;
  	   Integer @nResult          

Para usarla debemos pasar primero el número que identifica una clave superior, normalmente una de la claves básicas que podemos identificar con las definiciones siguientes:

     #define HKEY_CLASSES_ROOT -2147483648
  	 #define HKEY_CURRENT_USER -2147483647
     #define HKEY_LOCAL_MACHINE -2147483646
     #define HKEY_USERS -2147483645
     #define HKEY_CURRENT_CONFIG -2147483653
     #define HKEY_DYN_DATA -2147483654

Después pasamos una cadena con el nombre de la clave que queremos abrir, un 0, una máscara de seguridad y por último una variable por referencia donde se almacenará el número de la clave abierta.

La máscara de seguridad se parece mucho a los permisos que tenemos cuando abrimos un fichero a bajo nivel. Los tipos de permisos de acceso los podemos ver en las siguiente definiciones:

     #define KEY_QUERY_VALUE        1
     #define KEY_SET_VALUE          2
     #define KEY_CREATE_SUB_KEY     4
     #define KEY_ENUMERATE_SUB_KEYS 8
     #define KEY_NOTIFY            16
     #define KEY_CREATE_LINK       32
     #define KEY_READ          1+8+16
     #define KEY_WRITE            2+4
     #define KEY_EXECUTE KEY_READ
     #define KEY_ALL_ACCESS 1+2+4+8+16+32  

Una vez abierta la clave podemos obtener la información que nos interesa utilizando la siguiente función:

     DECLARE ;
       Integer RegQueryValueEx ;
       IN WIN32API ;
       Integer nKey, ;
       String cValueName, ;
       Integer nReserved, ;
       Integer @nType, ;
       String @cData, ;
       Integer @nSizeData

Debemos pasar el número que obtenemos con RegOpenKey(), el nombre de la subclave que queremos leer, un 0, una variable por referencia donde se nos devolverá el tipo del valor y, también por referencia un buffer, y el tamaño de este buffer.

Una vez que terminemos de operar con la clave debemos cerrarla por medio de la siguiente función:

     DECLARE ;
       Integer RegCloseKey ;
       IN WIN32API ;
       INTEGER nKey          

Todas las funciones devuelve un 0 si todo a funcionado correctamente u otro valor si existe algún problema.

Ahora ya podemos hacer un pequeño programa de ejemplo. En este programa obtendremos una de la información que graba Visual FoxPro sobre su configuración para el usuario activo.

    #include "registro.h"
 	DO registro.prg

  	nKey = 0
  	=RegOpenKeyEx( HKEY_CURRENT_USER, ;
  	  "Software\Microsoft\VisualFoxPro\3.0\Options",;
  	  0, ;
  	  KEY_READ, ;
  	  @nKey )

  	nType = 0
  	nSize = 255
  	cValor = REPLICATE( CHR(0), nSize )

  	=RegQueryValueEx( nKey, "_BROWSER", 0, @nType, @cValor, @nSize )
  	=RegCloseKey( nKey )
  	? SUBSTR( cValor, 1, nSize-1 )  

 

Como crear y escribir

Para crear una entrada en el Registro se utiliza esta función:

     DECLARE ;
       Integer RegCreateKeyEx ;
       IN WIN32API ;
       Integer nKey, ;
       String cSubKey, ;
       Integer nReserved, ;
       String cClass, ;
       Integer nOptions, ;
       Integer nDesired, ;
       String @cSecurityAttributes, ;
       Integer @nResult, ;
       Integer @nDisposition

Por medio de esta función crearemos y/o abriremos una entrada en el Registro. Si ya existe la entrada, esta se abre, si no existe, esta se crea. Para saber cual es la acción realizada podemos ver el valor devuelto en el último parámetro pasado por referencia, que puede tener estos dos valores:

     #DEFINE REG_CREATED_NEW_KEY 1
     #DEFINE REG_OPENED_EXISTING_KEY 2

Para introducir un valor dentro de una clave utilizamos la función:

     DECLARE ;
       Integer RegSetValueEx ;
  	   IN WIN32API ;
  	   Integer nKey, ;
  	   String cValueName, ;
  	   Integer nReserved, ;
  	   Integer nType, ;
  	   String cData, ;
  	   Integer nSizeData  

Si el valor existe se reemplaza, pero si no existe se crea otra entrada con este valor. Si se pasa como segundo parámetro una cadena nula, el valor se incluye a nivel de clave.

Veamos un pequeño ejemplo que nos mostrará la forma de usar estas dos funciones:

     #include "registro.h"
  	 DO registro.prg

  	 nKey = 0
  	 nResult = 0

  	 =RegCreateKeyEx( HKEY_LOCAL_MACHINE, ;
  	  "Software\MiEmpresa\MiProducto\2.0", ;
  	  0, ;
  	  0, ;
  	  REG_OPTION_NON_VOLATILE, ;
  	  KEY_ALL_ACCESS, ;
  	  0,;  	  @nKey, ;
  	  @nResult )

  	 IF nResult == REG_OPENED_EXISTING_KEY
  	   WAIT WIND "Entrada abierta..."
  	 ELSE
  	  WAIT WIND "Entrada creada..."
  	ENDIF

  	= RegSetValueEx( nKey, "Config", ;
  	  0, REG_SZ, "creada", 5 )

  	= RegCloseKey( nKey )

 

Ejemplo: Guardar la posición de una ventana

Por medio de estas pocas funciones podemos hacer muchas cosas. Para ejemplificarlo vamos a crear una ventana que por medio de los eventos Load y UnLoad obtiene y guarda la posición de la ventana y de esa forma el usuario siempre la vea donde la dejó.

Este ejemplo es muy parecido al que dimos en el mes de marzo, pero en ese caso trabajábamos sobre ficheros .INI y ahora sobre el Registro de Configuraciones.

    #include "registro.h"
  	#define EMPRESA "MiEmpresa"
  	#define PRODUCTO "Ejemplos"
  	#define VERSION "1.0"

  	*** Cargar la Form y mostrarla
  	PUBLIC oEjemplo3
  	oEjemplo3 = CREATEOBJECT( "Ejemplo3" )
 	oEjemplo3.Show()
  	RETURN

  	*** Form del ejemplo en forma de clase
  	DEFINE CLASS Ejemplo3 AS FORM

  	  BackColor = RGB(192,192,192)
  	  Caption = "Ejemplo sobre Registry"
  	  Name = "Ejemplo3"

  	  *** Botón para cerrar ***
  	  ADD OBJECT cmdAceptar ;
 	    AS COMMANDBUTTON WITH ;
  	    Top = 6, ;
  	    Left = 6, ;
  	    Height = 29, ;
  	    Width = 94, ;
  	    Caption = "Aceptar"

  	  *** Evento al crear la form
  	  PROCEDURE Load
  	    *** Cargar las declaraciones
  	    DO Registro

  	    *** Obtener los valores con
  	    *** los que se cerró la última vez
  	    nKey = 0
  	    WITH This
  	      IF RegOpenKeyEx( HKEY_CURRENT_USER, ;
  	        "Software\"+;
  	          EMPRESA+"\"+;
  	          PRODUCTO+"\"+;
  	          VERSION+"\"+;
  	          .name, ;
  	        0, ;
  	        KEY_ALL_ACCESS, ;
  	        @nKey ) == NO_ERROR

  	        nType = 0
  	        cValor = REPLICATE( CHR(0), 4 )
  	        =RegQueryValueEx( nKey, "Top", 0, ;
  	          @nType, @cValor, 4 )
  	        .Top = CTOWORD(cValor)

  	        nValor = 0
  	        =RegQueryValueEx( nKey, "Left", 0, ;
  	          @nType, @cValor, 4 )
  	        .Left = CTOWORD(cValor)

  	        nValor = 0
  	        =RegQueryValueEx( nKey, "Width",0, ;
  	          @nType, @cValor, 4 )
  	        .Width = CTOWORD(cValor)

  	        nValor = 0
  	        =RegQueryValueEx( nKey, "Height",0,;
  	          @nType, @cValor, 4 )
  	        .Height = CTOWORD(cValor)
  	      ENDIF
  	    ENDWITH
  	  ENDPROC

  	  *** Evento al destruir la form
  	  PROCEDURE Unload
  	    *** Cargar las declaraciones
  	    *** pueden haberse descargado
  	    DO registro

  	    *** Si está minimizado o maximizado
  	    *** restaurar al tamaño normal
  	    This.WindowState = 0

  	    *** Abrir o crear la clave si no existe
  	    nKey = 0
  	    nResult = 0

  	    WITH This
  	      IF RegCreateKeyEx(HKEY_CURRENT_USER,;
  	        "Software\"+;
  	          EMPRESA+"\"+;
  	          PRODUCTO+"\"+;
  	          VERSION+"\"+;
  	        .name, ;
  	        0, ;
  	        0, ;
  	        REG_OPTION_NON_VOLATILE, ;
  	        KEY_ALL_ACCESS, ;
  	        0,;
  	        @nKey, ;
  	        @nResult ) == NO_ERROR

  	        *** Guardar los valores de
  	        *** posición y tamaño
  	        =RegSetValueEx( nKey, "Top", 0, ;
  	          REG_DWORD, WORDTOC(.Top), 4 )
  	        =RegSetValueEx( nKey, "Left", 0, ;
  	          REG_DWORD, WORDTOC(.Left), 4 )
  	        =RegSetValueEx( nKey, "Width", 0,;
  	          REG_DWORD, WORDTOC(.Width), 4 )
  	        =RegSetValueEx( nKey, "Height",0,;
  	          REG_DWORD, WORDTOC(.Height), 4)
  	      ENDIF
  	    ENDWITH
  	  ENDPROC

  	  *** Método para cerrar la form
  	  PROCEDURE cmdAceptar.Click
  	    ThisForm.Release()
  	  ENDPROC
  	ENDDEFINE

  	*** Combierte un número en un buffer
  	PROCEDURE WORDTOC
  	LPARAMETER nNumber
  	RETURN CHR(BITAND(255,nNumber))+;
  	  CHR(BITAND(65280,nNumber)%255)+;
  	  CHR(BITAND(16711680,nNumber)%255)+;
  	  CHR( BITAND(4278190080,nNumber)%255)

  	*** Combierte un buffer en un número
  	PROCEDURE CTOWORD
  	LPARAMETER cBuffer
  	RETURN ASC(SUBSTR(cBuffer,1,1))+;
  	  ASC(SUBSTR(cBuffer,2,1))*256+;
  	  ASC(SUBSTR(cBuffer,3,1))*65536+;
  	  ASC(SUBSTR(cBuffer,4,1))*16777216
  

Una de las características a resaltar de este ejemplo, es la creación de dos funciones, WORDTOC y CTOWORD para poder pasar un número en un buffer del tipo cadena de caracteres. Visual FoxPro no puede definir un tipo void o any y los buffers deben ser de tipo String.

 

Diferencias entre los Sistemas Operativos

Los programas anteriores sólo son válidos en Windows 95 y Windows NT, si quiere ejecutarlos en Windows 3.1 desde FoxPro 2.x o Visual FoxPro con Win32s se encontrará que no funcionan, por varios motivos.

Por una parte hemos trabajado con las configuraciones del Registro propias de Windows NT y Windows 95, recuerde lo que decíamos en los apartados sobre las “claves principales” y sobre “donde guardar nuestra configuración” acerca las diferencias de claves entre las distintas versiones de Windows. Si trabajamos sobre Windows 3.1 debemos usar siempre HKEY_CLASSES_ROOT.

Por otra parte, no todas las funciones están disponibles en todas las plataformas. Si observa la figura 2 puede ver que funciones están disponibles en que versiones de Windows.

Fig. 2 - Funciones del Registro soportadas por Windows

Función

Win16

Win32s

Win95

WinNT

RegCloseKey

X

X

X

X

RegConnectRegistry

-

-

X

X

RegCreateKey

X

Usar RegCreateKeyEx

RegCreateKeyEx

-

X

X

X

RegDeleteKey

X

X

X

X

RegDeleteValue

-

-

X

X

RegEnumKey

X

Usar RegEnumKeyEx

RegEnumKeyEx

-

X

X

X

RegEnumValue

-

X

X

X

RegFlushKey

-

-

X

X

RegGetKeySecurity

-

-

-

X

RegLoadKey

-

-

X

X

RegNotifyChangeKeyValue

-

-

-

X

RegOpenKey

X

Usar RegOpenKeyEx

RegOpenKeyEx

-

X

X

X

RegQueryInfoKey

-

-

X

X

RegQueryMultipleValues

-

-

X

-

RegQueryValue

X

Usar RegQueryValueEx

RegQueryValueEx

-

X

X

X

RegReplaceKey

-

-

X

X

RegRestoreKey

-

-

-

X

RegSaveKey

-

-

X

X

RegSetKeySecurity

-

-

-

X

RegSetValue

X

Usar RegSetValueEx

RegSetValueEx

-

X

X

X

RegUnLoadKey

-

X

X

X

 

En Windows 3.1 utilizaríamos la función RegCreateKey en vez de RegCreateKeyEx, RegOpenKey en vez de RegOpenKeyEx, RegQueryValue en vez de RegQueryValueEx y RegSetValue en vez de RegSetValueEx.

Otra limitación, que ya comentamos, es que en Windows 3.1 cada clave sólo puede tener un valor, por lo que en el ejemplo anterior deberíamos hacer que todas las entradas fueran claves.

Por último debemos recordar que los datos que puede manejar Windows 3.1 se reducen al tipo carácter.

Para saber en que sistema operativo estamos podemos usar la función OS(1) de FoxPro y Visual FoxPro y a partir de esta información bifurcar nuestro código.

 

Más posibilidades

Como puede ver en la figura 2, las posibilidades del API de Windows sobre el Registro de configuraciones no se limitan a las aquí presentadas. Pero en la mayoría de las veces con estas pocas funciones uno puede trabajar sin problemas.

Si quiere todas las declaraciones de las funciones para el manejo del Registro de Configuraciones puede verlas en el disco que acompaña a la revista.

Sobre su uso puede consultar el fichero de ayuda sobre el API de Win32 de la versión profesional en CD-ROM de Visual FoxPro.

 

Encapsular las funciones del API

Tal y como hemos venido proponiendo, es muy conveniente cubrir las funciones del API de Windows con funciones propias que aíslen nuestro código de llamadas directas.

Es muy sencillo construir una función para leer del Registro de Configuraciones y otra para escribir en él. También puede hacer una función que lea de ficheros .INI o del Registro dependiendo del sistema operativo o utilice unas funciones u otras dependiendo también del sistema operativo en el que nos encontremos.

Cada uno podrá estudiar que tipo de encapsulación le es más conveniente para su proyecto.

 

Conclusión

Antes de terminar quisiera aconsejarles prudencia. Debemos tener cuidado con las escrituras en claves del Registro que no sean de nuestra aplicación. Aun cuando podemos manejar información de otras aplicaciones y el propio sistema operativo modificando entradas en el Registro, debemos ser muy cuidadosos a la hora de utilizar este método.

La lectura de las entradas del Registro es casi siempre inofensiva, pero debemos estar seguros de que sabemos interpretar su contenido en todos los sistemas operativos a fin de no provocar confusión.

Como hemos dicho, no hemos descrito todas las posibilidades del Registro, sobre todo no hemos descrito las posibilidades de obtener información sobre el Sistema Operativo, Usuarios, etc. por este medio. Le invito a seguir estudiando por su cuenta las funciones y entradas que se nos ofrecen en el Registro.

Si quiere iniciarse en los secretos del Registro de Windows puede leer el capítulo 33 del Resouce Kit de Windows 95 y el capítulo 10 del Resouce Kit de Windows NT. Son realmente aclaratorios. Si desea avanzar en el conocimiento del API de Windows puede ver el fichero de ayuda sobre el API de Win32 de la versión profesional en CD-ROM de Visual FoxPro o bien la magnífica información y ejemplos contenidos en los CD-ROM del Microsoft Developer Network (MSDN) o los manuales de Visual C++ 4.x.

Espero que estos últimos seis artículos sobre el API de Windows hayan sido de su interés.

 


 

Vea también

Uso del API de Windows: Índice general

Tema principal API

 

Descarga

Uso del API de Windows: Manejo del Registro (y VI)

 


 

 

 



error: Contenido protegido