Ficheros INI al estilo clásico con diccionarios
De todos es conocido que el registro es una castaña para guardar la configuración de un programa, y más aún si el programa va a permitr que el propio usuario la toque a mano sin que el programa esté cargado, como es el caso de Opera y de otro mucho software, generalmente OpenSource.
El .NET Framework dispone de dos sistemas para guardar la configuración de una aplicación. El sistema más sencillo es colocar un archivo con el mismo nombre del ensamblado y con la extensión .config. La limitación de este sistema es que es de sólo lectura y compartido entre todos los usuarios. Otra castaña, vamos.
El segundo es bastante más poderoso. Consiste en utilizar flujos y guardar en ellos los componentes que queramos. También tiene sus limitaciones, algunas de ellas desesperantes. No se pueden guardar fichas (o al menos yo no he podido), toda clase/componente ha de tener la propiedad [Serializable] (que automáticamente hereda de Iformatter añadiendo bastante tamaño a los objetos instanciados), y sólo se pueden guardar en serie como si de una pila corriente y moliente se tratara. Si queremos sacar el tercer componente guardado, hemos de sacar los dos anteriores, y si una vez extraído ese queremos obtener el anterior, debemos cerrar y volver a abrir el flujo. Otra pasada castañera, vamos.
Pero las limitaciones no terminan ahí, no. Los ficheros en donde se almacenan los datos pueden ser binarios o XML. Los binarios no tienen ninguna limitación si no es la de que nadie sabe qué hay dentro (que para el propósito que nos ocupa no nos sirven), y los XML son incapaces de almacenar la mitad de cosas… Vamos, otra castaña más.
¿Cuál es la solución? Pues volver al tradicional fichero INI de toda la vida, el mayor invento en archivos de configuración. Podríamos treabajar con fichero XML, pero son bastante complejos de leer a mano por un ser humano y encima los sistemas para acceder a ellos son enormemente lentos.
Por eso acabo de terminar de codificar una clase para trabajar con ficheros INI de texto para mi programa. La he realizado utilizando un diccionario que contiene otro diccionario, de la forma que a la hora de acceder a un elemento de una sección es enormemente sencilla:
String ^elem=iniFile[seccion][nombre];
De esta forma, si accedemos a un elemento inexistente, el objeto devolverá una cadena vacía, y si asignamos algo nuevo, se crearán las secciones correspondientes automáticamente.
También he creado los métodos tradicionales del API de Win16, ReadString, etc. Bueno, aunque de momento sólo tengo implementados la obtención de una cadena y de un entero, fijándose en el código se puede implementar trivialmente casi cualquier otro tipo de dato básico.
Lo curioso del tema es que una vez que la he terminado me he dado cuenta de que mediante diccionarios es imposible mantener los comentarios, pues una vez que se escribe de nuevo el fichero, éstos se pierden, pero aun así todavía es útil. Quizás más adelante implemente una nueva clase con otro tipo de estructura de datos que permita mantener los comentarios.
Los ficheros de código fuente se pueden bajar de aquí.

