Win32 API
Tratar con ventanas
Buscar..
Creando una ventana
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
const TCHAR CLSNAME[] = TEXT("helloworldWClass");
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PTSTR cmdline,
int cmdshow)
{
WNDCLASSEX wc = { };
MSG msg;
HWND hwnd;
wc.cbSize = sizeof (wc);
wc.style = 0;
wc.lpfnWndProc = winproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLSNAME;
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, TEXT("Could not register window class"),
NULL, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx(WS_EX_LEFT,
CLSNAME,
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInst,
NULL);
if (!hwnd) {
MessageBox(NULL, TEXT("Could not create window"), NULL, MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, cmdshow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
return DefWindowProc(hwnd, wm, wp, lp);
}
Lo primero que se ve son las dos definiciones de macro, UNICODE
y _UNICODE
. Estas macros hacen que nuestro programa comprenda cadenas de caracteres anchos ( wchar_t[n]
), no cadenas simples y estrechas ( char[n]
). Como resultado, todos los literales de cadena deben estar envueltos en un TEXT(
macro. El tipo de carácter genérico para cadenas de Win32 es TCHAR
, cuya definición depende de si se define o no UNICODE
. Se incluye un nuevo encabezado: <tchar.h>
contiene el Declaración de TCHAR
.
Una ventana consiste en lo que se conoce como una clase de ventana . Esto describe información sobre una ventana que se compartirá entre las instancias de ella, como el icono, el cursor y otros. Una clase de ventana se identifica con un nombre de clase de ventana, que se proporciona en la variable global CLSNAME
en este ejemplo. El primer acto de WinMain
es completar la estructura de clase de la ventana, WNDCLASSEX wc
. Los miembros son:
- cbSize: el tamaño, en bytes, de la estructura
- estilo: los estilos de clase de ventana. Esto es 0 por ahora.
- lpfnWndProc: Este es uno de los campos más importantes. Almacena la dirección del procedimiento de ventana . El procedimiento de ventana es una función que maneja eventos para todas las ventanas que son instancias de esta clase de ventana.
- cbClsExtra: el número de bytes adicionales para asignar a la clase de ventana. Para la mayoría de las situaciones, este miembro es 0.
- cbWndExtra: el número de bytes adicionales para asignar para cada ventana individual. No confunda esto con
cbClsExtra
, que es común a todas las instancias. Esto es a menudo 0. - hInstance: El manejador de instancia. Basta con asignar el
hInst
argumento enWinMain
a este campo. - hIcon: El manejador de icono para la clase de ventana.
LoadIcon(NULL, IDI_APPLICATION)
carga el icono de la aplicación predeterminada. - hCursor: El controlador de cursor para la clase de ventana.
LoadCursor(NULL, IDC_ARROW)
carga el cursor predeterminado. - hbrBackground: Un identificador para el pincel de fondo.
GetStockObject (WHITE_BRUSH)
otorga un identificador a un pincel blanco. El valor de retorno se debe convertir porqueGetStockObject
devuelve un objeto genérico. - lpszMenuName: El nombre de recurso de la barra de menú a usar. Si no se necesita una barra de menú, este campo puede ser NULL.
- lpszClassName: el nombre de clase que identifica esta estructura de clase de ventana. En este ejemplo, la variable global
CLSNAME
almacena el nombre de la clase de ventana. - hIconSm: un identificador para el icono de clase pequeña.
Una vez que se inicializa esta estructura, se llama a la función RegisterClassEx
. Esto hace que la clase de ventana se registre con Windows, lo que se conoce en la aplicación. Devuelve 0 en caso de fallo.
Ahora que la clase de ventana se ha registrado, podemos mostrar la ventana utilizando CreateWindowEx
. Los argumentos son:
- stylesex: Los estilos de ventana extendida. El valor predeterminado es WS_EX_LEFT.
- clsname: el nombre de la clase
- cap: el título de la ventana, o subtítulo. En este caso, es el título que se muestra en la barra de título de una ventana.
- estilos: los estilos de ventana. Si desea crear una ventana de nivel superior (principal) como esta, la bandera para pasar es WS_OVERLAPPEDWINDOW.
- x: la coordenada x de la esquina superior izquierda de la ventana.
- y: la coordenada y de la esquina superior izquierda de la ventana
- cx: el ancho de la ventana
- cy: la altura de la ventana
- hwndParent: el identificador de la ventana principal. Como esta ventana es en sí misma una ventana principal, este argumento es NULL.
- hMenuOrID: si la ventana que se está creando es una ventana principal, este argumento es un identificador del menú de la ventana. No confunda esto con el menú de clase, que es
WNDCLASSEX::lpszClassName
. El menú de clase es común a todas las instancias de ventanas con el mismo nombre de clase. Este argumento, sin embargo, es específico solo para esta instancia. Si la ventana que se está creando es una ventana secundaria, este es el ID de la ventana secundaria. En este caso, estamos creando una ventana principal sin menú, por lo que se pasa NULL. - hInst: El manejador de la instancia de la aplicación.
- etc: La información adicional que se pasa al procedimiento de la ventana de la ventana. Si no se va a transmitir información adicional, pase NULL.
Si x
o y
o cx
o cy
es CW_USEDEFAULT
, entonces el valor de ese argumento será determinado por Windows. Eso es lo que se hace en este ejemplo.
CreateWindowEx
devuelve el identificador a la ventana recién creada. Si la creación de la ventana falló, devolvió NULL
.
Luego mostramos la ventana llamando a ShowWindow
. El primer argumento para esta función es el manejador de la ventana. El segundo argumento es el estilo de presentación, que indica cómo se mostrará la ventana. La mayoría de las aplicaciones simplemente pasan el argumento cmdshow
pasado en WinMain
. Una vez que se muestra la ventana, debe actualizarse mediante una llamada a UpdateWindow
. Hace que se envíe un mensaje de actualización a la ventana. Aprenderemos lo que esto significa en otro tutorial.
Ahora viene el corazón de la aplicación: La bomba de mensajes. Bombea los mensajes enviados a esta aplicación por el sistema operativo y envía los mensajes al procedimiento de la ventana. La llamada GetMessage
devuelve un valor distinto de cero hasta que la aplicación recibe un mensaje que lo hace salir, en cuyo caso devuelve 0. El único argumento que nos concierne es el puntero a una estructura de MSG
que se completará con información sobre el mensaje. Los otros argumentos son todos 0.
Dentro del bucle de mensajes, TranslateMessage
traduce los mensajes de clave virtual en mensajes de caracteres. El significado de esto, de nuevo, no es importante para nosotros. Toma un puntero a una estructura de MSG
. La llamada que lo sigue directamente, DispatchMessage
, envía el mensaje señalado por su argumento al procedimiento de la ventana de la ventana. Lo último que debe hacer WinMain
es devolver un código de estado. El miembro wParam
de la estructura MSG
contiene este valor de retorno, por lo que se devuelve.
Pero eso es solo para la función WinMain
. La otra función es winproc
, el procedimiento de ventana. Manejará los mensajes para la ventana que Windows le envía. La firma para winproc
es:
- hwnd: un identificador de la ventana cuyos mensajes se están procesando.
- wm: el identificador de mensaje de ventana
- wp: uno de los argumentos de información del mensaje. Esto depende del argumento
wm
- lp: Uno de los argumentos de información del mensaje. Esto depende del argumento
wm
. Este argumento se suele utilizar para transmitir punteros o manejadores.
En este programa simple, nosotros no manejamos ningún mensaje nosotros mismos. Pero eso no significa que Windows tampoco lo haga. Esta es la razón por la que se debe llamar a DefWindowProc
, que contiene el código de manejo de ventanas predeterminado. Esta función debe llamarse al final de cada procedimiento de ventana.
¿Qué es un mango?
Un identificador es un tipo de datos que representa un objeto único. Son punteros, pero a estructuras de datos secretas mantenidas por el sistema operativo. Los detalles de estas estructuras no necesitan preocuparnos. Todo lo que un usuario debe hacer es simplemente crear / recuperar un identificador utilizando una llamada a la API, y pasarlo a otras llamadas a la API que tomen ese tipo de identificador. El único tipo de identificador que utilizamos fue elHWND
devuelto por CreateWindowEx
. Constantes
En este ejemplo, encontramos un puñado de constantes, que están en mayúsculas y comienzan con un prefijo de 2 o 3 letras. (Los tipos de Windows también están en mayúsculas)- IDI_APPLICATION: El nombre del recurso que contiene el ícono de la aplicación predeterminada. Esto se usa con
LoadIcon
oLoadImage
(LoadIcon en este ejemplo). - IDC_ARROW: El nombre del recurso que monta el cursor de la aplicación predeterminada. Esto se usa con
LoadIcon
oLoadImage
(LoadIcon en este ejemplo). - WHITE_BRUSH: El nombre de un objeto de stock. Este objeto de stock es el pincel blanco.
- MB_ICONERROR: un indicador utilizado con
MessageBox
para mostrar un icono de error. - WS_EX_LEFT: El estilo de ventana extendida predeterminado. Esto hace que la ventana tenga propiedades alineadas a la izquierda.
- WS_OVERLAPPEDWINDOW: un estilo de ventana que indica que la ventana debe ser una ventana principal con una barra de título, un cuadro de tamaño y otros elementos típicos de las ventanas de nivel superior.
- CW_USEDEFAULT: se utiliza con
cy
argumentosx
,y
,cx
ocy
CreateWindowEx
. Hace que Windows elija un valor válido para el argumento por el que se pasóCW_USEDEFAULT
.
Tipos de Windows
Al programar para Windows, tendrá que acostumbrarse a los tipos de Win32, que son alias para los tipos incorporados. Estos tipos están en todas las tapas. Los tipos de alias utilizados en este programa son:- TCHAR: El tipo de carácter genérico. Si se define
UNICODE
, este es unwchar_t
. Otheriwse, es unchar
. - UINT: Un entero sin signo. Se utiliza para representar el identificador de mensaje en procedimientos de ventana y otros fines.
- WPARAM: En Win16, este era un argumento WORD (de ahí el prefijo
W
). Sin embargo, con la introducción de Win32, este es ahora unUINT_PTR
. Esto ilustra el punto de estos alias de Windows; Ellos están ahí para proteger a los programas del cambio. - LPARAM: Este es un argumento
LONG
(LONG_PTR
en Win64). - PTSTR: La
P
significa puntero. LaT
significa carácter genérico y laSTR
significa cadena. Por lo tanto, esto es un puntero a una cadenaTCHAR
. Otros tipos de cadena incluyen:- LPTSTR: igual que
PTSTR
- LPCTSTR: Significa
const TCHAR *
- PCTSTR: Igual que
LPCTSTR
- LPWSTR: cadena ancha (
wchar_t *
) - LPCWSTR: Significa
const wchar_t *
- PWSTR: Igual que
LPWSTR
- y mucho más Como puede ver, los tipos de Win32 pueden ser difíciles de entender, especialmente con tantos tipos, que es un artefacto de Win16.
- LPTSTR: igual que
- LRESULT: este tipo se utiliza para representar el valor de retorno de los procedimientos de ventana. Suele ser un LARGO (de ahí la
L
).