Versión 3.0

800x600 mínimo
En esta lección:

Requisitos básicos de un protector
-----
El esqueleto del protector
-----
Características propias del protector
-----
Compilando el protector
-----
Descargas
-----


Otras secciones:

Conceptos básicos
-----
Programando en C
-----
Programando en C++
-----
Programando Windows 9x.
-----
Teoría electrónica
-----
Circuitos electrónicos
-----
Actividades adicionales
-----
Hipervínculos
-----


Contácteme:

Dudas y comentarios
-----


Programando un protector de pantalla

     Sin duda alguna todos sabemos qué es un protector de pantalla, se trata de un programa que se ejecuta automáticamente después de que el ratón y el teclado han estado inactivos por un periodo de tiempo determinado. Las razones principales de la existencia de tales programas son:

  1. Para evitar que el recubrimiento fosforescente del monitor se marque como consecuencia de dejar una imagen estática por un tiempo prolongado.
  2. Para proteger información sensible desplegada en la pantalla del monitor en ausencia del operador de la computadora.
  3. Para desplegar propaganda comercial.
  4. Por diversión.

     El propósito de éste artículo es mostrar la manera de programar un protector de pantalla sencillo poniendo en práctica lo expuesto en los pasados tres capítulos de la sección Programando Windows 9x., además éste artículo está basado en diferentes documentos encontrados en la red, incluyendo la documentación de Windows y el artículo escrito por Lucian Wischik.

Un reloj binario

     El protector de pantalla mostrado en éste capítulo despliega un reloj que muestra la hora en un formato de 24 horas utilizando el sistema numérico binario. De acuerdo con la imagen a la derecha, el reloj está compuesto de seis columnas, de izquierda a derecha las primeras dos indican el valor para la hora, las siguientes dos columnas son para el valor de los minutos y las dos columnas de la derecha despliegan el valor correspondiente a los segundos. Además consta de cuatro renglones, al inferior le corresponden valores de 20=1, el siguiente renglón toma valores de 21=2, el siguiente renglón hacia arriba es para valores de 22=4 y el renglón superior toma valores de 23=8. La hora se obtiene sumando los respectivos valores de cada columna, por ejemplo la hora desplegada en la figura es igual a 18:37:29. La manera en que despliega la información de la hora es ideal para familiarizarse con el sistema numérico binario, para más información puede consultar el artículo Sistemas numéricos y representación de datos de la sección Conceptos básicos.

Requisitos básicos de un protector de pantalla 

     Empezamos el estudio de cómo programar un protector de pantalla por el final. Un protector de pantalla es un programa ejecutable en la plataforma Windows al cual le cambiamos la extensión, de *.exe por *.scr, además el programa debe responder a una serie de argumentos pasados desde la línea de comandos. Dependiendo del sistema operativo en que se ejecute el protector será el tratamiento de los argumentos de la línea de comandos, Windows NT se encarga por sí del manejo de contraseñas y de la respuesta a la actividad del ratón y del teclado, en cambio bajo Windows 9x. es necesario escribir rutinas especiales para manejar éstos eventos. Debido a que el código fuente del protector presentado en éste capítulo es un poco extenso he decidido mostrarlo en una ventana separada, haga clic aquí para desplegar el código que le servirá a lo largo de todo lo expuesto en éste capítulo.

