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.
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.
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:
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.
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.
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:
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:
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)
L"La clave se abrió, pero no se modificó.",
MB_ICONEXCLAMATION);
//Error que no esté contemplado.
else
L"Error al crear o abrir la clave.",
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);
//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:
}//Cierra método principal.
Cuando se cree, el mensaje indicado en MessageBox anterior debería ser similar al siguiente:
Cuando la clave ya exista, el mensaje debería ser similar al siguiente:
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í:
Las propiedades del resultado deben indicar claramente los permisos asignados:
*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:
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.