Uso del API de Windows: Funciones para ficheros .INI (II)
27/03/2020
Índice
Tal y como prometimos en el artículo anterior, vamos a entrar a fondo en algunas funciones realmente útiles del API de Windows para ser usadas desde Visual FoxPro (VFP). En este caso vamos a referirnos al conjunto de funciones que nos permiten crear y consultar los ficheros de configuración de Windows, los ficheros .INI.
¿Donde guardar nuestra configuración?
Antes o después todos nos hacemos esta pregunta, queremos guardar información sobre la posición de una ventana, el último usuario que entró en la aplicación, guardar las distintas opciones, etc.. Las respuestas pueden ser muy variadas, pero en principio existen tres métodos:
1. Bases de Datos: con este sistema guardamos nuestra configuración en una tabla creada por nosotros con este fin. En ella almacenamos toda la información que necesitamos para configurar la aplicación, con las ventajas que da el uso de un tabla de FoxPro.
El problema es que no podemos editarla con facilidad. Si necesitamos ver o modificar el contenido del fichero de configuración debemos o bien ejecutar el VFP y abrir la tabla o bien hacer alguna pantalla especial de mantenimiento para la misma.
Un buen ejemplo de este tipo de configuración es el fichero de recursos de Visual FoxPro, normalmente llamado FOXUSER.DBF y donde FoxPro guarda gran parte de los datos sobre su propia configuración.
[Nota: nosotros podemos también utilizar el fichero de recursos para nuestros intereses abriéndolo con USE AGAIN e introduciendo en él lo que queramos. Este fichero no tiene por qué llamarse FOXUSER, basta con utilizar SET RESOURCE TO para utilizar otro.]
2. Ficheros .INI: el sistema que habitualmente utilizan los programas de Windows para almacenar los datos de su configuración son los ficheros .INI, normalmente residentes en el directorio donde está instalado Windows.
Si hacemos una breve visita a nuestro directorio de Windows podremos encontrar un buen número de estos ficheros. En concreto yo he encontrado 97 ficheros .INI en el equipo donde escribo estas líneas.
Los ficheros .INI son ficheros de texto y, en principio, son muy fáciles de modificar con un simple editor. Pero su estructura es en muchas ocasiones excesivamente simple, cuando se hacen muy grandes puede ser algo lento buscar dentro de ellos. Pero sin duda son el sistema más difundido.
3. El Registro de Configuraciones: la proliferación de ficheros .INI 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. Así, partiendo del Registro de Windows 3.1, en Windows NT y Windows 95 se nos ofrece un auténtico Registro de Configuraciones.
El Registro es una base de datos jerárquica en la que el sistema y las aplicaciones puede, y deben, guardar la información necesaria para interactuar con otros programas y para su propia configuración.
Como el sistema de Registro está muy limitado en Windows 3.1, es necesaria la compatibilidad con los ficheros .INI, y Windows 95 y Windows NT siguen soportándolos, duplicando en muchos casos los datos del registro en este tipo de ficheros.
En nuestro caso vamos a hacer una aproximación a los ficheros .INI, en otros artículos estudiaremos el Registro y las funciones para manejarlo.
Estructura de los ficheros .INI
Como hemos dicho, los ficheros de configuración de Windows son ficheros de texto con la extensión INI y almacenados, casi siempre, en el directorio donde se encuentre instalado Windows, aunque pueden estar en cualquier otro directorio.
La estructura de los ficheros INI se organiza en Secciones. Las secciones están identificadas con una etiqueta entre corchetes y agrupan información relacionada entre si.
Dentro de una sección se incluyen Claves, es decir, líneas de con un identificador, un signo igual y un valor.
Un fichero .INI muy simple podría ser:
[Ventana Principal] Tipo de Ventana=1 Abrir=0 Título=Pantalla Principal Nivel=0 [Usuario] Nombre=Pablo Almunia SanzComo podemos ver los ficheros de configuración pueden tener varias secciones, los identificadores de sección o de claves pueden contener espacios o caracteres acentuados, pero cuando accedamos a ellos deberemos dar su nombre exacto, sin importar las diferencias entre mayúsculas y minúsculas.
Los valores asociados a las claves siempre se guardan en formato de cadenas de caracteres, aun cuando sean números, valores lógicos, fechas, etc..
En Windows 3.1 existen dos ficheros .INI que configuran el sistema, son los conocidos WIN.INI y SYSTEM.INI. En estos dos ficheros se guarda casi toda la información necesaria para el sistema.
WIN.INI es normalmente el fichero de configuración más grande de todos los equipos. En principio este fichero está reservado para las configuraciones general de Windows y para las configuraciones que deban ser compartidas por más de una aplicación. Pero muchos programas deciden no tener su propio fichero .INI y utilizar este fichero para su configuración.
Nosotros deberemos ser elegantes y utilizar WIN.INI sólo para las información que deban compartir más de una aplicación y no para los datos de configuración privados.
En WIN.INI se almacena información muy interesante sobre las fuentes, extensiones de ficheros registrados, internacionalización, etc.. Usada con cuidado esta información puede ser muy útil para nuestra aplicación.
En SYSTEM.INI se definen todos los controladores, la configuración de la red, etc.. En muchas ocasiones el mejor sistema para saber si está instalado un determinado dispositivo es consultar este fichero, pero no debemos modificarlo si no sabemos realmente lo que estamos haciendo pues es un fichero difícil.
Tanto WIN.INI como SYSTEM.INI son soportados por Windows 95 y Windows NT. Aun cuando estos sistemas guardan su información en el Registro de Configuraciones, mantienen una copia de los datos del registro dentro de los ficheros .INI, pues saben que muchas aplicaciones acceden directamente a ellos.
Antes de lanzarnos a trabajar con SYSTEM.INI y WIN.INI desde VFP recomiendo que nos demos un paseo por la documentación viendo funciones como AFONT(), APRINTERS(), SYS(), FONTMETRIC(), PTRINFO(), SYSMETRIC(), SET SYSFORMAT, etc. que nos ofrecen la mejor manera de trabajar con la información del sistema.
[Nota: si desea información detallada sobre la estructura de los ficheros de configuración de Windows puede acudir a los ficheros SYSINI.WRI y WININI.WRI de vienen en la instalación de Windows, la amplia documentación del Windows Resource Kit y a los excelentes CD-ROMs del Microsoft TechNet.]
Los ficheros .INI de las aplicaciones
En la mayoría de los casos las aplicaciones Windows tienen ficheros .INI para su configuración. La estructura de estos ficheros .INI varia completamente de una aplicación a otra, dependiendo de lo que los programadores de la misma hayan decidido.
Nosotros deberemos analizar la estructura de nuestro fichero de configuración a fin de que responda a nuestras propias necesidades. No es muy fácil dar recomendaciones a este respecto, vea lo que hacen otras aplicaciones y coja las mejores ideas que observe.
En cuanto a acceder a los ficheros .INI de otras aplicaciones debemos tener algunas reservas. No es conveniente trastear desde nuestra aplicación en los .INI de otras aplicaciones si no se nos ofrece documentación muy clara al respecto. Muchos programas pueden dejar de funcionar correctamente o podemos interpretar erróneamente la información en ellos contenida. Pero en algunos casos las aplicaciones dan información sobre como interpretar e incluso modificar la información de sus ficheros de configuración y no tenemos porque temer nada.
[Nota: si desea información sobre FOXPRO.INI consulte el documento de Microsoft Q107830.]
Como acceder a ficheros .INI desde VFP
Visual FoxPro no dispone ordenes para acceder a los ficheros .INI directamente y debemos utilizar algún otro sistema.
Podemos construirnos nosotros mismos algunas funciones a base de abrir los ficheros a bajo nivel con FOPEN(), leer con FREAD() y escribir con FWRITE(). No es muy sencillo, pues debemos reconocer las distintas estructuras de los ficheros .INI y puede ser problemático en algunas ocasiones.
Las últimas versiones de la librería FOXTOOLS disponen dos funciones, GetProStr y PutProStr para leer y escribir en WIN.INI. En mi opinión son unas funciones bastante limitadas, pero podemos utilizarlas sin problemas.
Existe otro sistema, bastante sencillo y muy potente, utilizar el API de Windows. Este API nos proporciona un amplio conjunto de funciones y para leer, escribir y borrar el contenido de cualquier fichero .INI.
Veámoslo:
Leer cadenas de ficheros .INI
El API de Windows nos ofrece una función para consultar el valor de una clave dentro de una sección de un fichero .INI como una cadena de caracteres. Su declaración es:
DECLARE ; INTEGER GetPrivateProfileString ; IN WIN32API; STRING cSeccion, ; STRING cClave, ; STRING cDefecto, ; STRING @cCadenaRetorno, ; INTEGER nTama, ; STRING cNombreFicheroA esta función le debemos pasar la sección que consultamos, la clave que queremos obtener, un valor por defecto si no encuentra esta clave, sección o fichero, un buffer para la cadena de retorno, el tamaño de este buffer y el nombre del fichero INI con extensión (el path no es necesario si se encuentra en el directorio de Windows).
La función devuelve 0 si existe algún problema o, si todo a funcionado correctamente, el tamaño de la cadena incluida en el buffer. Si se almacena en el buffer el valor por defecto, que le hemos indicado para el caso de no encontrar el fichero, la sección o la clave, se retorna el tamaño de esta cadena. En ambos casos no se incluye en el tamaño el carácter nulo -CHR(0)- de fin de cadena.
Veamos un pequeño ejemplo:
*** Cargar las declaraciones *** DO profiles.prg *** Creación de la variable de retorno *** LOCATE cRetorno, nRet cRetorno = REPLICATE( CHR(0), 255 ) *** Consultar el ratón instalado *** nRet = GetPrivateProfileString( ; "boot.description", ; "mouse.drv", ; "", ; @cRetorno, ; 255, ; "system.ini" ) IF nRet > 0 ? "Ratón instalado: " ?? SUBSTR( cRetorno, ; 1, ; AT( CHR(0), cRetorno ) -1 ) ENDIFDebemos fijarnos en que la cadena que pasamos debe tener un cierto tamaño reservado, sino podemos provocar un error. Este tamaño puede ser rellenado por cualquier carácter, pero prefiero utilizar el carácter nulo.
La cadena retornada está siempre terminada por un carácter nulo -CHR(0)-, que no es necesario en Visual FoxPro, por ello es conveniente hacer siempre un SUBSTR hasta el primer nulo que encontremos.
Los más observadores se habrán percatado que los parámetros de tipo cadena que nosotros pasamos a las funciones del API de Windows no tienen el carácter nulo final, preceptivo en C. No hay ningún problema, Visual FoxPro lo incluye por nosotros.
Leer enteros de ficheros .INI
Si lo que queremos hacer es obtener un dato que sabemos que es un número entero podemos utilizar esta función:
DECLARE ; INTEGER GetPrivateProfileInt ; IN WIN32API ; STRING cSeccion, ; STRING cClave, ; INTEGER nValorDefecto, ; STRING cNombreFicheroDebemos tener cuidado en el tipo de valor que podemos obtener, pues siempre debe ser numérico y que quepa en un entero de 32 bits. Por ejemplo, un número con decimales no puede ser obtenido por este sistema y deberá ser leído como cadena y luego convertido.
En esta función, como en la de lectura de cadenas, debemos indicar un retorno por defecto si no se encuentra la clave, la sección o el fichero. Debemos elegir correctamente este valor de retorno a fin de poder controlar los posibles errores o bien siempre retorne un valor válido.
Leer una sección completa
Si preferimos leer una sección entera y no clave a clave podemos utilizar esta función:
DECLARE ; INTEGER GetPrivateProfileSection ; IN WIN32API ; STRING cSeccion, ; STRING @cCadenaRetorno, ; INTEGER nTama, ; STRING cNombreFicheroLa cadena de retorno contendrá todas las claves de la sección, incluido el identificador, el signo igual y el valor, separadas las líneas por caracteres nulos y finalizando la cadena con dos caracteres nulos.
Esta función es una clara candidata a estar encapsulada en una función de mayor nivel que, por ejemplo, ponga todas las líneas de una sección dentro de un matriz de dos dimensiones, donde la primera columna son las claves y la segunda los valores. Esta función puede ser:
PROCEDURE GtIniSec LPARAMETER cSeccion, cArray, cNombreFich *** Tamaño del buffer (puede cambiarse) *** #define MAX_SECTION 5120 *** Todas la variables locales *** LOCAL cBuffer, cTmp LOCAL nResult, nCont LOCAL nInicioLinea, nFinLinea, nPosIgual *** Cargar las declaraciones *** DO profiles *** Preparar bufferr *** cBuffer = REPLICATE( CHR(0), MAX_SECTION ) *** Obtener todo la sección *** nResult = GetPrivateProfileSection( ; cSeccion, ; @cBuffer, ; MAX_SECTION, ; cNombreFich ) *** Comprobar si existen errores *** IF nResult == 0 RETURN 0 ENDIF *** Inicializar variables *** nInicioLinea = 1 nFinLinea = 1 nCont = 0 cTmp = "" *** Procesar el resultado *** DO WHILE nFinLinea < nResult *** Aumentar el contador de nulos *** nCont = nCont + 1 *** Posición del próximo nulo *** nFinLinea = AT( CHR(0), cBuffer, nCont ) *** Cadena de una línea *** cTmp = SUBSTR( cBuffer, ; nInicioLinea, ; nFinLinea - nInicioLinea ) *** Redimensionar el array *** PUBLIC ARRAY &cArray.[nCont,2] *** Posición del igual *** nPosIgual = AT( '=', cTmp ) *** Cargar la clave *** &cArray.[nCont,1] = ; SUBSTR( cTmp, 1, nPosIgual - 1 ) *** Cargar el valor si existe *** IF nPosIgual < LEN( cTmp ) &cArray.[nCont,2] = ; SUBSTR( cTmp, nPosIgual + 1 ) ELSE &cArray.[nCont,2] = "" ENDIF *** El incio de la siguiente línea *** *** es el fin de esta *** nInicioLinea = nFinLinea + 1 ENDDO *** Retornar el número de líneas *** RETURN nContPara utilizar esta función, que sin duda es mejorable, debemos pasar el nombre de la sección, el nombre de la matriz y el nombre del fichero, algo como esto:
? ASECTION( "windows", "aVer", "win.ini" ) DISPLAY MEMORY LIKE aVerRecomiendo sin reservas el encapsular las llamadas del API de Windows dentro de funciones escritas por nosotros mismos, de esta forma, no sólo damos una mayor funcionalidad a nuestros programas, sino que aislamos nuestro código antes posibles cambios en el API.
En este artículo sólo nos ha dado espacio para ver las funciones de lectura de ficheros .INI. En la próxima entrega veremos las funciones de escritura y un ejemplo completo del uso de estas funciones.
- Introducción a Foxtools (Foxtools.fll)
- Introducción (I)
- Funciones para ficheros .INI (II) --> Usted está aquí.
- Funciones para ficheros .INI (III)
- Introducción a las Ventanas (IV)
- Introducción a las Ventanas (V)
- Manejo del Registro (y VI)