Compartir a través de


Explorando la API de Windows en C++: Creando mi primera clave de Registro con la función RegCreateKeyEx (es-ES)

Extraido del post original en mi blog.

La primera función con la que quiero empezar, se llama RegCreateKeyEx, y es básicamente la que nos permite, a través de la API de Win32, crear una clave específica en el Registro de Windows. El problema que tuve yo, fue que enfrentarme a crear por primera vez una aplicación de Win32 y manejar este tipo de funciones en C++, definitivamente me iba a resultar complejo, y la documentación de MSDN es muy precisa y de pocos ejemplos para poder empezar. Tuve que indagar en la web, y después de ires y venires con diferentes variaciones, pude comprender en términos generales la forma como se expresa la función en MSDN, y finalmente crear mi primera clave. El segundo problema, es que para poder aprender de una mejor forma, necesitaba además de practicar, poder tener documentarlo en alguna parte, y que se convierta en mi propia referencia a futuro, y es aquí donde aparece mi blog, ¿qué mejor lugar para hacerlo, si no es en mi propio espacio?

Lo que haré en este artículo, será mostrar el paso a paso para crear nuestra primera aplicación de Win32 como referencia, pero además, cómo utilizar la función RegCreateKeyEx de una forma correcta para generar una clave en el Registo de Windows, incluyendo diferentes algunas formas de interactuar con ella utilizando Process Monitor de Sysinternals.

Creando la primera App de Win32

Antes que utilizar cualquier función, es necesario por supuesto crear un nuevo proyecto; y para asegurarme que los que sigan este artículo les funcione, quise incluir esta primera parte de la creación básica de una Aplicación de Win32. Necesitaremos básicamente:

- Visual Studio, en cualquier versión, pues este tipo de proyectos se crea desde cero. Preferiblemente, aconsejaría estar bajo 2012 que es la última oficial. Pueden descargar la versión Express que es totalmente gratuita desde aquí:
http://www.microsoft.com/visualstudio/esn/downloads#d-2012-express

- Equipo técnico con Windows XP / Vista / 7 / 8 para realizar la prueba. Este post se enfocará sobre Windows 8.

Para crear y configurar nuestro proyecto:

1. Desde Visual Studio, hacemos clic en Archivo > Nuevo > Proyecto.

2. Seleccionamos Visual C++ dentro de las Plantillas, o bien dentro del nodo de Otros tipos de proyectos (Según como hayamos configurado Visual Studio), y escogemos Proyecto de Win32. Lo demás será indicar el nombre y el destino del proyecto:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_40A824D8.png

3. En el Asistente para Aplicación de Win32 que aparece, hacemos clic en el botón de Siguiente (Next) y después seleccionamos ‘Aplicación de Windows’ debajo de Tipo de Aplicación y Proyecto Vacio, debajo de Opciones adicionales para darle clic en Finish.

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_09642E4E.png

4. Hasta aquí, lo único que veremos será nuestro Explorador de Soluciones sin nada agregado en el panel derecho. Para poder agregar el código fuente, hacemos clic en el menú de Proyecto > Agregar nuevo elemento, seleccionamos Archivo de C++, le ponemos un nombre (Puede ser el predeterminado) y clic en Agregar:

 

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_48583F9A.png

5. Una vez estemos en la plantilla en blanco de nuestro código fuente, podremos empezar a escribir nuestra aplicación de Win32. Lo primero, y más importante, es referenciar nuestro archivo de cabecera, y nuestro punto de entrada, que conocemos como función principal.
Para una aplicación de Win32, nuestro punto de entrada es <Windows.h>, que incluye a su vez otras cabeceras de Windows y la función principal, o punto de entrada se llama WinMain.

Llevando esto al código, para cada aplicación de Win32, tendríamos lo siguiente:

#include <Windows.h>
 
//Se declara método principal 
 
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, 
                   int nCmdShow){
 
} //Cierra método principal.

 

