Explorando la API de Windows en C++: La función RegOpenKeyEx para abrir claves de Registro

TechDays-Figure-with-Laptop_7A06897A

Volviendo a lo que espero sea una larga serie de artículos sobre la API de Windows, hemos visto al detalle ya cómo utilizar las funciones de RegCreateKeyEx para crear una sub-clave de registro y RegSetValueEx para establecer un tipo de valor e indicarle su contenido. Por supuesto, en todas he tocado RegCloseKey para cerrar la operación en la clave o sub-clave de Registro.

Si fuéramos a resumir, ya sabemos crear una sub-clave de registro, utilizar un apuntador para establecer un valor y cerrarla pero falta algo muy importante en el orden natural que debería tener, y es básicamente abrir la clave para funcionar con ella. Aunque Windows ya lo hace de forma nativa sobre las principales claves (HKCU, HKLM, e.t.c), abrir una clave de producto nos facilitará el acceso para escribir directamente un valor, en vez de tener que crear la sub-clave nosotros mismos. A continuación entonces, pasaré a describir de la misma forma cómo debemos declarar las variables de RegOpenKeyEx y para que haya algo de valor, unir esta función con RegCreateKeyEx y RegSetValueEx.

La función RegOpenKeyEx

Para poder utilizar correctamente esta función, es necesario realizar las siguientes declaraciones:

HKEY hKey: Como siempre, aquí se debe declarar un apuntador a una clave que esté abierta. Puede ser retornada por RegCreateKeyEx, o bien utilizar algunas de las que están predefinidas:

HKEY_CLASSES_ROOT
HKEY_CURRENT_CONFIG
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS

LPCTSTR lpSubKey: Aquí en este caso va el nombre de la sub-clave que se va a abrir. En caso de que deseemos utilizar algunas de las claves predefinidas como HKCU, es necesario declararlo como NULL para que no tome la sub-clave.

DWORD ulOptions: Tal cual funciona la variable de Reserved en las anteriores funciones, esta es la equivalente para RegOpenKeyEx, por lo tanto debe ser cero (0).

REGSAM samDesired: Como en todas las otras declaraciones, aquí debemos especificar el acceso que deseamos tener sobre la clave que se va a abrir. Hay que tener presente que como lo indica la documentación, el acceso puede fallar si los descriptores de seguridad que tiene la clave no lo permite.

PHKEY phkResult: Es el apuntador a la variable que va a recibir el apuntador de la clave que se abrió.

Como siempre, en caso de que la operación sea exitosa, se retornará un ERROR_SUCCESS. Es necesario utilizar una variable de tipo long para recibir y funcionar con el resultado.

Ahora, digamos que deseo abrir una sub-clave llamada DemoKey, ubicada en la clave HKCU, es decir: HKEY_CURRENT_USERDemoKey.

La declaración primero que todo sería entonces:

//Declarando variables para RegOpenKeyEx
        HKEY hKey = HKEY_CURRENT_USER;
        LPCTSTR lpSubKey = L"DemoKey";
        DWORD ulOptions = 0;
        REGSAM samDesired = KEY_READ | KEY_WRITE;
        HKEY phkResult;

Cuando se llame a la función, bastará con especificar todas variables que necesita:

//Llamando a la función
        long Result = RegOpenKeyEx(hKey, lpSubKey, ulOptions,
                                   samDesired, &phkResult);

Por último, para mostrar algún resultado sobre la operación, comprobaríamos lo que devuelva la variable de Result, así:

if (Result == ERROR_SUCCESS)
    MessageBox(NULL, L"La clave se abrió correctamente.",
                  L"Abriendo clave", MB_ICONINFORMATION);
else
    MessageBox(NULL, L"Error al abrir la clave",
               L"Abriendo clave", MB_ICONERROR);