¿Cómo y cuando se ejecuta un protector de pantalla?

     Existen diversas circunstancias en las cuales se ejecuta un protector:

  • Inactividad del sistema: Cuando la computadora ha estado inactiva por un tiempo y siempre que no esté presente una ventana de un programa de entrenamiento basado en computadora, el sistema envía un mensaje WM_SYSCOMMAND al la ventana actual de fondo (o sea el escritorio de Windows) con el argumento SC_SCREENSAVE, si la ventana activa no es una aplicación de sistema de Windows ó si el mensaje no es capturado entonces éste es pasado al procedimiento por defecto ejecutandose el protector a pantalla completa pasando el argumento /s en la línea de comandos.

  • Propiedades de pantalla: Cuando se invoca el cuadro de diálogo propiedades de pantalla seleccionando la pestaña protector de pantalla se ejecuta el protector de pantalla seleccionado pasando el argumento /p #### en la línea de comandos, donde #### indica un número decimal correspondiente al handle de ventana (HWND) de la ventana padre, ésta ventana es el color del escritorio llenando con éste color la pequeña imagen que aparece en el cuadro de diálogo y que representa un monitor. Su protector de pantalla debe generar una vista miniatura exactamente del mismo tamaño que ésta ventana de vista previa.

  • Configuración: Cuando se hace clic en el botón marcado Configuración... en el cuadro de diálogo propiedades de pantalla bajo la pestaña protector de pantalla la ventana de vista previa se destruye ejecutandose el protector pasando el argumento /c en la línea de comandos, éste argumento tiene el efecto de desplegar el cuadro de diálogo propio del protector que sirve para ajustar los valores que sean necesarios de acuerdo a la operación del protector en sí, éste cuadro de diálogo se ejecuta como una ventana hija de la ventana que está en primer plano, que en éste caso se trata del cuadro de diálogo Propiedades de pantalla. Una vez que se ha ejecutado el cuadro de diálogo del protector debe correr nuevamente la opción de vista previa del protector.

  • Contraseña: Si el usuario selecciona la opción Protegido por contraseña y hace clic en el botón Cambiar... en primer lugar se destruye la ventana de vista previa. Enseguida se ejecuta el protector seleccionado pasando el argumento /a #### en la línea de comandos, donde #### es la representación decimal del handle de ventana del cuadro de diálogo Propiedades de pantalla, el protector debe responder a ésto desplegando el cuadro de diálogo estándar Cambiar contraseña, ó desplegar su propia rutina de contraseña, como una ventana hija de ####. Una vez que termina el cuadro de diálogo Cambiar contraseña debe restituirse la vista previa del protector.

  • Vista previa: Cuando el usuario presiona el botón Vista previa del cuadro de diálogo Propiedades de pantalla bajo la pestaña Protector de pantalla, la ventana de vista previa es destruida, luego se ejecuta el protector de pantalla en el escritorio de Windows pasando en la línea de comandos el argumento /s.

  • Probar: Si el usuario hace doble clic en el ícono correspondiente al protector de pantalla, éste se ejecuta en el modo de prueba, este caso es el mismo que el punto anterior excepto que no se hace nada en el cuadro de diálogo Propiedades de pantalla.

  • Configurar: Cuando el usuario hace clic con el botón secundario del ratón y selecciona la opción Configurar entonces se ejecuta el cuadro de diálogo propio del protector con el valor de NULL para la ventana padre del cuadro de diálogo, o sea se ejecuta solamente el cuadro de diálogo del protector.

     Se debe tener especial cuidado acerca de cualquier actividad que pueda ganar el foco de entrada, si su protector ejecuta una ventana de primer nivel, ésto causa un desorden con el enfoque, entonces el cuadro de diálogo Propiedades de pantalla obtendrá el foco lo que causaría que se ejecute una segunda instancia de su protector y de ésta manera se genera un bucle infinito haciendo prácticamente imposible cerrar el cuadro de diálogo Propiedades de pantalla.

Argumentos para la línea de comandos

     El entorno esperado por el protector depende del argumento pasado en la línea de comandos. Si el argumento en la línea de comandos no es el esperado, entonces el protector debe terminar sin hacer nada.

  • /c, /c ####, ó sin argumentos: Como respuesta a cualquiera de éstos casos, el protector debe ejecutar su cuadro de diálogo de propiedades. Si no hay argumento entonces se utiliza NULL como valor para la ventana padre del cuadro de diálogo de propiedades del protector como se explicó arriba. Con /c como argumento el cuadro de diálogo utiliza el valor devuelto por la función GetForegroundWindow() como su ventana padre. En el caso de /c #### como argumento el protector debe interpretar a #### como una representación decimal de un handle de ventana y utilizar a ésta como su ventana padre.

  • /s Este argumento debe indicarle al protector que se ejecute a pantalla completa.

  • /p #### ó /l #### El protector debe tratar a #### como una representación decimal de un handle de ventana, debe ejecutar una ventana hija de éste handle y correr en modo de vista previa dentro de ésta ventana.

  • /a #### Este argumento en la línea de comandos sólo se utiliza con Windows 9x. El protector ejecuta la rutina de contraseña explicada arriba.

Volver al principio

Hola mundo

El esqueleto del protector 

     Como se mencionó al principio, un protector de pantalla es básicamente un programa ejecutable en la plataforma Windows que responde a una serie de argumentos pasados en la línea de comandos. Los argumentos y la extensión *.scr forman la parte medular del funcionamiento del programa como un protector de pantalla, naturalmente necesitamos que nuestro programa antes que nada sea capaz de ejecutar una tarea específica, para éste artículo escogí desplegar en forma gráfica la hora del reloj de sistema mediante el código decimal binario, distribuyendo en la totalidad del área cliente del programa un conjunto de rectángulos de color rojo que conforman una matriz de 6x4. Cada rectángulo es generado utilizando la función SetRect() y para cada uno de ellos existe una función similar de tal manera que con una función se colorea el rectángulo de rojo y con su función complementaria se colorea el rectángulo de negro, todo el conjunto de operaciones para dibujar en el área cliente la hora del sistema se realiza como respuesta a un mensaje WM_PAINT. La lógica para desplegar en forma gráfica un valor binario se presentó en el programa dacwin.c del artículo Conversión Digital/Analógico, conviene que estudie también éste programa.

     Como respuesta a un mensaje WM_CREATE el programa genera un temporizador mediante la función SetTimer() con un intervalo especificado en el tercer parámetro igual a 100 milisegundos, es decir Windows enviará al procedimiento de ventana del protector un mensaje WM_TIMER cada 100 milisegundos, escogí éste valor para tener la oportunidad de refrescar la pantalla al menos diez veces por segundo y evitar así lecturas erráticas para el valor de unidades de segundos. Cada vez que el procedimiento de ventana recibe un mensaje WM_TIMER se obtiene la hora del sistema almacenando el valor en una estructura de tipo tm llamada datetime, además se almacenan en variables globales de tipo div_t los valores correspondientes a las horas, minutos y segundos. Acto seguido se invalida la totalidad del área cliente del programa llamando a la función InvalidateRect() y por lo tanto generando un mensaje WM_PAINT. Se especifica el valor de FALSE en el tercer parámetro para evitar parpadeo en la pantalla. Como puede observar al estudiar el procedimiento de ventana del protector, toda la actividad visual se realiza como respuesta a un mensaje WM_PAINT. Si aún no despliega el código fuente, haga clic aquí.

     Como en la mayoría de los protectores de pantalla, el programa debe terminar si el usuario presiona una tecla, mueve el ratón ó presiona uno de los botones del ratón, ésto significa que el programa debe responder a los mensajes WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN y WM_KEYDOWN, el programa hace ésto llamando a la función verifica_password() y posteriormente envía un mensaje WM_CLOSE al procedimiento de ventana por defecto que a su vez genera un mensaje WM_DESTROY que el procedimiento de ventana del protector procesa destruyendo el temporizador y la ventana principal del protector. Se puede ver que la actividad propia del programa es en realidad sencilla. Hasta este punto el programa no difiere en lo absoluto de un programa convencional para Windows. Veamos ahora los aspectos particulares que hacen del programa un protector de pantalla.

Volver al principio

Hola mundo

Características propias de un protector de pantalla 

     Por lo expuesto en el párrafo anterior es conveniente programar toda la actividad que desarrollará nuestro protector de pantalla y luego agregar la funcionalidad propia de un protector, ésto incluye la lectura de la línea de comandos para determinar el argumento con el cual se ejecutará nuestro protector, el procedimiento de ventana para el cuadro de diálogo de las propiedades del protector y las funciones para establecer y/o cambiar la contraseña del protector.

La línea de comandos

     El siguiente fragmento de código demuestra una forma de leer la línea de comandos para determinar cómo se ejecutará nuestro programa:


letra = GetCommandLine();
if (*letra == '\"')
{
    letra++;
    while(*letra != 0 && *letra != '\"')
    letra++;
}
else
{
    while (*letra != 0 && *letra != ' ')
    letra++;
}
if(*letra != 0)
letra++;
while(*letra == ' ')
letra++;
hwnd = NULL;
if(*letra == 0)
{
    ScrMode = smConfig;
    hwnd = NULL;
}
else
{
    if(*letra == '-' || *letra == '/')
    letra++;
    if(*letra == 'p' || *letra == 'P' || *letra == 'l'
      || *letra == 'L')
    {
        letra++;
        while(*letra == ' ' || *letra == ':')
        letra++;
        if((strcmp(letra, "scrprev") == 0)
          || (strcmp(letra, "ScrPrev") == 0)
          || (strcmp(letra, "SCRPREV") == 0))
        hwnd = CheckForScrprev();
        else
        hwnd = (HWND)atoi(letra);
        ScrMode = smPreview;
    }
    else
    if(*letra == 's' || *letra == 'S')
    ScrMode = smSaver;
    else
    if(*letra == 'c' || *letra == 'C')
    {
        letra++;
        while
        (*letra == ' ' || *letra == ':')
        letra++;
        if(*letra == 0)
        hwnd = GetForegroundWindow();
        else
        hwnd = (HWND)atoi(letra);
        ScrMode = smConfig;
    }
    else
    if(*letra == 'a' || *letra == 'A')
    {
        letra++;
        while(*letra == ' ' || *letra == ':')
        letra++;
        hwnd = (HWND)atoi(letra);
        ScrMode = smPassword;
    }
}

     El programa utiliza la función GetCommandLine() para obtener el argumento pasado en la línea de comandos y almacena el valor en la variable llamada letra. En seguida el programa ejecuta una serie de decisiones condicionales basadas en la instrucción if para determinar todas las opciones posibles en la línea de comandos, sean letras minúsculas, mayúsculas y símbolos de barra inclinada, de dos puntos y guión corto. De esta manera el programa puede responder a cualquier argumento comprendido por ejemplo entre /p #### y -p:####. El programa utiliza una variable de tipo enum llamada TScrMode para identificar cada una de las diferentes opciones que el programa debe ejecutar basandose en el argumento encontrado en la línea de comandos. Los posibles modos están definidos en el programa como smNone, smConfig, smPassword, smPreview y smSaver. El programa ejecuta una acción específica en todos éstos modos excepto para smMode en cuyo caso la función principal WinMain() regresa 0 terminando el programa sin hacer nada.

Modo smConfig

     El programa especifica el modo smConfig cuando en la línea de comandos se encuentra presente el argumento /c ó bién cuando el usuario presiona el botón secundario del ratón y selecciona la opción Configurar lo que causa que se active el cuadro de diálogo propio del protector, la sección de código que causa la activación del cuadro de diálogo es esta:


if (ScrMode == smConfig)
{
    DialogBox(hInstance, "Configura", hwnd, ConfiguraProc);
    return 0;
}

     La función DialogBox() activa el cuadro de diálogo Configura y los mensajes generados por éste se controlan en el procedimiento de ventana llamado ConfiguraProc cuyo código es el siguiente:


BOOL CALLBACK ConfiguraProc(HWND hDlg, UINT iMsg,
  WPARAM wParam, LPARAM lParam)
{
    switch(iMsg)
    {
        case WM_INITDIALOG:
        return TRUE;

        case WM_COMMAND:
        switch(LOWORD(wParam))
        {
            case IDOK:
            case IDCANCEL:
            EndDialog(hDlg, 0);
            return TRUE;
        }
        break;
    }
    return FALSE;
}

     Se aprecia que el cuadro de diálogo del protector de pantalla en realidad no hace nada, simplemente muestra un mensaje y el usuario hace clic en el botón para retornar el control al programa, sin embargo en éste cuadro de diálogo se puede especificar por ejemplo, cambiar el color de los cuadros que representan la hora del sistema. El cuadro de diálogo se especifica en el archivo de recursos llamado relojbin.rc que a su vez utiliza el archivo de cabecera llamado relojbin.h. Otros elementos considerados como recursos del programa son el cursor transparente llamado relojbin.cur y el ícono del programa que apropiadamente se llama relojbin.ico. Todos los recursos utilizados en el protector de pantalla de éste capítulo fueron generados utilizando Microsoft Visual C++ 6.0, el código fuente del archivo de recursos relojbin.rc lo puede descargar en la sección Descargas al final de éste artículo.