*Nota: En toda aplicación de Win32, siempre existirá la cabecera de Windows.h y la función de WinMain.

Acerca de RegCreateKeyEx

La sintaxis de la función RegCreateKeyEx, requiere nueve entradas que se pueden especificar manualmente mientras se escribe, o bien, declararlas desde antes y enviarle las variables como muestra la documentación de MSDN. Las variables (Que no necesariamente requieren tener los mismos nombres) son:

HKEY hKey: Este es el apuntador a una clave de Registro abierta, y tiene unas predeterminadas, que en mi concepto, son las que deberíamos utilizar aquí siempre como punto de partida, que son:

HKEY_CLASSES_ROOT.
HKEY_CURRENT_CONFIG.
HKEY_CURRENT_USER.
HKEY_LOCAL_MACHINE.
HKEY_USERS.

*Nota: Como ven, los que maneja predeterminadamente son la mayoría de los Hives que tiene Windows.

LPCTSTR lpSubKey: Este es el nombre denuestra subclave que vamos a abrir o crear. Debe estar contemplada como una subclave de la clave que creamos con hKey. Por ejemplo, si especificamos que escribiríamos en HKEY_LOCAL_MACHINE\Software una subclave llamada MiPrograma, lpSubKey debería contener: “\Software\MiPrograma”.

DWORD Reserved: Este parámetro siempre debe ser 0.

LPTSTR lpClass: Es el tipo de clase definidio por el usuario, pero suele ser NULL.

DWORD dwOptions: Este parámetro tiene diferentes opciones:

REG_OPTION_BACKUP_RESTORE.
REG_OPTION_CREATE_LINK.
REG_OPTION_NON_VOLATILE
REG_OPTION_VOLATILE.

El parámetro predeterminado es REG_OPTION_NON_VOLATILE, que permite básicamente que la información se preserve en un archivo cuando se reinicie el sistema.

REGSAM samDesired: Aquí se especificará qué operación se permitirá realizar a la llave que se retorna, es decir, a la que se creó. Si se especifica 0, una aplicación podría obtener ACCESS DENIED cuando trate de escribir sobre ella, algo que puede ser demostrado fácilmente con Process Monitor:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_1C3DD35A.png

Los parámetros ideales para esta variable, pueden ser escogidos entre una lista que recibe el tipo de dato para determinar los derechos de acceso. Los más comunes son:

KEY_ALL_ACCESS
KEY_READ
KEY_WRITE
KEY_QUERY_VALUE.

Pueden ver toda la lista aquí:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms724878(v=vs.85).aspx

LPSECURITY_ATTRIBUTES lpSecurityAtrtributes: Si se desea establecer los ACLs para la clave que se cree, se utilizarían los parámetros de este tipo de dato. Lo más fácil para empezar, es dejarlo como NULL, para que obtenga los descritpores de seguridad predeterminados.

PHKEY phkResult: Este no es más que un apuntador a la variable que se creó o se abrió. Basta con delclararlo sin entregarle contenido, pues lo recibirá después de la operación.

DWORD lpdwDisposition: Este es un apuntador a una variable que puede recibir los siguientes valores después de la operación:

REG_CREATED_NEW_KEY.
REG_OPENED_EXISTING_KEY.

El que reciba una u otra, dependerá de si la clave que intentemos crear esté creada, incompleta o no se haya creado. Si está creada o incompleta, se recibirá REG_OPENED_EXISTING_KEY, pero si no está creada, se recibirá REG_CREATED_NEW_KEY.

*Nota: Internamente, Windows recibe es el número que referencia a estos valores. Si se especifica como NULL, no habrá retorno de nada.

Cabe aclarar que la función en sí misma tiene un retorno; en caso de ser satisfactoria la operación, es decir, que se creó, devolverá: ERROR_SUCCESS, aunque por experiencia propia, se vuelve más cómodo trabajar con el valor que retorne lpdwDisposition.

Con lo anterior claro, o por lo menos explicado de la mejor forma que entendí, y pude, lo siguiente es finalmente pasar a personalizar los parámetros y valores para que generar nuestra clave:

Creando nuestra primera clave de Registro

Para proceder y ser prácticos en toda la teoría anterior, crearé una clave que se llame: MiClave, dentro de HKEY_CURRENT_USER\Checho’s Blog. Para no tener mayores inconvenientes con permisos ahora, y entre otras, porque todas las aplicaciones deberían escribir siempre allí; le pondré permisos para leer y escribir desde samDesired (KEY_READ | KEY_WRITE) y para poder corroborar, imprimiré un mensaje que me diga el resultado de la operación.

*Nota: Trataré lo del MessageBox al final del post.

Declarando las variables que introduje anteriormente, y con referencia a lo que quiero crear, el código quedaría así:

#include <Windows.h>

//Se declara método principal 

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

//Declaración de variables y sus parámetros:

HKEY hKey=HKEY_CURRENT_USER;
LPCTSTR lpSubKey=L"Checho's Blog\\MiClave";
DWORD Reserved=0;
LPTSTR lpClass=NULL;
DWORD dwOptions=REG_OPTION_NON_VOLATILE;
REGSAM samDesired=KEY_READ | KEY_WRITE;
LPSECURITY_ATTRIBUTES lpSecurityAttributes=NULL;
HKEY phkResult;
DWORD lpdwDisposition;


}//Cierra método principal.

*Nota: Hasta aquí, aún no hemos creado la clave, sólo se declararon las variables que utilizará la función de RegCreateKeyEx.

Ahora bien, en la función solo tendríamos que remplazar lo que pide con los valores en el orden adecuado, es decir:

RegCreateKeyEx(hKey,
               lpSubKey,
               Reserved,
               lpClass,
               dwOptions,
               samDesired,
               lpSecurityAttributes,
               &phkResult,
               &lpdwDisposition);

*Nota: En la documentación oficial, phkResult, debe ser de tipo PHKEY para que la función lo acepte, pero como es necesario después cerrar la operación con RegCloseKey, y ésta solo acepta un tipo HKEY, fue necesario declararla como tal, y dentro del la función anteponer un ‘&’ para que se refiere a la misma dirección de memoria que tiene nuestro valor. Lo mismo sucede con lpdwDisposition, pues para operar su resultado después, requiere que sea mínimo DWORD. Si logro encontrar la razón, o la forma de cómo no tener que modificarlas para suplir estas dos necesidades, actualizaré el post.

Con todo lo anterior, ya nuestra clave se crea sin ningún problema, pero como dije antes, es bueno tener algún tipo de respuesta de parte de la aplicación para saber cuándo fue exitoso, o cuándo falló. Para esto, utilizaremos el resultado que devuelva lpdwDisposition, y el método de MessageBox para mostrar un mensaje al usuario.

Como podemos tener REG_CREATED_NEW_KEY o REG_OPENING_EXISTING_KEY en lpdwDisposition, jugaremos con una sentencia de ‘if’ para mostrar el mensaje correcto.

El código sería:

//Validamos si la clave se creó.
if (lpdwDisposition==REG_CREATED_NEW_KEY)
{
    MessageBox(NULL,
        L"La clave se creó desde cero.",
        L"Mi primera Aplicación de Win32",
        MB_ICONINFORMATION);
}
//Validamos si la clave sólo se abrió, pues ya estaba creada.
else if(lpdwDisposition==REG_OPENED_EXISTING_KEY)
{
    MessageBox(NULL,
        L"La clave se abrió, pero no se modificó.",
        L"Mi primera Aplicación de Win32",
        MB_ICONEXCLAMATION);
}
//Error que no esté contemplado.
else
{
    MessageBox(NULL,
        L"Error al crear o abrir la clave.",
        L"Mi primera Aplicación de Win32",
        MB_ICONERROR);
 
}

Estamos validando con if, tres opciones:

1. Si la clave se crea porque no existe.
2. Si no sucede lo primero, se valida que la clave ya exista.
3. Si no se da alguna de las anteriores, indicamos error.

Los tres mensajes se hacen con un MessageBox. Es muy sencillo de crear, le indicamos un valor NULL como primera parámetro, lo segundo es el texto que deseamos tenga, el tercer parámetro es el título del mensaje y el último, el icono y/o bonotes que puede mostrar. Más información acerca de la función de MessageBox:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx

Finalmente, y como algo que siempre debe hacerse, cerramos la operación que hicimos sobre la sublcave de Registro con la función RegCloseKey, que solo recibe un parámetro, y es el apuntador que devuelve en este caso phkResult. La sintaxis sería:

RegCloseKey(phkResult);

Juntando todo lo anterior, el código completo sería:


#include <Windows.h>
 
//Se declara método principal 
 
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, 
                   int nCmdShow){
 
//Declaración de variables y sus parámetros:
 
HKEY hKey=HKEY_CURRENT_USER;
LPCTSTR lpSubKey=L"Checho's Blog\\MiClave";
DWORD Reserved=0;
LPTSTR lpClass=NULL;
DWORD dwOptions=REG_OPTION_NON_VOLATILE;
REGSAM samDesired=KEY_READ | KEY_WRITE;
LPSECURITY_ATTRIBUTES lpSecurityAttributes=NULL;
HKEY phkResult;
DWORD lpdwDisposition;
 
//Declarando la función:
 
RegCreateKeyEx(hKey,
               lpSubKey,
               Reserved,
               lpClass,
               dwOptions,
               samDesired,
               lpSecurityAttributes,
               &phkResult,
               &lpdwDisposition);
 
//Validamos si la clave se creó.
if (lpdwDisposition==REG_CREATED_NEW_KEY)
{
    MessageBox(NULL,
        L"La clave se creó desde cero.",
        L"Mi primera Aplicación de Win32",
        MB_ICONINFORMATION);
}
//Validamos si la clave sólo se abrió, pues ya estaba creada.
else if(lpdwDisposition==REG_OPENED_EXISTING_KEY)
{
    MessageBox(NULL,
        L"La clave se abrió, pero no se modificó.",
        L"Mi primera Aplicación de Win32",
        MB_ICONEXCLAMATION);
}
//Error que no esté contemplado.
else
{
    MessageBox(NULL,
        L"Error al crear o abrir la clave.",
        L"Mi primera Aplicación de Win32",
        MB_ICONERROR);
 
}
 
RegCloseKey(phkResult);
 
}//Cierra método principal.

Cuando se cree, el mensaje indicado en MessageBox anterior debería ser similar al siguiente:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_2DB0C4E3.png

Cuando la clave ya exista, el mensaje debería ser similar al siguiente:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_665DFFA1.png

Si vemos con Process Monitor el resultado de los permisos resultantes en la subclave, teniendo en cuenta que le indiqué lectura y escritura, se vería así:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_45D950A0.png

Las propiedades del resultado deben indicar claramente los permisos asignados:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_3731356C.png

*Dato curioso: La información actual sobre esta función (RegCreateKeyEx), indica que la DLL o el módulo que la contiene, es Advapi32.dll, pero al momento de confirmar esto, utilizando la pestaña de Stack que tiene Process Monitor, que permite ver operaciones a nivel de usuario y Kernel, el resultado es diferente:

http://geeks.ms/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/checho/image_5F00_thumb_5F00_48A426F5.png

Como ven, el módulo que ahora contiene esta y la gran mayoría de las principales funciones de la API de Windows, es: KernelBase.dll.

Según me confirmó muy amablemente Aaaron Margosis de Microsoft, existen todavía unos puntos de entrada en Advapi32.dll, pero redireccionan a KernelBase.dll, y que además esto sucede desde Windows 7.

Espero les sea de utilidad, y por supuesto, si hay algo en lo que puedan aportar, no duden en hacer su comentario.