INTRODUCCIÓN A LA PROGRAMACIÓN EN WINDOWS
2. Visualizar una ventana de diálogo
3. Modificar la posición del cursor y sus propiedades
4. Creación de ventanas y manipulación de mensajes
Este artículo se centra como su propio nombre indica en una introducción a la programación en windows. Se podrá utilizar cualquier entorno que soporte la programación en 32 bits sobre la plataforma Windows. En los ejemplos se usará el Entorno de Desarrollo Integrado (IDE) Microsoft Visual C++. En este tutorial, mediante la construcción de una serie de mini aplicaciones, se irán introduciendo contenidos de concepto y sobre todo contenidos procedimentales.
¿Cómo se puede comunicar una aplicación o proceso con el sistema operativo Windows?. Para ello existen un conjunto de funciones, procedimientos, tipos de datos, constantes que conforman lo que se denomina la Win32 API ( Application programming interface ).
Para poder usar la Win32 API hay que importar el módulo "windows.h". Ésta a su vez importa otros módulos como por ejemplo "windef.h" donde están declarados prácticamente todos los tipos de datos usados en windows. Si se le echa un vistazo a este último módulo citado se podrán ver las siguientes declaraciones de tipos:
Nombre tipo de dato | Tipo de dato equivalente |
ULONG | unsigned long |
PULONG | ULONG * |
USHORT | unsigned short |
PUSHORT | USHORT * |
UCHAR | unsigned char |
PUCHAR | UCHAR * |
PSZ | char * |
DWORD | unsigned long |
BOOL | int |
BYTE | unsigned char |
WORD | unsigned short |
PWORD | WORD near * |
LPWORD | WORD far * |
INT | int |
WPARAM | UINT |
LPARAM | LONG |
LRESULT | LONG |
2. Visualizar una ventana de diálogo
Se va a crear una aplicación que realice una llamada a una función de la API. El código fuente puedes descargártelo en el siguiente enlace:
Primeramente habrá que crear un proyecto de Win32 Console Application tal y como viene reflejado en la figura 1.
- Figura 1-
Una vez rellenado los campos con el nombre del proyecto y la ubicación del mismo, pulsar sobre el botón "OK".
- Figura 2 -
En la siguiente ventana (figura 2) seleccionar "An empty project", pues se quiere construir un proyecto vacío.
- Figura 3 -
En la figura 3, aparece una ventana donse se informa que se ha creado un proyecto vacío.
- Figura 4 -
Ahora se tendrá que añadir un archivo a nuestro proyecto que inicialmente se encontraba vacío ( ver figura 4).
- Figura 5-
Tal y como se ve reflejado en la figura 5, le pondremos un nombre al archivo. En este ejemplo se le ha puesto el nombre de "principal.c".
Ahora se va a introducir el código de la figura 6 en el archivo"ppal.c".
#include <windows.h> #include <stdio.h> void main(void) respuesta = MessageBox(NULL, "¿Tienes 18 años o más?", "Comprobación de edad del usuario",MB_YESNO); if (respuesta == IDYES) |
- Figura 6 -
Ejecutar el código pulsando sobre el icono con el signo de admiración o bien dándole a la combinación de teclas CTRL+F5.
Cómo se puede observar el resultado de ejecución es una ventana de diálogo ( figura 7 ).
- Figura 7 -
Vamos a analizar cada una de las líneas de código del archivo "principal.c".
En este archivo se importan las siguientes librerías:
1) windows.h : donde se encuentra la mayor parte de las funciones de WIN32 API
2) stdio.h : para poder usar la función printf, y todas las funciones de entrada/salida.
A continuación se realiza una llamada a la función MessageBox ( función perteneciente a la WIN32 API ). El prototipo de esta función es el siguiente:
int MessageBox( HWND hWnd, // manejador de la ventana propietaria
LPCTSTR lpText, // Texto que aparecerá dentro de la ventana
LPCTSTR lpCaption, // Texto que aparecerá en el título de la
ventana
UINT uType // Estilo de la ventana de diálogo
);
Generalmente en una aplicación existe una jerarquía de ventanas, como en este ejemplo no existe dicha jerarquía, el primer parámetro de la función ( ventana propietaria ) "MessageBox" se le pasará el valor NULL.
El estilo de la ventana de diálogo puede ser cualquiera de los siguientes:
MB_OK: un botón de aceptar, MB_OKCANCEL : un botón de aceptar y otro de cancelar, MB_YESNO : dos botones uno "Sí" y otro con el valor "No", etc....
La función "MessageBox" devuelve un entero que corresponde con una de las siguientes constantes:
IDCANCEL: Se ha seleccionado el botón de cancelar, IDNO:
se ha elegido el botón con la etiqueta "NO", IDYES: el botón
con la etiqueta "SÍ" ha sido pulsado, IDOK: se seleccionó
el botón "Aceptar".
3. Modificar la posición del cursor y sus propiedades
El código fuente puede descargárselo en el siguiente enlace:
Creamos un nuevo proyecto Win32 Console Application siguiendo los pasos del apartado anterior. Obviamente lo único que se va a alterar es el contenido del archivo principal.c.
#include <windows.h> #include <stdio.h> #include <conio.h> void main(void) printf("\nIntroduzca las siguientes coordenadas(LIMITES X=80,Y=25)"); //Hay que asegurar que las coordenadas no sobrepasan coordenadas.X = valor; do{ coordenadas.Y = valor; //Se indican las coordenadas en las cuales SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coordenadas); printf("Linea escrita en coordenada X:%hd,Y:%hd\n",
printf("\nPulsa una tecla para volver a visualizar el cursor"); //Vuelve otra vez a ser visible el cursor printf("\nPulsa una tecla para borrar la pantalla"); coordenadas.X = 0; //Se va a insertar el color. Todos los colores atributo_color = FOREGROUND_BLUE | FOREGROUND_GREEN for(i = 0; i< 25; i++) FillConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), printf("\nFin de programa\n"); } |
- Figura 8 -
En la figura 8 se podrá ver el contenido del archivo "principal.c".
Este programa realiza las siguientes acciones:
A) Situar el cursor en una posición determinada
Para ello se realiza una llamada a la siguiente función:
BOOL SetConsoleCursorPosition(
HANDLE hConsoleOutput, // Manejador de la pantalla de la consola
COORD dwCursorPosition // Nuevas coordenadas
);
En el primer parámetro se le pasa el manejador de la salida estándar. Gracias a la siguiente llamada obtenemos como valor de retorno el manejador de la salida estándar:
GetStdHandle(STD_OUTPUT_HANDLE)
En el segundo argumento hay que pasarle las coordenadas donde se quiere posicionar el cursor:
typedef struct _COORD {
SHORT X; // coordenada horizontal
SHORT Y; // coordenada vertical
} COORD;
B) Ocultar el cursor
Para ocultar el cursor se llama a la función "SetConsoleCursorInfo":
BOOL SetConsoleCursorInfo(
HANDLE hConsoleOutput, // Aquí se le pasará como en el apartado
anterior, el manejador de la salida estándar.
CONST CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
// Dirección de la estructura que describe las características
del cursor.
);
La estructura del tipo de dato CONSOLE_CURSOR_INFO es la siguiente:
typedef struct _CONSOLE_CURSOR_INFO { // cci
DWORD dwSize;
BOOL bVisible; //Este campo indica si el cursor va a ser visible (TRUE) o
no ( FALSE)
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
El campo "dwSize" especifica un número entre
1 y 100, indicando el porcentaje de la celda del carácter que será
rellenada por el cursor.
CONSOLE_CURSOR_INFO info_cursor;
info_cursor.bVisible = FALSE;
info_cursor.dwSize = 100;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info_cursor);
C) Volver a mostrar el cursor
Prácticamente igual que en el apartado anterior, lo que ocurre es que el tamaño tomará el valor 10 ( tamaño normal ) y el campo visible el valor TRUE (bVisible).
D) Borrar el contenido de la pantalla.
//El atributo de color se tiene que realizar con la combinación de los colores rojo, verde y azul ( será una variable de tipo "int"):
atributo_color = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ;
//Primero se rellena la coordena X con el valor 0:
coordenadas.X = 0;
//Se hace un barrido de las 25 líneas de texto
for(i = 0; i< 25; i++)
{
//En cada iteración se modifica el número de línea
coordenadas.Y = i;
//Se indica el color (atributo_color), cuántos caracteres se van a escribir (80), y la posición a partir de la cual se va a escribir ( //coordenadas )
FillConsoleOutputAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
atributo_color, 80, coordenadas, &escrito);
//Se va a escribir el carácter espacio blanco (' '), 80 veces, a partir de las coordenadas especificadas ( coordenadas )
FillConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE),
' ', 80, coordenadas, &escrito);
}
4. Creación de ventanas y manipulación de mensajes
Windows utiliza el modelo de programación conducido por eventos. De tal forma, que generalmente, las aplicaciones de Windows se inicializan y esperan a que suceda algo, un "evento". Ese evento puede ser que se presione una tecla, que se realice cualquier acción con el ratón, etc... Pero cómo funciona este mecanismo internamente: cuando ocurre un "evento", Windows envía un mensaje a la aplicación sobre la cual va dirigida el evento, añadiendo para ello el mensaje a la "cola de mensajes" que tiene cada aplicación. Obviamente la aplicación estará constantemente comprobando el contenido de esa cola de mensajes, cuando recibe un mensaje éste será enviado a la "Ventana de Procedimiento" ( window procedure). Esta ventana de procedimiento ejecutará una serie de acciones en respuesta al mensaje recibido.
- Figura 9 -
Cada aplicación tendrá su propia cola de mensajes ( observar figura 9 ). El bucle de mensajes está comprobando constantemente el contenido de la cola de mensajes. Se encargará de obtener el mensaje de la cola y lo enviará al procedimiento de ventana ( windows procedure ). Una vez que este bucle emita el mensaje, Windows ejecutará "automáticamente" el procedimiento de ventana. Con respecto al procedimiento de ventana, aclarar que una aplicación podrá tener varios procedimientos de ventana. En este procedimiento de ventana se escribirá el código que será ejecutado en respuesta a un evento determinado.
A continuación se van a indicar los pasos necesarios para crear una aplicación muy sencilla. El código del siguiente ejemplo puedes descargártelo pulsando en el siguiente enlace:
Lo primero de todo es crear un nuevo proyecto del tipo "Win32 Application Project" ( AVISO: ¡No seleccionar Win32Console Application Project!).Ver figura 10.
- Figura 10 -
Una vez seleccionado el tipo de proyecto, en la siguiente ventana ( figura 11 ), se elige "An empty project".
- Figura 11 -
Ahora habrá que añadir al menos un archivo al proyecto con extensión .c ( por ejemplo "principal.c"). Para ello se situa el ratón en la carpeta "Source files" se pulsa el botón derecho del ratón y se hace click en la opción "Add Files to Folder..." ( figura 12 )
- Figura 12 -
Ahora mismo dispondremos de un archivo sin ninguna línea de código. Lo primero que habrá que hacer es importar el módulo "windows.h" ( #include <windows.h> ), que tendrá todo lo necesario para la programación en windows ( Win32 API ). El programa tendrá que tener como mínimo las siguientes dos funciones:
1) int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int); Esta función es la equivalente al "main()" pero para la programación en windows.
2) LRESULT CALLBACK ProcesaMensajes(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); Esta función manejará los eventos que recibirá la ventana. Se le denomina "Procedimiento de ventana".
Habrá que crear una variable global que se usará para identificar la ventana principal. Todas aquellas variables que hagan referencia a un objeto para poder identificarlo del resto se les denominará "Handle" ( manejador ). Se inicializará al valor cero.
El código hasta ahora escrito quedará de la siguiente forma:
#include <windows.h>
/*Esta variable se utilizará
para identificar la ventana ppal. "Handle" a una ventana.
En la programación de windows se usará los "Handles"
para poder referenciar a cualquier objeto*/ /*Prototipos de subprogramas*/ LRESULT CALLBACK ProcesaMensajes(HWND hwnd, UINT
msg, WPARAM wParam, LPARAM lParam); |
- Figura 13 -
A continuación se verá la primera función: "WinMain". Esta función se encargará de crear la ventana, y luego entrará en un bucle en el cual irá recuperando los mensajes de la cola. Para la creación de una ventana se tendrán que seguir los siguientes pasos:
PRIMER PASO
int
WINAPI WinMain( /***************************************************************************** WNDCLASSEX clase_ventana; //Esta
variable se usará para especificar los atributos de la ventana
clase_ventana.lpfnWndProc = ProcesaMensajes;
// Se le indica cuál será //Los siguientes parámetros
indican si se quiere un almacenamiento extra después de la clase
ventana (cbClsExtra) o de la estructura window // (cbWndExtra). //Hay que pasarle el manejador
de la instancia de la propia aplicación. Se utiliza el
parámetro pasado en WinMain ( hInstance ) //Se especifica el icono de
la barra de título. Para cargar el icono se usa la API "LoadIcon".
Hay iconos predefinidos como: //IDI_APPLICATION, IDI_WINLOGO,
etc... //Color de fondo de la ventana
( WHITE_BRUSH, BLACK_BRUSH, HOLLOW_BRUSH ( fondo transparente )) //Manejador de un icono que
se usa para la barra de tareas y esquina |
- Figura 14 -
Como se puede observar en el primer paso se ha usado una constante denominada "CLASE_VENTANA". Para la definición de esa constante habrá que escribir la siguiente línea de código en las primeras líneas del programa:
#define CLASE_VENTANA "La clase ventana"
Esta constante lo que realmente almacena es el nombre que va a tener la clase creada, para poder referenciarla posteriormente.
SEGUNDO PASO
/***************************************************************************** if (!RegisterClassEx(&clase_ventana)) |
- Figura 15 -
TERCER PASO
/***************************************************************************** IdentificadorVentanaPrincipal = if (!IdentificadorVentanaPrincipal) |
- Figura 16 -
CUARTO PASO
/***************************************************************************** |
- Figura 17 -
QUINTO PASO
/***************************************************************************** |
- Figura 18 -
SEXTO PASO
/***************************************************************************** //Se rellena el bloque de memoria con ceros seguir = 1; while (seguir) ..................................../*
Es un mensaje distinto al de WM_QUIT*/ |
- Figura 19 -
Ya se ha finalizado el código básico de la función "WinMain". Lo lógico para que el código del programa quede un poco "más ordenado" hubiera sido crear otra función que tuviese el código del sexto paso. Para bajarte este ejemplo organizado de la forma descrita pinchar en el siguiente enlace:
A este último trozo de código ( sexto paso ) se le denomina bucle de mensajes. Este bucle de mensajes se puede resumir en la siguiente secuencia de pasos:
1) Se usa la función "PeekMessage" para buscar en la cola de mensajes. En el caso de estar vacía, la función devolvería falso, y por tanto se pasaría a ejecutar el bloque de código "else".
2) Una vez obtenido un mensaje de la cola, se usará la función "TranslateMessage", el cual traduce los mensajes de teclas virtuales a mensajes con caracteres.
3) Una vez traducido el mensaje llamamos a "DispatchMessage", con lo que se consigue enviar dicho mensaje al Procedimiento de Ventana definido ( en nuestro ejemplo: "ProcesaMensajes").
A continuación se va a entrar en la segunda función "ProcesaMensajes":
/* Esta función es el manejador
de los mensajes de windows. Cada vez que ..................break; |
- Figura 20 -
En "ProcesaMensajes" se realizará una acción asociada según el tipo de mensaje. Siempre, una vez finalizado el tratamiento del mensaje, habrá que llamar a la función "DefWindowProc" la cual realizará las acciones por defecto según el tipo de mensaje.
Una vez procesado el mensaje se volvería al punto desde donde se hizo la llamada a "DispatchMessage", regresando por tanto de nuevo al bucle de mensajes.
¿Qué contiene una variable del tipo MSG?:
typedef struct {
HWND hwnd; //manejador de la ventana a la cual va dirigida el mensaje
UINT message; // Identificador del mensaje
WPARAM wParam; // Información adicional del mensaje
LPARAM lParam;// Información adicional del mensaje
DWORD time;// Hora en la que el mensaje fue enviado
POINT pt; // Coordenadas del cursor cuando se produjo el evento
} MSG, *PMSG;
Por ejemplo en los mensajes de teclado se tendrían los siguientes parámetros:
message WM_KEYDOWN
wParam Código de la tecla virtual. Podría
ser: VK_F1 a VK_F12 ( teclas F1 a F2 ), VK_ESCAPE ( Escape), VK_SPACE ( Espacio),
VK_RETURN ( Tecla enter ), VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN ( Teclas del
cursor )
LParam Datos adicionales sobre la tecla pulsada: Bits 0-15: Nº
de repeticiones generadas, Bits 16-23: Scancode, Bit 24: Especifica si es
extendida (1/0), etc...
Si las teclas tienen una correspondencia directa con el formato ASCII, se podría utilizar el mensaje WM_CHAR:
wParam Código del carácter.
LParam Flags extra (como WM_KEYDOWN).
- Figura 21 -
Cuando se pulsa la tecla "E" se generan los siguientes tres mensajes ( en este orden ): WM_KEYDOWN( cuando se pulsa una tecla), WM_CHAR, WM_KEYUP ( cuando se suelta la tecla ). Se puede por tanto aprovechar el segundo mensaje (WM_CHAR) para rescatar el código ASCII de la tecla pulsada. ( observar figura 21 )
Imaginemos que se pulsa la tecla de mayúsculas (SHIFT) a la vez que la tecla "E". Ésto provocaría la secuencia de mensajes mostrada en la figura 22.
- Figura 22 -
Para los mensajes producidos por eventos del ratón tenemos los siguientes tipos:
message WM_MOUSEMOVE ( El ratón se mueve sobre
el área cliente ), WM_LBUTTONDOWN ( Botón izquierdo pulsado),
WM_RBUTTONDOWN ( Botón derecho pulsado) , WM_LBUTTONUP ( Botón
izquierdo soltado), WM_RBUTTONUP ( Botón derecho soltado). WM_LBUTTONDBLCLK
( Doble click botón izquierdo), etc..
wParam Estado de las teclas cuando el ratón ha
producido el evento
LParam Coordenadas del ratón. LOWORD(lParam) = Coordenada X
del ratón, HIWORD(lParam) = Coordenada Y del ratón.
Espero que este pequeño artículo le haya servido como una pequeña introducción a la programación en windows. Ahora solamente os queda lo que hoy en día más falta hace, dar rienda suelta a tu IMAGINACIÓN y sobre todo CREATIVIDAD.
¡ Te deseo suerte de todo corazón!.
Autor: Daniel Leyva Cortés
Contacto: transistor47@hotmail.com
BIBLIOGRAFÍA
Introduction To 3D Game Programming With DirectX 9.0
http://msdn.microsoft.com/library/spa/
http://msdn.microsoft.com/library/
http://winprog.org/tutorial/es/
http://pinsa.escomposlinux.org/sromero/articulos/win95/win95-3.html