Modo smPassword

     En el código fuente podemos ver qué sucede cuando en la línea de comandos se encuentra el argumento /a #### el cuál especifica el manejo de una contraseña, el programa llama a la función password() pasando a ésta como argumento el valor ####:


if (ScrMode == smPassword)
password(hwnd);

     La función password() ejecuta las siguientes instrucciones para el manejo de la contraseña del protector:


//  Establece o cambia el password
void password(HWND hwnd)
{
    HINSTANCE hpassword;
    typedef VOID (WINAPI *PWDCHANGEPASSWORD)(LPCSTR lpcRegkeyname,
      HWND hwnd, UINT uiReserved1, UINT uiReserved2);
    PWDCHANGEPASSWORD PwdChangePassword;

    hpassword = LoadLibrary("MPR.DLL");
    PwdChangePassword = (PWDCHANGEPASSWORD)GetProcAddress(hpassword,
      "PwdChangePasswordA");
    if(PwdChangePassword == NULL)
    {
        FreeLibrary(hpassword);
        return;
    }
    PwdChangePassword("SCRSAVE", hwnd, 0, 0);
    FreeLibrary(hpassword);
}

     Esta función redefine como de tipo VOID el tipo de dato PWDCHANGEPASSWORD y luego declara una variable llamda PwdChangePassword. Todo el proceso relacionado con el manejo de la contraseña se encuentra en la librería de enlace dinámico llamada MPR.DLL, muy conveniente porque en caso de que asignemos una contraseña con nuestro protector de pantalla, dicha contraseña seguirá siendo útil con el resto de los protectores de pantalla enlistados en el cuadro de diálogo Propiedades de pantalla.

     En caso de que se haya asignado una contraseña al protector de pantalla y luego que se produzca un evento sea de teclado ó de ratón, el protector debe verificar que la contraseña introducida sea la correcta, esto sucede en el procedimiento de ventana como respuesta a los siguientes mensajes:


case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_KEYDOWN:
if(verifica_password(hwnd))
SendMessage(hwnd, WM_CLOSE, 0, 0);
return 0;

     Se aprecia que si al llamar a la función verifica_password() el valor devuelto por ésta es verdadero (TRUE) entonces el procedimiento de ventana envía un mensaje WM_CLOSE que causará que el protector de pantalla termine su funcionamiento. La función para verificar la contraseña es ésta:


//  Verifica el password
BOOL verifica_password(HWND hwnd)
{
    OSVERSIONINFO osv;
    HINSTANCE hpwdcpl;
    typedef BOOL (WINAPI *VERIFYSCREENSAVEPWD)(HWND hwnd);
    VERIFYSCREENSAVEPWD VerifyScreenSavePwd;
    BOOL bres;

    osv.dwOSVersionInfoSize = sizeof(osv);
    GetVersionEx(&osv);
    if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT)
    return TRUE;
    hpwdcpl = LoadLibrary("PASSWORD.CPL");
    if (hpwdcpl==NULL)
    return TRUE;
    VerifyScreenSavePwd = (VERIFYSCREENSAVEPWD)GetProcAddress(hpwdcpl,
      "VerifyScreenSavePwd");
    if (VerifyScreenSavePwd == NULL)
    {
        FreeLibrary(hpwdcpl);
        return TRUE;
    }
    bres = VerifyScreenSavePwd(hwnd);
    FreeLibrary(hpwdcpl);
    return bres;
}

     Esta función devuelve el valor de TRUE en caso de que el protector de pantalla se ejecute bajo Windows NT, en el caso de que el protector de pantalla se ejecuta en la plataforma Windows 9x. entonces se utiliza la función PASSWORD.CTL que verifica si la contraseña introducida por éste ú otro protector de pantalla previamente coincide con el valor introducido recientemente por el usuario de la computadora, en caso afirmativo la función devuelve TRUE, en caso contrario la función devuelve FALSE que es el valor almacenado en la variable llamada bres.

