Win32: API: El registroWindows ideó una novedosa forma para organizar información sobre su propio sistema y programas, y posteriormente la hizo accesible para las aplicaciones. Hablamos del registro de Windows, o simplemente "registry". Esto ha hecho muy útil para las aplicaciones el poder manipular su información indistintamente del directorio donde son instaladas, además de poder proteger (o al menos "esconder") su información.
En fin, vamos al grano pués. Pensaba dar más información al respecto, pero creo que todos conocemos al registry, ¿no? Entonces no se diga más.
Bueno, solo una cosa. El registry está organizado de forma jerárquica. La máxima jerarquía se define por un conjunto de "llaves", las cuáles varían de acuerdo al sistema operativo, pero actualmente (y en Windows XP) podemos hablar de cinco.
HKEY_CLASSES_ROOT. Aquí se guarda información relacionada con los componentes COM y derivados, así como información sobre los tipos de archivos. Usualmente nunca nos meteremos a esa carpeta.
HKEY_CURRENT_USER. En esta carpeta se guarda información del usuario que actualmente se ha loggeado al sistema. Muy útil cuando queremos guardar "perfiles" dependiendo del usuario.
HKEY_LOCAL_MACHINE. En este nodo se guarda información relacionada con la máquina, es decir, toda la información de los programas que no dependan de un usuario en particular debe ir aquí.
HKEY_USERS. Aquí se guarda información sobre los diferentes usuarios del sistema. Lo usa Windows de forma interna y no deberíamos de meterle mano.
HKEY_CURRENT_CONFIG. Tiene la información sobre la actual configuración de Windows (fuentes, colores, vistas, etc). Nuestras narices deberían de mantenerse alejadas de aquí, en la medida de lo posible.
Así pués, el registro es una estructura de árbol. Debajo de los cinco nodos anteriormente mencionados se guardan "llaves", que pueden tener un valor asociado o bien más llaves hijas. Entonces lo que tenemos que hacer para manipular datos del registro, es abrir la llave, leer o escribir, y cerrar la llave. Obvio Windows nos provée funciones para ello. Antes de mencionarlas, empero, quiero recordarles que el acceso a estas llaves está regido por los permisos que un usuario tenga. Es decir, si el usuario bajo el cuál se ejecuta la aplicación intenta escribir una llave en el registry y que no tenga permisos, la llamada a la función mandará un error.
Ahora sí veamos cómo funciona el asunto. Vamos a poner un ejemplo. Supongamos que tenemos una clase de configuración, del sistema Super Sales, y queremos que al mandarse llamar el método Load se carguen los diferentes valores. No se rían que el ejemplo es real y lo saqué de un código que ando escribiendo en estas fechas. Mi método
Load tiene la siguiente forma:
void Configuration::Load()
{
try
{
_path = GetValue(_T("path"));
_dsn = GetValue(_T("dsn"));
_dbuser = GetValue(_T("dbuser"));
_dbpassword = GetValue(_T("dbpassword"));
_language = GetValue(_T("language"));
}
catch (const Exception& ex)
{
_corrupted = true;
throw ex;
}
}
Obvio lo que nos interesa está en
GetValue, la cuál se va a conectar al registroy bajar el valor que tenga la llave correspondiente, que se pasa como parámetro. Dado que estos datos se guardan siempre en
HKEY_LOCAL_MACHINE bajo
Software/Dark Software/Super Sales, solo necesito como parámetro el nombre de la llave misma. La función
GetValue luce así:
CString Configuration::GetValue(const CString& name)
{
TCHAR buffer[MAX_PATH];
DWORD size;
HRESULT ret;
HKEY key;
ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Dark Software\\Super Sales\\"), NULL, KEY_READ, &key);
if (ret != ERROR_SUCCESS)
throw Exception(IDS_ERR_SYS_CONFIGFILE, __FILE__, __LINE__);
size = sizeof(TCHAR) * MAX_PATH;
ret = ::RegQueryValueEx(key, name, NULL, NULL, (LPBYTE)buffer, &size);
if (ret != ERROR_SUCCESS)
throw Exception(IDS_ERR_SYS_CONFIGFILE, __FILE__, __LINE__);
::RegCloseKey(key);
return buffer;
}
Más claro ni el agua, ¿verdad? OK. De todas formas lo explico. La función
RegOpenKeyEx se encarga --sorpresa sorpresa-- de abrir una llave del registro. El primer parámetro es una macro que identifica el nodo del registry sobre el cuál tendremos que buscar. En mi caso, empleo
HKEY_LOCAL_MACHINE porque son valores que deben estar disponibles para la aplicación en cualquier caso. Si solo fuera información de un usuario (como preferencias) hubiera elegido
HKEY_CURRENT_USER. El segundo parámetro es el path que me llevará hasta la llave que contiene los valores que ando buscando. Como les dije, éste es
Software\Dark Software\Super Sales. El tercer parámetro siempre tiene que ser nulo. Quizás lo ocupen en algún futuro, pero por ahorita siempre deberá ser
NULL. Con el cuarto parámetro especifico la operación que voy a realizar así como los parámetros de seguridad. En este caso vamos a leer, así que le pasamos
KEY_READ, mientras que
KEY_WRITE se usa para escribir. Hay otros parámetros, pero solo estos dos son los más importantes. Los otros o son solo de sistema o forman parte de alguno de los dos mencionados (
KEY_WRITE se define como
STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY). Por supuesto, podemos emplear
KEY_ALL_ACCESS si queremos especificar todas las opciones (lectura y escritura). La función regresa
ERROR_SUCCESS si todo salió bien, o un código de error si salió mal. Las causas por las que puede fallar son esencialmente dos: o no se tienen permisos suficientes, o las llaves proporcionadas no existen. ¡Ah! Se me olvidaba que hay un quinto parámetro. Este es un handle a la llave que vamos a abrir y obvio es de lo más importante. Habrán vissto que el tipo de dato es
HKEY. Tenlo en cuenta para un momentín.
Luego tenemos que obtener el valor. Es decir, ya tenemos una llave abierta, ahora debemos buscar las sub-llaves y sacar el valor. Obvio, emplearemos
RegQueryValueEx. El primer parámetro es la llave que abrimos con
RegOpenKeyEx, que les dije que tuvieran en cuenta. El segundo parámetro es el nombre de la sub-llave que queremos obtener (y que en este ejemplo, se pasa como parámetro). El tercer parámetro es reservado y siempre deberá ser nulo. El cuarto parámetro es un puntero a un
DWORD y nos va a indicar el tipo de dato (
REG_SZ si es una cadena de texto,
REG_BINARY si es un conjunto binario,
REG_DWORD si es un número, etc).Finalmente, el quinto parámetro es un puntero a el búfer en el que vamos a obtener nuestro valor (el tipo de dato variará de acuerdo al tipo de la sub-llave; en nuestro caso es un puntero a caracter dado que se trata de un
REG_SZ), y el sexto parámetro es de entrada y de salida, y especifica el tamaño del búfer, y de salida especifica el tamaño que se escribió. La función nos regresa
ERROR_SUCCESS si todo salió chido,
ERROR_FILE_NOT_FOUND si no se encontró la sub-llave, o
ERROR_MORE_DATA si el tamaño del búfer no fue lo suficientemente grande.
Por último, empleamos
RegCloseKey para cerrar la llave. Y listo. Voilà. Sehr gut. Facilito, ¿no?
El siguiente pedazo de código es la función
Save que se encarga de guardar los cambios en el registro. Es muy similar a su contraparte:
void Configuration::Save()
{
try
{
SetValue(_T("path"), _path);
SetValue(_T("dsn"), _dsn);
SetValue(_T("dbuser"), _dbuser);
SetValue(_T("dbpassword"), _dbpassword);
SetValue(_T("language"), _language);
}
catch (const Exception& ex)
{
_corrupted = true;
throw ex;
}
}
Al igual que
Load, la llamada al registro queda en manos de una función auxiliar, cuyo código es el siguiente:
void Configuration::SetValue(const CString& name, const CString& value)
{
TCHAR buffer[MAX_PATH];
DWORD size;
HRESULT ret;
CString str;
HKEY key;
ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Dark Software\\Super Sales\\"), NULL, KEY_SET_VALUE, &key);
if (ret != ERROR_SUCCESS)
throw Exception(IDS_ERR_SYS_CONFIGFILE, __FILE__, __LINE__);
_tcscpy(buffer, name);
size = sizeof(TCHAR) * MAX_PATH;
str.Format(_T("Software\\Dark Software\\Super Sales\\%s"), name);
ret = ::RegSetValueEx(HKEY_LOCAL_MACHINE, str, NULL, REG_SZ, (BYTE*)buffer, size);
if (ret != ERROR_SUCCESS)
throw Exception(IDS_ERR_SYS_CONFIGFILE, __FILE__, __LINE__);
::RegCloseKey(key);
}
La primera llamada ya la conocen. Abrimos la llave con
RegOpenKeyEx. La única diferencia, de hecho, es que ahora llamamos a
RegQueryValue. A diferencia de cuando buscábamos leer el valor, aquí no es necesario abrir llave alguna. De hecho, la llamada se hace directamente desde
RegSetValueEx. Pero empleamos
RegOpenKeyEx y
RegCloseKey para cerciorarnos de que la llave existe. El primer parámetro es el nodo (
HKEY_LOCAL_MACHINE en este caso). La segunda es el llamado a la dirección completa (llave + sub-llave) donde queremos guardar el valor. El tercer parámetro será siempre nulo, el cuarto indica el tipo de dato a guardar, el quinto el valor que queremos guardar y, finalmente, el sexto indica el tamaño de nuestro búfer. Regresa
ERROR_SUCCESS si todo salió bien.
Bueno, como has visto, es una forma fácil de guardar datos y sin tener tanto embrollo de guardarlos en un archivo de configuración. Pero ¿qué pasa si queremos crear llaves y sub-llaves? ¿O modificarlas? Bueno, eso lo discutiremos en la siguiente entrega de este blog.
Publicado por: Fernando Arturo Gómez Flores
ORIGEN:
http://kithkanan-programacionencpp.blogspot.com/