¡Qué nostalgia!
Comment by Ignacio — May 18, 2006 @ 3:16 pm
Intentaré traducir eso, parece interesante, pero creo que tu primera afirmación no es del todo correcta. Si no me equivoco en las página de ‘el Guille’ hay un ejemplo de como usar ese fichero para guardar datos, pero tengo que revisarlo, lo mismo meto la pata
Comment by Peni — May 18, 2006 @ 7:05 pm
Peni: Por lo menos en el NET 1.1 no se podía… Creo que con la “Enterprise Library” hay clases para hacerlo, pero seguimos con el tema de que es para la aplicación de forma global y en XML…
Prefiero los INI de toda la vida. Además, voy a hacer una nueva clase con soporte para comentarios y que grabe los ficheros de forma “Bonita”.
Para taducirlo es fácil: cambia “default” por “this”, String^ por string, “gcnew” por “new” y los arrays en forma de plantilla por el formato del C#. Si tienes problemas me das un toque por privado.
Comment by rfog — May 18, 2006 @ 7:26 pm
Yo me estaba refiriendo al .NET 2005
´Tu código más o menos lo pillo, un compañero trabaja en C# y algo se me pega y lo mismo me anuimo a hacerlo desde 0 en VB .NET, ese ‘casi lenguaje’
en lo de los ini estamos de acuerdo y además es más amigable para tocarlo a mano. En VB hay una propiedad que se llama DataBindings y ApplicationSettings, lo has hechado un vistazo (aunque no es para lo mismo que el ini)?
Comment by Peni — May 18, 2006 @ 7:40 pm
Y solo para tocar las narices en VB.NET el equivalente a default es ‘Me’, ‘this’ es en C#
Comment by Peni — May 18, 2006 @ 7:44 pm
Peni, espérate unos días y hago la nueva clase con formateo “gonito” de los INI. Lo mismo la traduzco yo a C# también.
Respecto a lo de la configuración, creo que también es para el 2.0, o por lo menos así lo afirma lo que leí en su momento, y de momento no pienso usar la EE, para bugs ya tengo los del NET, del Visual Studio y los míos propios.
Comment by rfog — May 18, 2006 @ 8:12 pm
Por si les interesa, uno de los tantos proyectos disponibles:
http://nini.sourceforge.net/
Saludos
Comment by Alex — May 19, 2006 @ 1:09 am
Cuando conviertas la clase a C#, podrías compilar el assembly, pasarlo por el tamiz de Reflector (http://www.aisto.com/roeder/DotNet/) y regenerar el código de la clase en VB.NET mediante el add-in File Dissasembler (http://www.denisbauer.com/NETTools/FileDisassembler.aspx).
Por cierto, Rafa, al traducir tu código actual C++/CLI a C# más o menos literalmente, el compilador me ha chivado dos cosas:
IniStream.cs(10,13): warning CS0649: El campo ‘RFOG.IniStream.m_version’ nunca se asigna y siempre tendrá el valor predeterminado 0
IniStream.cs(13,14): warning CS0414: El campo privado ‘RFOG.IniStream.m_modified’ está asignado pero su valor nunca se utiliza
Avisos que el compilador de C++ no muestra ni con un /W4.
Comment by Ramón Sola — May 19, 2006 @ 1:55 am
Ramón, ya sabemos que el C++/CLI necesita unos cuantos hervores más. Esa variable iba a ser un control de versiones rudimentario que se me olvidó de implementar (al principio del fichero INI iba a ir una cadena sin sección que debería ser la versión).
Respecto a la de m_modified sí que se toca; a la hora de cerrar el ini se mira a ver si vale cierto y en el WriteString que hace el trabajo sucio se cambia a cierto, por lo que ahí está fallando el compilador de C#.
Respecto a lo del disassembler me dejas a cuadros. Bueno es tenerte cerca.
Alex: gracias por tu información, conocía esa y como bien afirmas hay muchas más, pero como buen (o mal) programador del mundo C/C++ original estoy acostumbrado al autoservicio, je je.
En fin, que todo esto son más ejercicios que otra cosa.
Comment by rfog — May 19, 2006 @ 8:23 am
«Respecto a la de m_modified sí que se toca; a la hora de cerrar el ini se mira a ver si vale cierto.»
Yo no estaría tan seguro de eso, al menos viendo los ficheros del ZIP ligado a esta entrada:
find /N “m_modified” inistream.*
———- INISTREAM.CPP
[7] m_modified=false;
[75] m_modified=false;
[111] m_modified=true; //Como todos terminan llamando a este método sólo hay que colocarlo aquí
———- INISTREAM.H
[15] bool m_modified;
Pero vamos, por lo que comentas se trata de poner un ‘if (m_modified) { /* resto del método */ }’ que abarque todo el método Close.
Comment by Ramón Sola — May 19, 2006 @ 4:19 pm
Ramón, no sólo Microsoft hace bugs…
Comment by rfog — May 19, 2006 @ 6:02 pm
«Los programadores mienten», diría un House metido a tester.
Comment by Ramón Sola — May 19, 2006 @ 6:11 pm
Muy buenos esos enlaces Ramón
Vaya que se me amontona el curro con el .NET!!
Comment by Peni — May 19, 2006 @ 7:02 pm
Ficheros INI de texto en C++/CLI
Recupero un artículo (recomiendo su lectura) ya viejo de otro de mis blogs en el que hablo de una clase
Trackback by .NET o no .NET, esa es la cuestión — November 16, 2006 @ 9:18 am