Modos smPreview y smSaver

     Entre estos dos modos existen un par de diferencias interesantes, si el argumento presente en la línea de comandos es /s entonces el protector de pantalla se ejecuta a pantalla completa, por otra parte, si el argumento presente es /p #### entonces el protector de pantalla se ejecuta en la modalidad de vista previa, la sección de código que controla éstas dos opciones es el siguiente:


if (ScrMode == smSaver || ScrMode == smPreview)
{
    wndclass.cbSize        = sizeof (wndclass);
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = ScreenSaverProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = LoadIcon (hInstance, szAppName);
    wndclass.hCursor       = LoadCursor (hInstance, szAppName);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szAppName;
    wndclass.hIconSm       = LoadIcon (hInstance, szAppName);
    RegisterClassEx (&wndclass);
 
    if (ScrMode == smPreview)
    {
        GetWindowRect(hwnd, &rc);
        hScrWindow = CreateWindowEx(0, szAppName, "SaverPreview",
          WS_CHILD|WS_VISIBLE,
          0, 0, rc.right-rc.left, rc.bottom-rc.top,
          hwnd, NULL, hInstance, NULL);
    }
    else
    {
        hScrWindow = CreateWindowEx(WS_EX_TOPMOST, szAppName,
          "SaverWindow", WS_POPUP|WS_VISIBLE,
          0, 0, GetSystemMetrics(SM_CXSCREEN),
          GetSystemMetrics(SM_CYSCREEN),
          NULL, NULL, hInstance, NULL);
    }
    .
    .
    .
}

     En ambos casos se establece las características generales para la clase de ventana de nuestro protector de pantalla llenando los diferentes campos de la estructura de tipo WNDCLASSEX. Sin embargo el tamaño de la ventana generada depende de argumento presente en la línea de comandos al momento de ejecutar el programa, si el argumento especifica la modalidad de vista previa entonces se genera una ventana para el protector de un tamaño especificado en la estructura de tipo RECT obtenidas a partir de la función GetWindowRect(). En caso de que el argumento presente en la línea de comandos especifique que el protector debe ejecutarse a pantalla completa, entonces se genera una ventana cuyo tamaño es igual a toda el área del monitor, es decir, a pantalla completa. Estas dimensiones se obtienen mediante la función GetSystemMetrics() especificando los parámetros SM_CXSCREEN y SM_CYSCREEN.

Volver al principio

Hola mundo

Compilando el protector 

     El protector de pantalla está compuesto de cinco archivos, el código fuente escrito en C llamado relojbin.c, el archivo de cabecera relojbin.h, el archivo de recursos llamado relojbin.rc, el cursor transparente relojbin.cur y el ícono del programa llamado relojbin.ico. Lo más conveniente es generar un proyecto para la plataforma Windows 9x en su propio compilador e incluir todos éstos archivos, después compilar dentro del proyecto tanto el archivo de recursos como el código fuente. Sin embargo también es posible compilar en forma separada el código fuente reloj.c para producir el archivo objeto relojbin.obj, por otra parte se compila el archivo de recursos incluyendo en éste proceso el archivo de cabecera relojbin.h produciendose así el archivo relojbin.res. Por último se enlazan los archivos relojbin.obj y relojbin.res para producir el archivo ejecutable llamado relobin.exe.

     Como dijimos al principio de éste artículo, el paso final consiste en cambiar el nombre al ejecutable resultante, de llamarse relojbin.exe pasará a llamarse relojbin.scr. Este es nuestro protector de pantalla terminado, para instalarlo simplemente cópielo en la carpeta del sistema operativo, generalmente llamada Windows que se encuentra en la raíz del disco duro principal, generalmente la unidad C.

Volver al principio

Hola mundo

Descargas 

     Los cinco archivos que componen el código fuente y los recursos utilizados en el protector de pantalla discutido en éste artículo, incluyendo una copia compilada del mismo se encuentran en el archivo comprimido llamado relojbin.zip (16.2 kb.), para descargarlo haga clic aquí.

Volver al principio

Hola mundo

© 1999 Virgilio Gómez Negrete, Derechos Reservados