Si recordamos bien, la función de RegCreateKeyEx también abre o crea la sub-clave dependiendo de su existencia. Esta actúa igual, pero sólo abre o no abre. Por supuesto, en el mini ejemplo anterior solo estamos contemplando la posibilidad de que haya abierto, de lo contrario, sin importarnos la razón, mostraremos el mensaje. En un ambiente más controlado, tendríamos que saber que manejar varios errores, como el que la Clave o Sub-Clave no se encuentre, tal como en este caso lo indica Process Monitor:

image

De la teoría a la práctica: RegOpenKeyEx, RegCreateKeyEx y RegSetValueEx

Muy bien, para que esta serie de artículos tengan un poco más de sentido, y podamos aprender de Windows Internals, plantearé un escenario más elaborado donde se pueda combinar las funciones de RegOpenKeyEx, RegCreateKeyEx y RegSetValueEx ya vistas.

Tomaremos como base la clave de HKEY_CURRENT_USER, y lo que haremos será básicamente abrir la sub-clave DemoKey, si se puede abrir, crearemos el valor DemoValue pero si la sub-clave no se puede abrir, crearemos la clave con RegCreateKeyEx y posteriormente estableceremos el mismo valor de DemoValue.

Empecemos con lo primordial, y es el mismo método principal, con su respectiva cabecera:

#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow){
}

En conjunto con la declaración anterior para RegOpenKeyEx, es necesario agregar las correspondientes a RegCreateKeyEx y RegSetValueEx:

//Declarando variables para RegCreateKeyEx
        DWORD Reserved = 0;
        LPTSTR lpClass = NULL;
        DWORD dwOptions = REG_OPTION_NON_VOLATILE;
        LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL;
        DWORD lpdwDisposition;
//Declarando variables para RegSetValueEx
        LPCTSTR lpValueName = L"DemoValue";
        DWORD dwType = REG_DWORD;
        DWORD lpData = 1;
        DWORD cbData = sizeof(lpData);

*Nota: Cabe recordar que estas declaraciones están explicadas al detalle en los dos artículos anteriores.

Tal cual lo hicimos antes, lo primero es buscar que la clave esté o no abierta utilizando la función RegOpenKeyEx:

//Llamando a la función
        long Result = RegOpenKeyEx(hKey, lpSubKey, ulOptions,
                                   samDesired, &phkResult);

Después haremos uso de un switch para controlar diferentes retornos en el resultado de la operación como Encontrado, No encontrado o Acceso denegado.

Si la operación devuelve un ERROR_SUCCESS, entonces procederemos a crear el valor, así que ese estará como primer “case”; si la operación devuelve un ERROR_FILE_NOT_FOUND, la clave no estará, así que procederemos a crearla con RegCreateKeyEx; si la operación devuelve un ERROR_ACCESS_DENIED, no se tendrán los permisos suficientes para efectuarla, así que solo lo indicaremos desde un mensaje y por último, si no es ninguna de todas las anteriores (“default”), devolveremos un error genérico como los de Windows.

*Nota: Es necesario claro está, validar también los resultados internos de cada “case”.

En este orden de ideas, el código quedaría:

switch (Result)
        {
        case ERROR_SUCCESS:

            //La clave existe.
            R2 = RegSetValueEx(phkResult, lpValueName, Reserved,
                dwType, (const BYTE *) &lpData, cbData);

            //Validar que se haya creado
            if (R2 == ERROR_SUCCESS)
            {
                MessageBox(NULL,
L
"La clave existía, se creó el valor.", L"Explorando la API", MB_ICONINFORMATION); } else if (R2 == ERROR_ACCESS_DENIED) { MessageBox(NULL,
L
"La clave existía, pero hay acceso denegado", L"Explorando la API", MB_ICONERROR); } else { MessageBox(NULL, L"Error creando el valor", L"Explorando la API", MB_ICONERROR); } break; //Fin primer case case ERROR_FILE_NOT_FOUND: //La clave no existe R2 = RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired,
lpSecurityAttributes, &phkResult, &lpdwDisposition);
if (lpdwDisposition == REG_CREATED_NEW_KEY) { R2 = RegSetValueEx(phkResult, lpValueName,
Reserved,dwType, (
const BYTE *) &lpData,
cbData);
switch (R2) { case ERROR_SUCCESS: MessageBox(NULL,
L
"La clave no existía, se creó con valor.", L"Explorando la API", MB_ICONINFORMATION); break; case ERROR_ACCESS_DENIED: MessageBox(NULL,
L
"Se creó clave, acceso denegado al valor", L"Explorando la API", MB_ICONINFORMATION); break; default: MessageBox(NULL,
L
"Se creó clave, error al crear valor", L"Explorando la API", MB_ICONINFORMATION); break; } } //Aquí no debe llegar si ya estaba, pero se valida. else if (lpdwDisposition == REG_OPENED_EXISTING_KEY) { R2 = RegSetValueEx(phkResult, lpValueName,
Reserved,dwType, (
const BYTE *) &lpData,
cbData);
switch (R2) { case ERROR_SUCCESS: MessageBox(NULL,
L
"Clave modificada, valor creado", L"Explorando la API", MB_ICONINFORMATION); break; case ERROR_ACCESS_DENIED: MessageBox(NULL,
L
"Clave modificada, acceso denegado al valor", L"Explorando la API", MB_ICONINFORMATION); break; default: MessageBox(NULL,
L
"Clave modifcada, error al crear valor.", L"Explorando la API", MB_ICONINFORMATION); break; } } else //Fin de REG_OPENED_EXISTING_KEY { MessageBox(NULL, L"Error creando la clave.", L"Explorando la API", MB_ICONERROR); } break; //Fin del segundo case case ERROR_ACCESS_DENIED: //Acceso denegado al abrir la clave MessageBox(NULL,
L
"No se abrió la clave, Acceso Denegado", L"Explorando la API", MB_ICONERROR); break; default: //Otro tipo de error no contemplado MessageBox(NULL, L"Error al abrir la clave.", L"Explorando la API", MB_ICONEXCLAMATION); break; //Fin de default } //Fin del switch

Independiente del swith, es necesario cerrar la operación en la sub-clave con RegCloseKey:

//Cierra operación en Registro
        RegCloseKey(phkResult);

Explorando resultados

Saliendo del lado desarrollador, Process Monitor nos puede ayudar a explorar el comportamiento en dos de los casos más comunes dentro del swith, que se deba crear todo, o solo modificar, es decir, entrar al primer o segundo case.

– Si el resultado es ERROR_FILE_NOT_FOUND, Process Monitor lo reportaría así:

ROP1

Tal cual se lo indicamos, la primera operación que hace se ve como RegOpenKey, sobre la sub-clave DemoKey, pero como tiene un resultado de NAME NOT FOUND, es decir, que no la encontró, pasa a llamar a RegCreateKey para escribirla, y al obtener resultado SUCCESS, finaliza creando el valor DemoValue con la operación RegSetValue.

En algún punto además, cerrará la operación también correctamente sobre la sub-clave:

image

El mensaje para el usuario, de acuerdo a lo programado, se mostraría así:

image

– Si el resultado por el contrario es ERROR_SUCCESS, Process Monitor lo reportaría así:

ROP2

Primero, realizando la operación de RegOpenKey, intentaría abrir la sub-clave de DemoKey, al recibir SUCCESS, establecería la información correspondiente y llamaría ahora a la operación de RegSetValue para escribir el valor de DemoValue también con SUCCESS como resultado.

El mensaje se mostraría así:

image

Bastante interesante el Registro, ¿no les parece?

Para los que deseen, he subido la solución completa a una carpeta de SkyDrive, y la pueden descargar desde aquí:

Espero sea de utilidad. ¡Comentarios bienvenidos!

PD. No olviden seguirme en Twitter: www.twitter.com/secalderonr

Saludos,

Checho

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *