0 ratings0% found this document useful (0 votes) 202 views66 pagesTutorial WinAPI
Tutorial de la API de Windows en c++
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content,
claim it here.
Available Formats
Download as PDF or read online on Scribd
saan “at ancn
Introducci6n
éSobre qué trata este tutorial?
Este tutorial esté destinado a presentarte las bases (y algunos extras comunes) de escribir programas utilizando la
API de WIN32. El Ineguaje usado es C, y generalmente compilaran bien la mayoria de los compiladores de C++.
Ademés, mucha de [a informacién es plicable a cualquier lenguaje que pueda acceder a Ia API, incluyenco Java,
‘Assembler y Visual Basic. Sin embargo no voy a presentar niglin cédigo relacionado a estos lenguajes y queda a
cargo del lector si desea hacerlo, de hecho, varias personas han usedo previamente este tutorial en otros lenguajes
con algo de éxito.
Este tutorial no pretende ensefiar el lenguaje C++ ni tampoco céme usar algin compilador en particular (Borland
CH, Visual C++, BCC-Win32, etc...). Sin embargo me torraré un momento en el apéndice para dar algunas notas
sobre como usar los cormpiladores que conozco.
Si no sabes que es una macro o que que es typedef 0 sino sabes cémo funciona la sentecia switch (}, entonces
te recomiendo que primero leas un buen libro o tutorial sobre el lenguaje C.
Notas Importantes:
A.veces 2 lo largo del texto voy a indicar ciertas cosas que son IMPORTANTES de leer, por lo tanto debes prestarle
atencién. La primer nota es
iLos fuentes incluides en el archivo zip no son opcionales! No he incluido todo el cédigo en el tutorial, solo
aquel que es relevante para lo que estoy explicando en ese momento, Para ver como este cédigo se integra con el
resto del programa debes mirar el codigo fuente incluido en el archivo zip.
Y segundo:
Leelo todo! Si tienes alguna pregunta durante una seccién del tutorial, solo ten un poco de paciencia ya que esta
podria ser respondida en el resto del ris.
tra cosa para recordar es que si tienes alguna pregunta sobre un tema A ésta podria finalmente ser respondida en
una discusién de B o C 0 quizds D. Por lo tanto si tienes alguna duda sigue leyendo el tutorial
Ok, Pienso que esto son todos los consejos que tengo que dar por el momento, Adelante...
Un Simple programa Win32
Si eres un completo principiante varros a aseguramos que eres capaz de compilar una aplicacién basica de
windows. Copia el siguiente cédigo a tu corrpilador y si todo va bien obtendrés uno de los prograrmas mas pobres
jams escritos
Recuerda compllar esto come c, no comma c-+. Esto probablemente no importe pero debido a que todo el cédigo es
C puro tiene sentido comenzar por el carrino correcto. En muchos casos todo lo necesario es simplemente poner el
cédigo en un archivo con extensién .c en vez de .cpp. Si todo esto te marea simplemente guarda el archivo con el
nombre test.c y listo.
Hinclude
inl WINAPI Winkain (INSTANCE hinst
1 imdshow)
evinstance,
R lpcmdLine, int n
BessageBox (NULL, "Goodbye, cruel wor:
"Note", MB_OK);
Si esto no funciona, tu primer paso es leer que errores obtuviste y si no los entiendes buscalos en la ayuda o en
algin documento que acompafie tu corpilador. Asegdrate de haber especificado "GUI Win32" y no "Console"
dentro de la opcién project/compile/target de tu compilador. Desafortunadamente no puedo ayudarte mucho
con esta parte ya que los errores y cémo corrtegitios varian de compilador en compilador (y de persona en
persona).
Quizés obtengas algunos "Warnings" diciéndote que no has usado los parémetros provistos en wisain( }, pero
esto est bien. Ahora que sabermos que puedes compilar un programa veamos esta pequefia porcién de cédigo:
hapaivingrog org cries hal 12ssra2016 “Tuer: eroduccion
ial WINAPT WinMain(BINSTANCE hinslance, HINSTANCE BPrevinstance, =!
int acedshow'
WinMain( ) es en Windows el equivalente al main ( ) de DOS o UNIX. Es ahi donde los programas comrienzan su
ejecucién. Los parémetros son los siguientes:
HINSTANCE bins:
Handle al médulo ejecutable del programa (el archivo .exe en memoria).
HINSTANCE hPrevinstance
Siempre nulo (0 wuz) en programas Win32.
TR IpCndLine
Un string (0 cadena de caracteres) con los argumentos de linea de comando. No incluye el nombre del
programa.
nt. ncndshow
Un valor entero que puede ser pasado a ShowWindow( ), veremos esto mas adelante.
instance es usado para cosas como cargar recursos y cualquier otra tarea que sea realizada en una base por-
modulos. Un médulo puede ser el archivo .EXE cargado en memoria o una libreria DLL cargada en tu programa. En la
mayor parte de este tutorial (por no decir todo el tutorial) habré un solo médulo del cual preocuparse: el EXE,
hPzevinstance es usado en Win16 come handle a Ia instancia de ejecucién anterior (sl la hubo). Esto ya no se
aplica en Win32 por lo tanto ignorarros este parémetro,
Especificaciones de Ilamada:
WINAPI especifica una convencién de llamada y es definida como stdcall. No te preocupes si no sabes que sigifica
esto ya que no nos afectaré dentro del alcance de este tutorial. Solo recuérdalo,
Tipos de datos en Win32
Seguramente has encontrado que muchos de los tipos de datos tienen definiciones especiticas de
windows, 7=NT para unsigned int (entero no signado) £=STR para char*, ete...lo que elijas depende de ti, Site
sientes mas cémodo utilizando chaz* en lugar de L278 eres libre de hacerlo. Solo asegirate bien de que tipo de
dato se trata antes de substituir algo,
Sélo recuerda algunas cosas y luego ser facil de interpretar, El prefijo Lerepresenta Long Pointer (Puntero Largo).
En Win32 la parte larga es obsoleta por lo tanto no te preocupes por ella. Si no conoces que es un puntero puedes
hacer lo siguiente: 1) Buscar un tutorial de C, 0 2) Seguir adelante y saltearte un parte, Realmente recorrienco la
primer opcién, aunque muchas personas elijen la segunda... Después no digas que no te avisé,
La letra c que sigue luego de LP indica que se trata de un puntero "Constante", Por jo tanto 8 representa
"Long Pointer to Const String” a "Puntero Largo un String Constante" pero carro ya dijimas, en Win32 poderros
ignorar la parte “long” asi que esto podria leerse "Puntero a un String Constante", uno que no se puede modificar.
Por otro lado i2s72 no es constante y puede ser modificado.
Quizas has visto una "T" ahi en medio. No te preacupes por esto por ahora, a menos que intencionalmente estés
trabajando con UNICODE, no significa nada.
hapaivingrog org cries hal
2Rssra2016 “Tutoral Una Simple Versa
Una Simple Ventana.
Ejermplo: simple window
A veces las personas ingresan al IRC y preguntan "Como hago una
ventana...?". Bien, no es tan simple como parece. No es dificil una vez
que sepas lo que estas haciendo, pero hay algunas cosas que necesitas
hacer para poder crear un ventana. Y son mas de las que pueden ser
explicadas en una sala de chat 0 en una nota répida.
Siempre me gusté hacer las cosas primero y aprenderlas luego... por lo
tanto aqui esté el cédigo de una simple ventana que ser explicado en
breve.
#include
const char g sClassName[] = "myWindowclass";
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam,
‘
switch (msg)
(
case WM_CLO
Destroydindow (hwnd) ;
break;
case WM_DESTAOY:
PostguitMessage (0);
break:
default:
return DefWindowProc (hwnd, msg, wParam, 1Param);
>
return 0;
int WINAPI WinMain(HINSTANCZ hinstance, HINSTANCE herevingtance,
LPSTR lpCndLine, int nCndshow)
WNDCLASSEX we;
AND bunds
MSG Msgz
//Step 1: Registering the Window Class
we. cbsize = sizeof (WNDCLASSEX)
we-style = 0;
wc.lptntinéProc = wndProcs
we. ecbC1SFxtra, 0,
nc.cblindExtra = 0;
We hinstance Binstance;
we.hreon, Leadicon (NULL, IDI_APPLICATION) ;
We hCursoz = LoadCursox (NULL, 1DC_ARROW) ;
uuc.hbrBackground = (HBRUSH) (COLOR_WINDOW=1);
we. lpszMenuName = NULT;
we-IpszClassName = ¢_s2ClassNane;
we.hreonsm HGadicon (NULL, IDI_APPLICATION) ;
AE (!ReqisterClasakx (swe) )
‘
MessageBox (NULL, "Window Registration Failed!", "Error! ™,
MB_ICONEXCLAMATION | MB_OK);
return 0;
>
// Step 2: Creating the Window
nund = CreateWindowrx (
hipaivingrog.orgfutrialesisimple_ window!
LPARAM 1Param)
15ssra2016
WS_BX_CLIENTSDGE,
9.
"The title of my window",
WS_OVERLAPPEDWINDOW,
C_USEDEFAULT, Ci
NOME, NU
assNane,
| bInstance, NULL)?
£ (hwnd == NU
MessageBox (N
MB_ICONEXCLAMATION
owWin
(hwnd, ncnds
0 show) 5
updateWindow (hwnd) ;
// step
hile (Gi
Pra asa) 5
Dispatch¥essage (6Msg) ;
return Msg.wearam:
USEDEFAULT, 240, 120,
“Tutoral Una Simple Versa
Generalmente este es el programma mis simple que puedes escribir par crear una ventana funcional, cigamos
unas 70 ineas de cédigo. Si has corrpilado el primer ejermplo, entonces este deberia funcionar sin problemas.
Paso 1: Registrar la Clase Ventana
Una Clase Ventana almacena informacién sobre el tipo de ventana, incluyendo su Window Procedure, los
iconos grandes y pequefios y el color de fondo. De esta forma podemos registrar una clase una vez y luego
crear muchas ventanas a partir de ésta, sin tener que especificar todos los atributos una y otra vez.
Muchos de los atributos que especificamos en Ia Clase Ventana pueden ser cambiados, si se desea, en una
base per-windows.
La Clase Ventana no tiene nada que ver con las clases de C++.
© g_s2ClassName[] = "nyWindowClase";
La variable anterior almacena el norrbre de nuestra Clase Ventana y la usarermps en breve para registrar
nuestra clase en el sisterra
WNDCLASSEX wer
chSize ~ sizeof (WNDCLASSEX) +
pénindPro.
we-nInstance = hinstance;
we.nIcon ~ LoadIcon (NULL, IDT_APPLICATION) ;
we. nCursor adCurser (NULL, TDC_ARROW) ;
we.hbzBackground = (HBRU
zMenuName = NULL;
we. Ips2ClassName ~g s2ClassNane,
We.AlconSs, = Loadicon (NULL, LD1_APPLICATLON) ;
fH) (COLOR_WINDOW+) ¢
tRegisterClassix (swc) }
MessageBox (NULL, "Window Registration Failed
retura 0;
hipaivingrog.orgfutrialesisimple_ window!
“Eeror!
Ma_ICONEXCLAMATION | MB_OK);
28ssr22016 Tutorial: Ura Single Ventana
Este es el cédigo que usamos en wintain( ) para registrar nuestra Clase Ventana en el sistema. Para ello,
rellenamos los campos de una estructurawiaczascrx y llamamos a RegisterClassix( ). Los campos de la
estructura son los siguientes
El tamafio de la estructura
style
El estilo de la clase (CS *), no confundirse con el estilo de la ventana (WS *). Generalmente este
campo puede ser puesto en cero.
Iptniind2zoc
Puntero al Window Procedure de esta clase.
Cantidad extra de asignacién de memoria para datos de la clase. Generalmente cero.
ebandext
Cantidad extra de asignacién de memoria por ventana de este tipo. Generalmente cero.
Instance
Handle a la instancia de la aplicacién (la que obtenerms en el primer parémetro de WinMain( ) )
hTeon
Handle al icono grande (32x32), mostrado cuando el usuario presiona Alt+Tab.
Handle al cursor que sera mostrado sobre la ventana,
hbrBackgzound
Pincel para fijar el color de fondo de nuestra ventana
ApszMenuName
Nombre del recurso Meni para usar con las ventanas de esta clase.
IpszClassNane
Nombre para identificar la clase.
sm
Handle al icono pequefio (16x16), usado en la barra de tareas y en la esquina superior izquierda de la
ventana.
No te preocupes si esto no queda muy claro atin, las distintas partes que vimos serén explicadas mas
adelante. Otra cosa para recordar es no intentar memorizar toda esta estructura. Yo raramente (de hecho
nunca) memorizo estructuras o parémetros de funciones, esto es un desperdicio de tiempo y esfuerzo. Si
conoces las funciones que necesitas llamar, entoces es una cuestién de segundos mirar los pardmetros
exactos en la ayuda de tu compilador. Si no tienes los archivos de ayuda entonces consigue algunos porque
sin ellos estas perdido, Eventualmente irés aprendiendo los parémetros de las funciones que mas usamos.
Luego llamamos a RegisterClassEx( ) y chequeamos por alguna falla, si hubo alguna mostramos un mensaje y
abortamos el programa retorando a la funcién WinMain( ).
Paso 2: Crear la Ventana
Una vez que la clase ha sido registrada, podemos usarla para crear una ventana. Observernos los parémetros
de la funcién Createwindowsx( }(como lo haremos con cada nueva llarrada a la API que vearmos), los
explicaré brevemente:
HWND hwnd:
CreateWindowkx
FX_CLTENTEDGE,
2ClasaNians,
The title of my window",
VERLAP2EDWINDOW,
CH_USEDEFAUL?, C_USEDEFAULT, 2
NULL, NULL, hinstance, NULL);
El primer parametro (vis_ex_CLIENvEDCE) es el estilo extendido de la ventana, en este caso he configurado
éste para darfe un borde interno y hundido al marco de la ventana. Pon este valor en cero para observar la
diferencia, 0 prueba con otros valores para ver que hacen,
A continuaciém tenemos el nombre de la clase (3_szClassNane), éste le dice al sistema que tipo de ventana
crear. Debido a que querermos crear una ventana a partir de la clase que hemos registrado, usamos el
nombre de dicha clase. Luego especificamos el nombre de nuestra ventana o titulo que apareceré en
la barra de titulo de la ventana,
hipaivingrog.orgfutrialesisimple_ window! 38ssr22016 Tutorial: Ura Single Ventana
El pardmetro que tenemos como ws_ovERLAPPEDWINDOM es estilo de la ventana. Hay algunos de éstos y
deberias mirarlos y experimentar para observar que hacen. De todos modos serén cubietos ras adelante.
Los siguientes cuatro pardmetros ( cii_USHDEFAULI, CW_USEDEFAULY, 320,240) son las coordenadas X ¢ Y
de la esquina superior izquierda de la ventana y el alto y ancho de de la ventana, respectivamente. He
puesto los campos X e Y con el valor cw_USEORFALTpara dejar que windows ellja en qué lugar de la pantalla
ubicar la ventana. Recuerda que la parte izquierda de la pantalla tiene un valor de X igual a cero y se
incrementa hacia la derecha y la parte superior de la pantalla tiene un valor cero de Y y se incrementa
cuando avanzamos hacia abajo en la pantalla. Las unidades son los pixeles, el cual es la minima unidad que
una pantalla puede mpstrar en una resolucién determinada.
Los siguientes parémetros (Ut, YUL, ¢_AInstance, NULL) Corresponden al handle de la ventana padre, al
handle del meni, al handle de la instancia de la aplicacién y un puntero a los datoscon los cuales se cred la
ventana. En windows las ventanas son ordenadas en la pantalla de acuerdo a una jerarquia de padre e hijo.
Cuando ves un botdn en una ventana, el botén es el Hijo de la ventana en la cual est contenido y dicha
ventana es suPadre. En este ejemplo, el handle al padre es nulo debido a que no tenemos padre, estamos en
el nivel principal de las ventanas. I handle al mend es nulo debido a que, por ahora, no tenemos ment El
handle a la instancia de la aplicacién es puesto con el valor que es pasado en el primer parémetro
del winiain( ). Los datos de creacién (los culaes nunca uso) pueden ser usados para enviar informacién
adicional a la ventana que esté siendo creada. También es nulo, Si estas preguntandote que es la palabra
magicaut.t,, es simplemente definida como cero (0). En realidad, en C esta definida como ((void *) 0},
dedido a que esté destinada a ser usada con punteros. Por lo tanto probablemente obtengas algunos
“warnings” si la utilizas para valores enteros, de todas maneras esto depende del cormpilador y del nivel de
warnings en la configuaracién del mismo, Puedes elegir ignorar los wamings o usar el valor cero (o)en lugar
de NULL.
La principal causa de que las personas no conozcan cuales son las fallas de sus programas es que no
chequean los valores de retorno de las llamadas a funciones para determinar si éstas han fallado 0
No, CreateWindow ( ) puede fallar atin si eres un experimentado programador debido a que hay una gran
cantidad de errores que son faciles de cometer. Hasta que aprendas como identificar répidamente dichos
errores date la oportunidad de resolvertos y siempre chequea los valores de retorno!
if (hwnd
NULL)
“Window Creation Fa,
MB_OK);
ed)", "Ez:
Después que hemos creado la ventana y hemos chequeado para aseguramos de que tengamos un handle
vélido, mostramos le ventana utlizando el Ultimo pardmetro provisto en Wintain( 1 y luego lo actualizamos
para aseguramos que se halla redibujado en la pantalla a si mismo de manera apropiada.
Showitindow (hwné, ncmdshow) +
UpdateWindow (hwnd) ¢
El pardmetro acnashow es opcional, simplemente puedes pasar el valorsi_sHowNoRMAL en todo momento
Isto, Sin embargo si usamos el parémetro provisto en Wintiain( ) le da a quien esté corriendo tu programa
la oportunidad de especificar si quiere que la ventana corrienze visible, maximizada, minimizada, etc...
Encontrarés opciones para este parémetro en las propiedades de los iconos del escritorio y apartir de estos
valores seré determinado el valor que tome el pardmetro nCmdShow.
Paso 3: El Bucle de Mensajes
Este es el corazén del programa, la mayoria de las cosas que tu programa realiza pasan a través de este
punto de control
waile (GetMessage(EMsq, NULL, 0, 0) > 0)
(
‘Trans lazeMessage (6Msg) +
DispatchMessage (aMsg) 7
GetMessage () obtiene un mensaje de la cola de mensajes de tu aplicacién. Cada vez que el usuario mueve
hntpavingrog.orgfucriatesIsimple_ window! 45ssri22016 “Tutorial: Una Simple Ventana
el mouse, presiona el teclado, hace click en el menti de la ventana , etc... el sistema genera mensajes que
ingresan en la cola de mensaje de la tu aplicacién. Utilizando Getwessace( } estas solicitando que el
siguiente mensaje disponible en la cola sea removido de la misma y te sea entregado para procesarlo, Si no
hay mensajes,cetmessage( ) se bloquea . Si no ests fariliarizado con dicho término, esto significa que
espera a que halla un mensaje en la cola y luego te lo retorma.
TranslateNessage( ) hace algunos procesos adicionales en los eventos del teclado, como generar
mensajes wi_CHAR que van junto a los mensajes KEvDONN. Finalmente DispatchMessage( ) envia el
mensaje a la ventana que habia recibido dicho mensaje. Esta podria ser nuestra ventana principal, otra
ventana, 0 un control e incluso una ventana que fué creada "detras de la escena” por el sistema o algun
otro progama. Esto no es algo que deberia precocuparte debido a que lo Unico que nos interesa saber es que
obtenemos el mensaje y lo enviamos, el sisterra se encarga de hacer el resto asegurandose de trabajar con
la ventana correcta.
Paso 4: El Windows Procedure
Si el bucle de mensajes es el corazén del programa entonces el Windows Procedure es el cerebro, Aqui es
donde todos los mensajes son enviados para que sean procesados por nuestra ventana.
LRESULT CALLBACK WindProc(HWND hwnd, UINT meg, WPARAM wParam, LPARAM LP:
i
switeh (sg)
i
case M_CLOSE:
DeszroyWindow (hwnd) 7
ks
case W_DESTROY:
PostguitMessage (0)
break;
default:
return Deft
dowProc(nwnd, msg, wParam, lParam);
}
return 0;
El Windows Procedure (pronunciado "winprock") es llamado por cada mensaje que procesarros, el
parémetro un es el handle dela ventana a la cual se aplica el mensaje. Esto es importante debido a que
puedes tener dos o mas ventanas de la misma clase que usarén el mismo Windows Procedure (tind?roc( ) ),
la diferencia es que el parémetro hitna sera diferente dependiedno de la ventana. Por ejemplo cuando
obtenemos el mensaje Wi! CLOSE (cerrar ventana) destruimos la ventana y debido @ que usamws el handle de
la ventana (que recibimos en el primer parémetro) sabremos que ventana es la que hay que destruir. Las
demés ventanas no se verdn afectadas, solo la que recibié el mensaje.
ose es enviado cuando el usuario presiona ALT+F4 0 cuando presiona el botén Xi. Esto causard que la
ventana sea destruida por default, pero prefiero manejarlo explicitamente debido a que es el punto perfecto
Para realizar “limpiezas" o preguntarle al usuario si desea guardar los cambios, etc...antes que la ventana se
destruida. (‘.¢ el programe finalice)
Cuando llamamos a Destroyivindow( ) el sistema envia el mensajents_pssrRoy a la ventana que va a ser
destruida, que en este caso es nuestra ventana, y por lo tanto destruimos todas las ventanas hijas antes de
remover nuestra ventana del sistema. Debido a que esta es nuestra Unica ventana en nuestro programa
todo lo que hacemos es salir del rrismo, para ello utiizamos la llamada Post Quitttessace( }. Esta envia el
mensajeni_oU1” al bucle de mensajes pero nunca recibimos este mensaje debido a que éste causa
que cetwessage( ) retome FAISE y como vimos en el cédigo del Bucle de Mensajes, cuando esto sucede,
dejamos de procesar mensajes y retornamos el cédigo resultante final, el visaram del wt_Qur? el cual es el
valor pasado en Postouitmessage( ). El valor de retomo solo es usado si tu programa est disefiado para
ser llamado por otro prograrma y quieres que retorne un valor especifico.
Paso 5: No hay paso 5.
Bien, eso eso fué todo!(por ahora). Si las cosas atin no se han entendido del todo, solo sigue adelante y
espera que las cosas se tornen mas claras, como cuando vearmos algunos programes,
hipaivingrog.orgfutrialesisimple_ window! 58ssra2016 “Tutoral- Win: Mango de Mensajes
[ contenidos | #winprog ]
Manejo de Mensajes
Ejemplo: window click
Bien, tenemos una ventana pero esta no hace
nada excepto lo que DefWindowProc( ) le
permite que haga: cambiar el tamafio, ser
maximzada, minimizada etc... Nada realmente
excitante,
G) :\Codeutoratwindow_clck)Debuslwindow_cck.ewe
En la siguiente seccién voy a mostrar como
modificar lo que ya tenemos para que haga algo
nuevo. Asi, yo solo podria decirte "Procesa este
mensaje y haz esto en él..."y tu sabras lo que
digo y serds capaz de hacerlo sin mirer un ejemplo
entero, Esto es lo que espero, de todas maneras presta atencién.
Ok, para comenzar tomemos el ejemplo de |a Ultima ventana en la que trabajamos y aseguréronos que
compila y se ejecuta como lo esperamos. Puedes seguir trabajando en este archivo 0 puedes copiar el
cédigo a un nuevo proyecto.
Vamos a agregarle a la ventana la capacidad de mostarle al usuario cual es el nombre del programa. ESto
se hard cada vez que el usuario realice un click sobre la ventana. Nada excitante, es basicamente para ir
entendiendo como manejar mensajes. Observernos que tenemos en nuestro vindProc()
LRESULT CALLBACK Wad?roc (HAND hwad, UINT msg, WPARAM wParam, LPARAY
cam)
switch (nsg
case WY_CLOSE:
best royWi:
case WY_DESTROY
dow (hwnd) 5
defaule:
return DefWindowPr:
Si queremos manipular clicks del mouse, necesitamos agregar un menejador (0 procesador)
‘ay_1RUTTONDOWN (0 wM_RAUTTONDONN, MBUTTONDOWN para clicks con el botén derecho y medio,
respectivarente). Cuando decimos "procesar un mensaje" significa agregar dicho mensaje dentro del
winProc( } de nuestra clase ventana, lo hacemos de la siguiente manera
RESULT CALLBACK WnePro
a
D hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
switch (sa)
1
agregamos esto
se WY_CLo:
Des:
case WY_DES’
DefWindowProc(hwad, msg, wParam, 1Param) ;
hnapaivingrog org riaVesivindow elicind 1ssra2016 “Tutoral- Win: Mango de Mensajes
El orden en cual manejamos los mensajes raramente importa, Solo asegirate de poner el "break" despues
de cada uno. Como puedes ver necesitamos otro case dentro de nuestro switch (). Ahora queremos que
algo suceda cuando llegamos a esta nueva parte de nuestro programa.
Primero presentaré el cédigo que queremos agregar (el que le mostrar al usuario el nombre del archivo del
programa) y luego lo integraré dentro del nuestro programa, Mas adelante, probablemente solo te muestre
el cédigo y te dejaré que lo integres tu mismo. Esto es, por su puesto, mejor para mi porque no tengo que
andar tipeando demasiado y mejor para vos porque serds capaz de agregar el cddigo dentro de CUALQUIER
programa, no solamente el que estamos presentando. De todas maneras, sino estas segura de como
hacerlo puedes mirar el cédigo fuente correspondiente a esta seccién incluido en el arct
Getmo: LeNane (
MessageBox (hwnd, szFileName,
OK | MB_ICONINFORMATION) +
Este cédigo no puede ser copiado en cualquier lugar dentro de nuestro programa. Especificamente
queremos que éste se ejecute cuando el usuario clickea el mouse, por lo tanto veremos como poner dicho
cédigo dentro de nuestro programa:
RESULT CALLBACK WndProc(HNND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
swit:
case WY_LBUTTONDOWN:
// Comienzo del nuevo cédigo
‘
s2PLleName [MAX PATH];
INSTANCE Ainstance = Ge:
ciuleHandle (NULL) ;
GetModuleFileName(hiastance, sz¥ileName, MAX_PATA);
leName, "this progz: 7) MB_OK | MB,
MessageHox (hwnd, 92 NINFORMAY ION) 5
return DefWindowProc(hwnd, msg, wParam,
Observa el nuevo conjunto de llaves { }. Estas son necesarias cuando declaramos una variable dentro de
una sentencia switch ( }. Esto es conocimiento basico de C pero pienso que es bueno recordarlo para
aquellos que estén haciendo las cosas por el camino dificil.
Una vez que has agregado el cédigo, cormpilalo. Si funciona, has click dentro de la ventana y veras
desplegado un cuadro con el nombre cel archivo .exe
Habrés notado que hemos agregado dos variables, ninstance y sz#ileName. Observa la funcién
GetModulerilename y veras que el primer pardmetro es un HINSTANCE refiriéndose al médulo ejecutable
(nuestro programa,el archivo .exe), Donde obtenemos eso? GetHoculeHandie( ) es la respuesta. Las
referencias de GetMioduieiandie ( ) indican que si pasamos NULL como parametro, nos retornaré un
“Handle al archivo usado para crear el proceso de llamada" que es exactamente lo que necesitarros: el
HINSTANCE anteriormente mencionado. Poniendo todo esto, finalizamos con la siguiente declaracién:
INSTANCE hInstance = GetModuleHandle (NULL) 7
Ahora, con el segundo parémetro, nuevamente usamos nuestro confiable manual de referencia. Este dice
“un puntero al buffer que recibe la ruta y el nombre de archivo del modulo especificado" y el tipo de dato
hnapaivingrog.orgriaVlsivindow elichind 218ssri216 “Teoral- Win: Mango ce Mensa
es Le7sia (0 ner si tu referencia es vieja). Debido a que ues es equivalente a char* podermos
declarar un arregio de caracteres como el siguiente:
char s2¥ileNane(MAX_PATH];
MaX_PAT% es una macro incluida via definida como la maxima longitud de un buffer necesario
para almacenar el nombre de un archivo bajo Win32, Pasamos max pati: a la funcién GetModuleFileNane (
) para que ésta conozca el tarrefio del buffer.
Luego de que GetwodulerileName ( ) es llamado, el biffer s2?ilewane seré llenado con un string
terminado en cero conteniendo el nombre de nuestro archivo exe, Pasamos este valor a MessageBox ()
porque es una forma facil de mostrarlo en pantalla.
Por lo tanto, si has agregado esto en el cédigo, compilalo. Si funciona haz click en la ventana y deberds
ver desplegado un mensaje con el nombre del archivo,
Si esto no funciona, aqui este el cédigo completo. Compsralo con el tuyo pare encontrar que errores has
cometido,
#include
const char g_szClassName[] = "myWindowClass";
LRESULT CALLBACK WrdProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM 1Param)
‘
awitch (meg)
‘
case WM_LBUTTONDOW!
(
char s2FileName (MAX PATH];
EINSEANCE hinstance = GetModulellandle (NULL) +
GatwoduleFileName(hinstance, s2FileName, MAX_PATH);
MessageBox (hwad, s2FileName, "This program is:", MB_OX | MB_ICONINFORMATION);
,
break;
ease WM CLOSE:
DestroyWindow (hwnd) ;
break:
case WM_DESTROY:
PostQuitMessage (0);
break,
default:
return DefWindow?roc (hwnd, msg, wParam, 1Param);
:
return 0;
int WINAPT WinMain(HINSTANCZ AInstance, HINSTANCE hPrevInstance,
LPSTR LpCmaLine, int aCmdShow)
WNDCLASSEX wes
UND hwad:
MSG Msg;
we.chSize sizeof (WNDCLASSEX) 5
we-style 0;
we-lpfnlindProc = WndProcs
we-ebClsixtra 0;
we. cbtindzxtra 0;
we hingtance
we. hEcon
hInstance;
Loadtcon (NULL, IDT_APPLICATION) ;
we hCursor LoadCursor (NJLL, TDC_ARROW);
we.hbrBackground = (BBRUSH) (COLOR_WINDOW+1) :
wo. lpszMenuName = NULL?
Hnapaivingrog.orgriaVesivindow elichind 3ssra2016 Tutoral- Win82: Mango de Mensajes:
we, lpszClassName
we .hEconsm
g_s2ClassNam
Leadtcon (NULL, IDT_APPLICATION) ;
if (!RegisterClassEx (awe) }
‘
MessageBox (NULL, "Window Registration Faile:
MB_ICONEXCLAMATION | MB_OK);
return 0;
) "Error!",
}
hwnd = CreateWindowsx (
WS_EX_CLIENTEDGE,
g_szClassName,
"The title of my window",
Ws_OVERLAPPEDWINDOM,
CW_USEDBFAULT, Cl_USEDEFAULY, 240, 120,
NULL, NULL, binstance, NULL);
if (hwnd == NOLL)
i
MessageBox (NULL, "Window Creation Failed!", "Eeror!",
MB_ICONEXCLAMATION | MB_OK) ;
return 0;
,
Showilindow (hwnd, nCndShow) ;
UpdateWindow (hwnd) +
while (GetMess:
‘
e(aMisg, NULL, 0, 0) > 0)
TranslateMessage (aMsg) +
DispatchMessage (aMsq)
'
return Msg.wPazam;
Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.
Version en Espaf
‘ederico Pizarro - 2003.
hnapaivingrog org riaVesivindow elicind 44ssra2016 “Tutorial - Win32: Corprerder ol Bucle de Mens
[ contenidos | #winprog ]
Comprender el Bucle de Mensajes
Entender el Bucle de Mensajes y la estructura de envio de mensajes es escencial para escribir
hasta el programa mas trivial. Ahora que hemos visto un poco sobre manejo de mensajes
deberiamos mirar en profundidad todo este proceso porque las cosas se tornaran mas confusas si
no entendemos por qué las cosas suceden de la forma en que lo hacen.
Que es un Mensaje?
Un mensaje es un valor entero. Si observas en tus archivos de cabecera (lo cual es una buena
practica cuando investigamos el funcionamiento de las API's) puedes encontrar cosas como esta
#define WM_INITDIALOG 0x0110
define WM_COMMAND x11
fine WM_LBUTTONDOWN 0x020
-¥ asi siguiendo. Los mensajes son usados para comunicar la mayoria de las cosas en windows, al
menos en los niveles basicos. Si quieres que una ventana o control (el cual es una ventana
especializada) haga algo, debes enviarle un mensaje. Si otra ventana quiere que vos hagas algo,
entoces te envia un mensaje. Si ocurre un evento, como cuando el usuario mueve el mouse,
presiona el teclado, etc... entonces el sistema la envia un mensaje a la ventana afectada. Dicha
ventana recibe el mensaje y acttia adecuadamente.
Cada mensaje en windows puede tener hasta dos parametros, wparam y lparam. Originalmente
param tenia 16 bits y iparam tenia 32 bits, pero en Win32 ambos son de 32 bits. No todos los
mensajes usan estos pardmetros y cada mensaje los usa de manera diferente. Por ejemplo, el
mensaje wM_CLOSE no usa ninguno de los dos y por lo tanto deberias ignorarlos. El mensaje
WM_CONMAND usa ambos, wparam contiene dos valores 1i7WORD (wparam) es la notificacién del
mensaje (si se aplica) y LOWORD(wparem) es el ID del control o menti que envid el mensaje.
Iparam es el HAND (Windows Handle) del contro! que envid el mensaje o nulo (NULL) si el mensaje
no proviene de un control.
HTWORD( ) y LOWORD( ) Son macros definidas por windows que simplemente retoman la palabra
superior (High Word) y la palabra inferior (Low Word), respectivamente, de un valor de 32 bits. En
Win32 una palabra es un valor de 16 bits, haciendo un wore (palabra doble) un valor de 32 bits.
Para enviar un mensaje puedes usar PostMessage( ) 0 SendMessage( ), PostMessage( ) pone
el mensaje en la Cola de Mensajes y retorna inmediatamente. Esto significa que una vez que la
llamada a Posttiessage( ) se completa el mensaje puede o no puede haber sido procesado atin.
Senckessace( ) envia el mensaje directamente a windows y no retoma hasta que windows haya
finalizado de procesarlo. Si quisierarnos cerrar una ventana, podriamos enviarle a un mensaje
WM_CLOSE de la siguiente manera: PostMessage (h¥ind, N¥_CLOSE, 0,0). Lo cual podria tener el
mismo efecto que hacer click en el boton cerrar de la ventana. Observa que wparam y Iparan son
ambos 0. Esto es debido a que, como mencionamos, no se usan con el mensaje iIM_CLOsE.
Didlogos
Una vez que comiences a usar cajas de didlogo necesitarés enviar mensajes a los controles para
poder communicarte con ellos. Puedes hacer esto primero através de Getbig=tem( ) (utilizando el
ID como pardmetro) para obtener el handle al control y luego usar sendMessage( ). 0 puedes
usar SendDigitemMsg( }, el cual combina ambos pasos. Como pardmetros utilizamos el handle de
la ventana y un ID de un hijo y SencDigTtenMsg( ) retoma el handle del hijo y le envia el
mensaje. Ambas formas de enviar mensajes funcionan bien en todas las ventanas, no solamente
en cajas de didlogo.
hrapaivingrog.orgfutcrialesimessage oop ht 1ss22016 “uri = Wr Comprar Buse Mensajes
éQue es la Cola de Mensajes?
Digamos que te encuentras ocupado manejando el mensaje i™_PAINT y repentinamente el usuario
ingresa un gran cantidad de datos por el teclado. Que deberia suceder?. éDeberias interrumpir el
procesamiento de w_parnr (dejar de dibujar la pantalla) para atender el teclado, o deberias
simplemente descartar lo que el usuario ingresé por el teclado?. Error!, obviamente ninguna de
éstas opciones son rasonables. Para esto tenemos la Cola de Mensajes. Cada vez que se recibe
un mensaje, dicho mensaje es encolado en la cola de mensajes y cada vez que se quiere procesar
un mensaje dicho mensaje es removido de la cola (valga la redundancia), Esto asegura que no se
pierdan mensajes, si estas procesando uno, entonces los otros seran encolados hasta que los
recuperes de la cola.
éQue es el Bucle de Mensajes?
while(GetMessage(4Msg, NULL, 0, 0) > 0)
{
‘vranslateMessage (&Ms
DispatchMessage (aMsq)
1, El Bucle de Mensajes llama a GetNessage( ), el cual busca en tu cola de mensajes. Si la
cola de mensajes esta vacia, tu programa se detiene y espera hasta que haya un mensaje
(se bloquea)
2. Cuando ocurre un evento, causando que un mensaje sea puesto en la cola (por ejemplo el
sistema registra un click del mouse) Getessage( ) retoma un valor positivo indicando que
hay un mensaje para ser procesado y que ha llenado los campos de la estructura ws¢ que le
pasamos.
3. Tomamos el mensaje (en la variable usa) y se lo pasamos a TransalateMessage( ), éste
hace un proceso adicional traduciendo mensajes con teclas virtuales, en mensajes con
caracteres. Este paso puede ser opcional, pero ciertas cosas no funcionaran si no lo
utilizamos.
4, Una vez que finalizamos de traducir el mensaje, se lo pasamos DispatchMessage( ). Lo que
DispatchMessage( ) hace es tomar el mensaje, chequear a que ventana esta destinado y
buscar el Window Procedure de dicha ventana. Luego llama a dicho procedimiento enviando
como pardmetro el handle de la ventana, el mensaje, wparam y param.
5. En el Windows Procedure chequeamos el mensaje y sus parémetros y luego hacemos lo que
desamos con ellos. Si no estamos rranejando dicho mensaje, por lo menos siempre llamarnos
a DefWindowProc( ) el cual realizard las acciones "por default" (lo cual significa que a
veces no hace nada).
6. Una vez que hallas terminado de procesar el mensaje el Window Procedure retorna,
DispatehMessage () retorna y regresamos al comrienzo del bucle.
Este es un concepto muy importante para los programas de windows. El Windows Procedure no es
magicamente llamado por el sistema, de hecho lo llamamos indirectamente através de
DispatchMessage( ). Si deseas, puedes usar Getiindowiong( ) en el handle de la ventana a la
cual esté destinado el mensaje para buscar el Window Procedure de la misma y llamario
directamente!
while (GetMessage(sMsq, NULL, 0, 0) > 9)
‘
(ANDPROC) GetWindowLeng (Msg.hwnd, GWL_HNDPROC) ;
WNDPROC fWndProc
c |) Msg.message, Msg.wParam, Msq.1Param);
EW
He intentado esto con el cédigo del ejemplo anterior y funciona. Sin embargo hay varios aspectos
como la traduccién UNICODE/ANSI, entre otros, que este método no tendra en cuenta y
probablemente haga fallar hasta la aplicacién mas trivial. Por lo tanto hazlo como prueba pero no
lo hagas en el cédigo real.
hrapaivingrog.orgfutcrialesimessage oop ht 28ssri22016 “Tutorial - Wind2: Crprender el Bucle de Mensales
Observa que hems usado GetWindowLong( ) para recuperar el Window Procedure asociado con
la ventana, pero épor que no lo llamamos directamente?, Bien, nuestro bucle de mensajes es
responsable de TODAS las ventanas en nuestro prograrna, esto incluye cosas como botones,
listas, etc., que tienen su propio Window Procedure asociado, por lo tanto necesitamos
aseguramos que llamamos al procedimiento correcto para una cierta ventana. Debido a que mas
de una ventana puede usar el mismo Window Procedure, usamos el primer parémetro (el handle de
la ventana) para decirle al Window Procedure a que ventana esta destinado el mensaje.
Como puedes ver tu aplicacién pasa la mayor parte del tiempo dando vueltas y vueltas en el bucle
de mensajes donde alegremente envias mensajes a las felices ventanas que los procesardn. éPero
que haces cuando quieres que tu programa salga? Debido a que estamos usando un bucle while (
) SiGecMessage( ) retomara PALSE (je 0), el bucle podria finalizar y de esta manera podriamos
alcanzar el final de nuestro winain( ), es decir salir del programa. Esto es exactamente lo que
PostQuitMessage() realiza; pone un wM_QUIT en la cola y Getessage( ) en vez de retornar un
valor positivo, llena la estrucura sc y retorma 0, Hasta este punto el campo wparam de la variable
Msg Contiene el valor que le pasamos en PostQuitMessage( ) y podemos ignorarlo o retornarlo al
WinMain( ) donde se usaré como valor de salida cuando el programa finalice.
IMPORTANTE: GetMossage( } retornaré -1 cuando encuentre un error. Asegurate de recordar
esto o te sorprenderd en algtin momento... Ain cuando GetMessage( ) est definido para
retornar un 8001 (Booleano), éste puede retornar valores distinos de TRUE 0 FALSE, debido a que
Bo0r, estd definido como urNT (unsigned int), Los siguientes son ejemplos de cédigo que puede
parecer que funciona pero no procesaran ciertas condiciones correctamente,
while (GetMe
sage (6M:
while (GetMessage (sMsg, NULL, 0, 9)
while (GetMessage (sMsg, NULL, 0, 9)
Los segmentos de cédigo anterior estén mal! Quizds has notado que he usado el primero de
ellos en lo que va del tutorial, asi como lo mencioné, este funciona bien siempre y cuando
GetMessage( } no falle y cuando tu cédigo es correcto, no lo hace. Sin embargo no he tenido en
cuenta que si estas leyendo este tutorial tu cédigo probablemente no sea correcto en todo
momento y GetMessage( } puede fallar en algdin punto. Ya lo he corregido pero perdéname si he
olvidado algunos puntos.
while (GetMessage(sMsg, NULL, 0, 0) > 0)
Deberian usarse este 0 cédigos que tengam siempre el mismo efecto. Espero que ahora tengas un
mejor entendimiento del bucle de mensajes, si no es asi, no te asustes, las cosas tendran mas
sentido después que hayan sido usadas un par de veces.
Copyright © 1998-2003, Brook Miles (theForaer). Alll rights reserved.
Versién en Espafiol: Federico Pizarro - 2003
hrapaivingrog.orgfutcrialesimessage oop ht 38ssra2016 ‘Tutoral- Win82: Uso de Recursos
[ contenidos | #winprog ]
Us@ de Recursos
Puedes mirar los apéndices al final del tutorial para mas informacién sobre el uso de recursos con
VCH y BCH.
Antes de ver el tema en profundidad voy a cubrir el tema de recursos asi no tendré que reescribir
el mismo en cada seccién. Por ahora no necesitas compilar el codigo, solo es un ejemplo.
Los recursos son porciones pre-definidas de datos que se almacenan en formato binario dentro de
los archivos ejecutables y se organizan dentro de un archivo con extensién ".rc" (resource script).
Los compiladores comerciales cuentan con un editor visual de recursos que permite crear recursos
sin editar manualmente este archivo. A veces la Unica manera de poder crear un recurso es editar
este archivo manualmente sobre todo si tu compilador no tiene un editor visual o no soporta la
caracterisitica exacta que necesitas.
Desafortunadamente, no todos los compiladores rranejan los recursos de la misma manera, Haré lo
mejor que pueda para explicar las caracteristicas mas comunes que son necesarias para trabajar
con recursos en forma general.
El editor de recursos incluido con MSVC-++ hace muy dificil la tarea de editar los recursos
manualmente, debido a que fuerza un formato propio en ellos y si intentas guardar uno que has
creado manualmente estropea completamente el archivo, En general no deberias preocuparte por
crear archivos de recursos desde cero, pero conocer como modificarlos manualmente puede ser
muy Util. Otra incomodidad es que MSVC++ nombrara el archivo de recuros por default con el
nombre “resource.h" ain si deseas llamarlo con otro nombre. Utilizaremos este con el propésito de
simplicidad para este documento, pero te mostraré como cambiarlo en el apéndice sobre
conpiladores.
Primero miremos un archivo de recursos bastante simple, con solo un icono:
finclude "resourci
IDI_MYICON ICON “my icon.
Este es todo el archivo. 1D1_Mvrcon es el identificador del recurso, Icon es el tipo de recurso y
"my_icon. ico" es el nombre del archivo externo que contiene al icono. Esto deberia funcionar en
cualquier corpilador.
éQue hay acerca de #include "resource .h"?, Bien tu programa necesita una forma de
indentificar el icono y la mejor forma de hacerlo es asignandole a este un Unico ID (1D1_KY1CoN).
Podems hacer esto creando el archivo de cabecera "resource." e incluir éste tanto en el
archivo de recursos (.rc) como en el programa (.c)
define IDT_MYICON 101
Como puedes ver, hemos asignado a 1D1_M¥zCon el valor 101. De ahora en mas podriamos
olvidamos del identificador y simplemente usar 102 cada vez que necesitarms referenciar el icono,
Pero 1DI_MYICON es UN poco mas claro, mas representativo y mas facil de recordar, sobre todo si
estamos trabajando con una gran cantidad de recursos
Ahora supongamos que agregamns un recurso MEU
finclude "resource.h"
Hnapaivingrog org ucrialesitesources Fmt 1ssra2016 ‘Tutoral- Win82: Uso de Recursos
IDI_MYICON ICON “my_icon.ico"
IDR _MYMENU MENU
BEGIN
POPUP "
BEGIN
MENUITEM "Zexit", 1D PILE_EXTT
END
Pile
Nuevamente IDR_MYMENU es el nombre del recurso y ment es el tipo. Ahora observa el SECIN y el
END. Algunos editores o compiladores utilizan la llave que abre ¢ en lugar de BEGzN y la llave que
cierra } en lugar de Eno. Si tu compilador soporta ambos, puedes elejir el que mas te guste si solo
soporta uno u otro debrerds hacer los reemplazos necesarios en el codigo fuente para que esto
funcione.
También he agregado un nuevo identificador, 19_F1LH_#x1"r, por lo tanto necesitamos agregarlo al
archivo de cabecera de los recursos (resource.h) para poder usarlo dentro de nuestro porgrama.
fidefine IDI_MYICON 101
define ID_PILE_EXIT 4001
Generar y mantener un registro de todos los identificadores de recursos puede ser una tarea
costosa cuando trabajamos con grandes proyectos, esta es la razén por la que muchas personas
utilizan un editor visual de recursos, el cual se encarga de hacer todo este trabajo por nosotros.
Aunque pueden generar problemas de vez en cuando y podrias finalizar con multiples recursos con
el mismo ID o problemas similares, es bueno ser capaz de ir y poder corregir esos errores uno
mismo.
Ahora veamos un ejemplo de como usar un recurso dentro de un programa.
HICON hMyIcon = Loadrcon (Instance, MAKEINTRESOURCE (IDI_MYICON)) ;
El primer parametro de Loadicon( ) y muchos otros recursos que utilizan funciones es el handle a
la instancia actual (la que obtenemps en winMain( ) y que también puede ser recuperada
utilizando la funcién GetwoduleHandie ( ), como lo demostramos en secciones anteriores). El
segundo es el identificador del recurso.
Probablemente te estés preguntando que es MAKZINTRESOURCE( ) y posiblemente por qué
coadtcon() toma un parémetro de tipo LPcTsTR en vez de, por ejemplo TNT, cuando le estamos
pasando un ID. Todo lo que MAXETNTRESOURCE hace es Convertir un entero (el tipo de nuestro ID)
a un LPCTSTR que es lo que Loadrcon( ) espera, Esto nos da una segunda forma de identificar
recursos: utilizando strings. Hoy en dia casi nadie hace esto, por lo tanto no voy a entrar mucho
en detalle, Basicamente sino utilizas #cefine para asignar un valor entero a tus recursos,
entonces el nombre es interpretado como un string y podemos referenciar los recursos dentro de
nuestro programa de la siguiente manera
‘CON hMyTcon
‘Ieon(hInstance, “M!
Cuando se les pasa un valor entero Loadicon( ) y otras APIs que cargan recursos pueden
identificar la diferencia entre un entero y un puntero a un entero chequeando la palabra superior
de dicho valor, Si es cero (comp podria ser el caso de un entero con un valor menor a 65535)
entonces asume que se trata del ID de un recurso, Esto efectivamente nos limita a usar IDs
inferiores a 65535, que a menos que tengas una gran cantidad de recursos, no deberid ser un
problema, Si es distinto de cero, entonces asume que el valor es un puntero y busca el recurso
por el nombre. Nunca le confies este trabajo a la API a menos que esté explicitamente establecido
en la documentacién. Por ejemplo, esto no funciona para commandos del meni como 1b_FILE_#X1
Hnapaivingrog org ucriaesitesources Fmt 28ssra2016 “Tutorial -Wins2: Mens @teones
[ contenidos | #winprog J
Mentes e Iconos
Eemplo: menu_one
Esta es sélo una pequefia seccién para mostrar como agregar mentes bisicos en nuestra ventana. Generalmente utlizarerros un
recurso mend pre-definido que estard en un archivo -re y que luego seré compilado y enlazado con el archivo .exe. Esto suele
Gepender de cada compiladar, los compilacores comerciales cuentan con un editor de recursos que puedes utiizar para crear tus
mendes, pero en este ejemplo voy a mostrar el texto del archivo re para que puedas agregar rrendes rranualmente. Generalmente
tengo un archivo .h que incluyo centro de mis archives .c y .fc .Este archivo contiene os identificadores pare controles, terms de
tun mend, ete
En este ejemplo puedes comenzar can el cédigo de la ventana que viros en el ejemplo simple_window, e ir agregando el cédioo de
esta seccién,
Primero el archivo .h, generalmente llamado "resource.h”
#dofine 208 MYMENU 101
fdefine ZT MYICON 201
#define 20 FILE EXIT 9001
#dofine 2D _S1EE_Go 9002
No hay demasiado aqui, pero nuestro mend serd muy simple. Los nombres y valores puedes escogerlos arbitrariamente. Ahora
‘escribaros nueste archiva re
Hinclude “resource
POPE “KFiLe™
BEGIN
BEGIN
PENUITEN "EGO", 10 STUFF GO
END
Deberds agregar el archivo .rc @ tu project o makefile dependiendo de que herrarienta estas usando.
También debes agregar la linea $include "sesource.n” en tu archwvo fuente (.c) para que os identificadores de los comandos de!
mend y los del recurso mend estén definides.
La forme mas fécil de asignar un mend y un icone a nuestro ventana es especificar ambos cundo registramos la clase ventana,
comp esto:
we.hieon = losdtcon (GetModu: etfandie (NUL , MAKEINTRESOURCE (7DT_MYICOX)
Werhiconsn = (HICON) Leadirage (GotMedulsHandls (NULL), MAKE*NT3230URCE (ZDT MY=CON), IMAGE ICON, 16, 16, 09;
Carrbia esto y observa que sucede, Tu ventana ahora deberia tener un meni File y un mend Stuff, con los respectives items
dentro, Esto es asurienda que nuestro arcnivo .rc fug apropiadamente cormllada y enlazaco en nuestro programa, (nuevamente
mmra las notas sobre corplledares)
He usado icadreon( ) para cargar el icono grande debido a que es ras simple, sin embargo solo cargaré los iconos en la
resalucién por defauit de 32x32, por lo tanto pare cargar la imagen pequefia necesitarmos usar Loaatmase ( } Observa que los
archivos de iconos y de recursos pueden contener miltiples imgenes y en este caso, la Gnica imagen que hay, contiene los dos
tamaios que estamos cargendo.
Ejemplo: menu_two
Una altemativa cuando usarrs el recurso meni es crear uno en tiempo de ejecucién. Esto lleva un poco mas de trabajo de
programacién, pero agrega ras flexiblldad y a veces es necesario.
‘También podemes usar iconos que no estén alracenados come recursos, podriares elegir almacenar nuestros iconos en un archivo
separado y cargarfos en tiempo de ejecucién, Esto también podria damos la opcién de permtir al usuario seleccionar un icono con
los dialoges comunes (comeron dialogs) que veremes luego, 0 alguna otra opcién que tenga el mismo efecto.
hnapaivingrog.orgutorialesimenas Hin 18ssra2016 “Tutorial -Wins2: Mens @teones
Comenzaremos nuevarrente con el ejemplo simple_window sin agregar el archivo .hy el archivo .re. Ahora manejaremos el mensaje
JM CREATE y agregaremos un meni 2 nuestra ventana,
Uaege Soke tan tte cane
cee
HMENY hMenu, bsubvenay
hyena = createmenu(}
hewbliens = CreatePopupttoms (12
appandMens (hsubMens, ME_STRING, ID _ETLE EXIT, Meaxit*)
AppendMesu (hilenu, MF_STRING | MP_POPUP, (UINT)nSubMenu, "SFLIeM)
heubtienu ~ createPepspttens ():
AppendMieas (hSublen, MF STRING, ID_STUFE_¢
Appenddions thier, JF STRING | MP POPUP, (UIKN)RSuBNenu, “eStuee™) 2
Setvenu (hand,
gethund, WM SETICON, ICON SIG, (LUARAM) RIcoA);
ogg "could not load large icon!) "Error, MB_OK | MS_TCONERROR) +
hzconsa
Se {sTeonsm)
SoncMessage {hwnd W¥_S8TICON, ICON SMALL, (LPARAM) AIconSm) +
else
MessageBox thane, "Could not load anall icon!" "Eezer, MB_OK | MS_ICONERROR)
Jaateage (Note, *wenu_two.ieo", IMAGE_ICON, 26, 16, LR LORDEROMEILE) +
1
break:
Este crea un mend igual al que hemos definido en nuestro archivo de recursos y se lo asigna @ nuestra ventana. Un meni que es
asignado a una ventana es automsticamente removido cuando el programa termina, por lo tanto no necesiterms preacuparnos por
librarmos de &I cuando el prograrra finalice.
EI cédigo para los (conos es muy simple, llamarras 2 Loadrmage( ) dos veces: primero para cargar el [cono de 16x16 y luego el
Icono de 32x32, No poderrns usar uoacteon( ) en todo momenta debide a que éste sélo carga recursos, no archivos, En el
pardmetro del handle a la instancia, especificarres el valor NULL debido a que no estamos cargando un recurso de nuestra médulo y
fen vez de pasar el ID de un recurso, pasamos el nombre del archivo donde se encuentra el icono, Por iltim, pasamos el flag
1uR_LoADEROW? 1u= para indicar que la funcién trate el string que le pasarmos com un nombre de archive y no como el nombre de un
Sl esto tiene éxito, asignarros el handle al icone utilzando w_si:r icon y sifalla mastramos un mensaje diciéndanos que algo ha
curd.
NOTA: La larrade @ Losdnage( } fallaré si el archivo con el icono no se encuentra en el directorio de trabajo del programa, Si
estas usando VC+ y ejecutas el programe desde el IDE, el directorio de trabajo seré aquel donde se encuentre almacenaco el
proyecto. Sin embargo si efecutas desde el Debug, desde el explorer ola linea de corrando, entonces necestarss copiar el archivo
‘con los iconos dentro de dicho directario pare que el progremma pueda encontrarlo. Sitado esto fala, especifica la ruta completa.
Ok, ahora que tenemos un meni necesitamns que haga algo. Esto es muy simple, todo Io que necesitimaos hacer es manejar el
mensaje iey_comzaxo. Ademés necesitaremos chequear que comendo estamas reclbiende para pader actuar acorderente. Ahora
nuestro Winbroet ) deberia lucir como este:
RESULT CALLBACK WndProe (HHND hynd, UINT Message, APARAM wParam, LPARAM lParam)
switch (eseags)
t
case WY_CREATE:
sERU hora, Subtenuy
hviens = Createsiena(l
hgubtenu = CreatePopuptteny (7
AppendMent (hSubMena, MF_STRING, ID_FIE EXIT,
Appendtienc (hens, M&_STRING | ME_POPUE, (UINT)SubMens, "SPLle"):
SubMons = CreatePopupMens () 7
Appendtien (hSubMenu, MP STRING, 1D STUEY GO, *s60"):
hnapaivingrog.orgutorialesimenas Hin 20ssra2016 “Tutorial -Wins2: Mens @teones
Appendvonsthtents, KE STRING | MP ROPUP, (UINE)hSubNens, "AStufe")
Setmeau (owed, Menu ¢
icon = Leadinage (WULL, "mens tvo.eo", IMAGE ICON, 22, 32, LR_LOADFROMPILE) ;
fe rsreon)
SendMessage (tnd, WM_SBTICON, ICON BIG, (LPARAM) hIcon) ¢
MessageBox thwnd, “Could not load large icon!" “Eezor™, MB_OK | MB_ICONERROR)
one = Loadinaga (NULL, ‘meny_two-Leo", IMAGE ICON, 16, 16, 18 LOADEROMPILE)
ir ¢areonsay
Sondliessage (tnd, WM SBTICON, IGON SMALL, (LPARAM) nIconSe)
Meseagetiox (hwnd, "Could not load amall ‘eon!", “Error, M_OK | MB_ICONERROR) :
d
beeak
break:
beeaks
'
break:
case WY CLOSE:
Destroyaindow (hina):
break
case WY_DSSTROY:
EguisMessage (0)?
sesame
return pefitindow!
cthwnd, Message, wParam, 12aram);
1
return 0:
'
Coro poderros ver, hemos procesado el mensaje wiz_commann y éste tiene adentro otro switch( }. Este switch ) dlstingue
Usando el valor de la palabra inferior ce wparam: si és el caso de un mensaje ay. coMmennd, entonces contiene el ID del control o
mend que envié el mensaje.
Obviamente querermos que el item Exit clerre el program, Por lo tanto en el procesador del mensaje sl covaNs, 1D siLk ext
usaremas el siguiente c6digo:
PestMessage (hind, HH_CLOSE, 9, 0):
El procesador del mensaje i_commaxo deberia lucir comm este:
‘case WM COMMAND:
witch (ZOWORD(wearan) )
‘
case ID FILE EXIT
PosiMessage (nwnd, WM_CLOSE, 0, 0)
case ID STUFF co:
)
breaks
Te dejo comp tarea realizar el otro comanda Ip STUFF co para que haga ajo.
Elicono del Programa
Habrés notade que ahora el archive mens. one exe muestra com {cone el icono que agregarms come recurso, mientras que
snenu_two.exe no lo hace debido @ que estamos cargando un archivo extemo. El Windows Explorer simplemente muestra el primer
ican (ordenados numericarrente por ID) y debido a que sélo tenerros un icono, este es el que seri mostrado. Si quieres
asegurarte de que un cierto icono sea mostrado por el Windows Explorer simplemente agrega este comp recurso y asignale un
humero bajo de ID... como i. De agul en més, en tu programa, ya no necesitas referite al archivo que contiene al icono y puedes
asignarle a cada ventana un icona diferente silo deseas.
Copyright © 1998-2003, Brook Miles (theForger) All rights reserved
Versién en espafiol: Federico Pizarro - 2003,
hnapaivingrog.orgutorialesimenas Hinssra2016 “Tora - wins2: Dialogos, los mejores ages dos programadores de GUIs
[ contenidos | #winproa }
pialogos, los mejores amigos de los
programadores de GUIs
Ejemplo: dig_one
Dffcimente exista un programe en Windows que no | Eanennaere =lo/x)
Use cajas de didiogos. Simplemente haz click en Har
File/Open de cualquier editor de textos 0 cualquier =
otro tipo de editor y isorpresa! estas enfrente de
una caja de didlogo, una que probablemente te
permita escoger el archivo que deseas abrir. About this progam.
Los didlogos no estén Imtados a los didiogos -«-Lx| |_ Anminele Pag unshoniatow tours
estandar para abrir un archivo, por el contrario,
pueden lucir camo se te ocurr. El punto atractivo ty icFone
de los diélogos es que proven una forma répida de
ordenar y crear Interfaces Graficas al Usuario (GUI)
y también realizar algunos procesos por default, disminuyendo la cantidad de cédigo que deberms escribir.
Una cosa para recordar es que los didlogos son solo ventanas. La diferencia entre un dlalogo y una ventana
“normal” es que, con los didlogos, el sistema hace algunos procesos adicionales por default, como
inicializar controles y manipular el orden del tab. Todas las APIs que son aplicables a las ventanas
funcionan correctamente en los didlogos y viceversa.
EI primer paso es crear el recurso dialogo. La manera en que se hace depende de cada corrpilador/IDE, al igual
que con todos los recursos. Aqui, voy a mostrarte el texto plano del dialogo en el archiva .rc y debes
Incorporarlo tu mismo en tu proyecto.
IDD_ABOUT DIALOG DISCARDAB:
STYE® DS_MODALFRAME POPU
CAPTION "My About Bo:
FONT 8, "MS Sans Serif’
+ 0, 239, 66
3_CAPTTON |
BEGIN
DEFPUSHBUDTON "40K", IDOK, 174,18, 50,14
USHBUTTON "gCancel", IDCANCEL, 174, 35,50/14
GROUPBOX "About this program...",1DC_STATIC, 7,7, 225,52
cTExt "An example program showing hew to use 9 Boxes\r\n\e\nby theForger"
IDC_STATIC, 16,18, 144,33
END
En la primer linea, :pb_aBOUTDLG es el ID del recurso, DraLos es el tipo de recurso y los cuatro numeros son:
coordenada X de le esquina superior Izquierda, coordenada Y de la esquina superior Izquierda, Ancho y Alto del
dialogo, Estas NO SON PIXELES, estén expresadas en unidades de didlogos que estén basadas en el tamafio de
la fuente usada por el sisterra (y elegida por el usuario). Si tienes seleccionada una fuente grande el dialgo
sera grande y si utilizas una fuente pequefia el dialogo serd mucho menor. Esto es importante porque asegura
que todos los controles tengan el tamafio apropiado para poder mostrar adecuadamente el texto con la fuente
seleccionada. Puedes convertir unidades de didlogo a pixeles en tiempo de ejecucién usando vapDialogRect (
). DISCARDABLE le dice al sistema que puede realizar un "swap" para copiar el dialogo de la memoria al disco
cuando no esta siendo utilizado para conservar recursos del sistema,
La segunda linea comienza con sz¥iz y sigue con los estilos de ventana que serén usados para crear el
didlogo, Esto deberia estar explicado en createtlindow() en tus archivos de ayuda. Para poder usar las
constantes predefinidas necesitamos agregar #include "windows." en nuestro archivo de recursos .rc 0 en
el caso de VC++ podemos usar Si utllzas un editor de recursos estos archivos
serdn incluidos automiticamente, si es necesario.
nres.n" 0 "afxres
Con capzzow indicamos el titulo del didlogo,
La linea row? especifica el nombre y tamafio de la fuente que utilizaremos para este didlogo. Esta puede no ser
la misma para todas las cormputadoras, depende de las fuentes que cada persona tenga instaladas y de los
tamafios que haya especificado.
Ahora tenemos la lista de controles para crear el didlogo
hapaivingrog org utoriallesialogs nt 1ssra2016 “Tora - wins2: Dialogos, los mejores ages dos programadores de GUIs
DEFPUSHBUTTON "60K",
)174,19,50,14
Esta es la linea para el botén OK. El "8" indica que la letra que sigue (en este caso la "0") puede ser combinada
con la tecla Alt para activear el control mediante el teclado (Alt+O, en este caso), Todo esto es parte del
proceso automitico que he mensionado, Dox es el identificador del control. TD0x es un control pre-definido
por lo tanto no necesitamos definirlo. Los cuatro nlimeros al final son las coordenadas X ¢ Y ce la esquina
superior izquierda y el ancho y alto de botén, todas en unidades de didlogos.
Si utilizas un editor de recursos para crear didlogos, esta informacién deberia ser puramente académica, de
todos modos suele ser necesario conocer como hacerlo directamente en el archivo de recursos (.rc),
especialmente si no tienes un editor visual de recursos.
Hay dos controles que tienen un ID zpc_sTa7zc (el cual es -1), esto es usado para indicar que nunca
necesitames acceder a ellos, por lo tanto no necesitamos un identificador. Sin embargo esto no quita la
posibilidad de darle un ID o que editor de recursos lo haga autorticamente.
El texto "\r\n" en la linea del cTEXT es el par CR-LF que representa el comienzo de una nueve linea
Bien, habiendo agregado esto a nuestro archivo de recursos (.rc) necesitamos escribir un Dialog Procedure
para procesar los mensajes de este dialogo, No te preocupes esto no es nada nuevo, pricticamente es lo
mismo que con nuestro Window Procedure (aunque no exactamente).
BooL ¢
{
7
AbousDigProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM LParam)
switch (Nessage)
case WM_INTTDIALOG
case WM_COMMAND
h (LOWORD (wear
case IDOK:
sndDialogthwnd, 1D0%) ;
breaks
case IDCANCEL:
log (hwnd, IDCANCEL) ;
)
Hay algunas diferencias importantes entre un Dialog Procedure y un Windows Procedure. Una es que NO
Jamamos a deri ndow?roc( ) para mensajes que no manejamos. Con los dilogos esto es realizado
automiticamente.
Otra diferencia es que, generalmente, retornamos =azs= para aquellos mensajes que no procesamos y TRUE
para aquellos que si procesarms, A MENOS que el mensaje especifique que valor deberms retornar. Observa
que esto es lo que hicimos anteriormente, por default, no debemos hacer nada y retomar ease, mientras que
los mensajes que si procesamos entran en el switch () y retoman 720R.
La siguiente diferencia es que no llamamos iow () para cerrar el didlogo, si no que utilizamos
End2ialog( }. El segundo pardmetro es el valor retornado a quien halla llamado a D!alogkox (
Por ditima, en vez de procesar Wi_CREATS, procesarmos Wi_rNTTDIALOG para realizar cualquier proceso que sea
necesario antes que aparezca el didlogo y luego retomamos sus, para darle el foco al teclade en el control
asignado por default.(Puedes procesar el mensaje vo CREATE, pero éste es enviado ANTES que los controles
hayan sido creados, por lo tanto no puedes acceder a ellos. Cuando procesamos it_INI7DIAL0G los controles
ya han sido creados, lo que nos permite acceder a ellos).
Suficiente, hagéroslo...
case WM
MAND
ca (LOWORD (wPaxam) )
hnapaivingrog.orgutoriallesialogs mt 28ssra2016 “Tora - wins2: Dialogos, los mejores ages dos programadores de GUIs
cqBox (Getoduletiand.e (wu
ABOUT) , wa:
Ly
AbOULDLgProc) +
MessageBox ( alog exited with IDOK.", "Notice",
MB_OK | MB_TCONTNFORMATION) +
)
else if (ret == IDCANC
sageliox (hwnd WIth TDCANCEL.", "Notice!
‘MB_OK | MB_TCONTNFORMATTON) ;
)
else if(ret == -1)(
MessageBox (hwnd, "Dialog failed!",
MB_OK | MB_ICONINFORMATION) ?
break:
Este es el cédigo que he usado para crear mi dialogo "About", seguramente te preguntas por que est puesto
Gentro del manejador iy cokMawn, si no tlenes claro este aspecto, puedes revisar la seccién de menués
iconos. 1D_KELP_ABOUT es el dentificador del item mend Help\About.
Debido a que queremos que e! ment de nuestra ventana pricipal cree el didlogo, obviarmete debermos poner
este cédigo en el wiinProc( ) de nuestra ventana principal, no en el Dialog Procedure.
Ademas, he alrmacenado los valores de retomo de las lamadas a Dialogiox( ), solamente con el propésito de
observar el efecto de presionar dentro del didlogo los dos Botones, Esc, Enter, etc... y poder ilustrar como
utilizar los valores de retomo de un didlogo para chequear si tubo éxito, hubo alguna falla o para ver la eleccién
del usuario o alguna otra informacién que elijas para envidrsela de retomo a quien lo llame desde el Dialog
Procedure.
Box (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_ASOUT), hwnd, AboutD1gProc) ;
1DD_ABOUY es el ID del recurso didlogo. hwnd el el handle a la ventana padre del didlogo. aboutDigPzac( ) es,
por su puesto, el Dialog Procedure usado para controlar el didlogo.
Un astuto lector eventualmente podria haberse preguntado: si DialogBox() no retorna hasta que el didlogo
sea cerrado, entonces no podemos procesar mensajes mientras el dialogo esté activo, écomo funciona esto?.
Bueno, el tema es que los didlogos tienen su propio bucle de mensajes, por lo tanto mientras el didlogo esta
slendo mostrado en pantalla (esta activo), nuestro bucle de mensajes es pasado a un segundo plano y empieza
a ejecutarse el bucle que Windows utiliza por default. Este bucle es manejado por Windows y también se
encarga de cosas como mover el faco del teclado de control en control cada vez que presionarmos la tecla
Tab.
Otro efecto de usar DialogBox( ) es que la ventana principal queda desactivada hasta que el didlogo sea
despachado. A veces esto es lo que deseamos y otras veces no, corre cuando queremos usar un didlogo con
una barra de herramientas flotante, En este caso queremos ser capaz de interactuar con ambos, la ventana y
el didlogo. Pero esto serd visto en la préxima seccién
Copyright © 1998-2003, Brook Miles (theForaer). All rights reserved.
Versién en espafiol: Federico Pizarro - 2003
hapaivingrog org utoriallesialogs nt 38ssra2016 Tutorial - Win82: Dialogos de Madlamiento
[ contenidos | #winoroa ]
Dialogos de Modelamiento
Ejemplo: dig_two
Ahora echémosle un vistazo a CreateDialog( ), la hermana
de DialogBox( ). La diferencia es que mientras DialogBox ()
implementa su propio bucle de mensajes y no retorna hasta
que el didlogo sea cerrado, createbiasog() actla de manera
mas parecida a una ventana creada con Createwindowsx (),
la cual retorna inmediatamente y depende de tu bucle de
mensajes enviar los mensajes a tu ventana principal. Esto es
llamado Modelamiento mientras que DialogBox () crea SS — Dr This One
didlogos Modales.
Podemos crear el recurso didlogo de la misma manera que lo hicimos en el ejemplo anterior, también
deberms asignar el estilo extendido "Tool Window" (ventana de herrarrientas) para darle a la barra de
titulo el tipico titulo pequefio de las barras de herramientas. El recurso didlogo que he creado es el
siguiente:
IDD_TOOLBAR DIALOGEX 0, 0, 98, 52
STYLE DS_M RANE | WS_POPUP | WS
STYLE WS EX TOOLWINDOW
CAPTION "My Dialog Toolba
FONT 8, "MS Sans Serif"
BEGIN
PUSHBUTTON
PUSHBUTTON
CAPTION
This Button", IDC_PRESS,7,7,84,14
is One", TDC_OTHER, 7,31, 84,14
END
Habrés observado que el editor de recursos ha reemplazado DiAL0G con D1a10GRx indicando que
queremos asignar un EXsTYLE en nuestro didlogo.
A continuacién, queremos crear el didlogo cuando el programa se ejecuta y ademas queremos que el
didlogo sea visible, por lo tanto hacemos esto en nt,_CRSATE. También queremos declarar una varialbe
global para almacenar el handle retormado por createDialog( }, que usarerms luego, DialosBox( )
no retorna un handle debido a que cuando éste retorna, el didlogo ha sido destruido.
ND g hToolbar = NULL;
case WM CREATE
g_hToolbar ~ Create!
wnd, ToolD1gProc) +
£(g_hToolbar != NULL)
RESOURCE (IDD_TOOLBAR) ,
olbar, SW_SHOR);
MessageBox (hwnd, "CreateDialeg returned NUL!
MB_OK | MB_ICONINFORMATION) ;
break;
Es una buena idea chequear siempre los valores de retomo, y si son validos (distintos de nuit)
mostramos la ventana usando showindow (), CoN DialogBox( } esto no es necesario debido a que el
sistema lama a Shovstiindow() por nosotros.
Ahora necesitamos un Dialog Procedure para nuestra barra de herramientas.
hnapaivingrog.orgfutrialesmaddess dialogs hint 1ssra2016 Tutorial - Win82: Dialogos de Madlamiento
BOOL CALLBACK Too1Dlg?roc(HWND hwnd, ge, WPARAM wParam, LPARAM 1Param)
‘n (Message)
switch (LOWORD (wParam) )
© IDC_PRESS:
MessageBox (hwnd, "Hi!
OK | MB_ICONEX
nis is a message",
LAMATION) 7
case IDC_OTHER
MessageBox (hwnd, "Sye!", "This is also a message",
MB_OK | MB_TCONEXCLAMATTON) ;
break;
break;
La mayoria de las reglas de procesamiento de mensajes se aplican a los didlogos creados con
CreateDialog( ) y al igual que con Dialogzox( ) no llamamos a beftiindowProc( ), retormamos
FALSE en los mensajes que no procesamos y TRUE en los que si lo hacernos.
Un cambio es que, en los didlogos de modelamiento, no llamamos a EndDialog( ), simplemente
usamos Des=royivindow( ) como lo haciamos con las ventanas regulares. En este caso, destruimos el
diélogo cuando la ventana principal es destruida. En el vinaProc(} de la ventana principal.
case WM DESTROY:
DestroyWindow(g,
Por iltimo, pero no menos importante, queremos ser capaz de mostrar y ocultar nuestra barra de
herramientas cada vez que el usuario lo desee, por lo tanto agregamos dos comandos al menti y los
procesamos asi:
OMMAND
fh (LOWORD (wParam) )
DIALOG_sHOW
ShowWindow(g_hToolbar, $N_SHOW);
break;
case ID_DIALOG_BIDE
ShowWindow (g_hToolbar,
break;
//... other command handlers
Hasta aqui, debes ser capaz de crear tu propio ment usando el editor de recursos 0 manualmente, si
no es asi (comp pasa siempre) puedes observar el ejemplo dig_two que viene junto a este tutorial.
Cuando ejecutes el programa, debes ser capaz de acceder a la ventana de didlogo y a la ventana
Pricipal al mismo tiempo
Si ejecutaste el programe e intentaste correr el foco entre los dos botones usando la tecla tab,
probablemente habrés observado que esto no funciona, como asi tampoco funciona activar los
botones presionando Alt-O 0 Alt-P. Por que no? DialogBox() implementa su propio bucle de
hnapdivingrog.orgfutrialesmddess dialogs hint 28ssri216 Tutorial - Win: Didlogas de Modelariento
mensajes y procesa estos eventos por default, pero CreateDialog( } no lo hace. Podemos hacerlo
nosotros, llamando a TsDialogMessace( } en nuestro bucle de mensajes, el cual hard los procesos
por default en lugar nuestro.
while (GetMess: NULL, 0, 0))
if (1TsDialogMessage (g_hToolbar, sMs¢))
rans laleMessage (6M'sq) 5
DispatchMessage (aMsg) ¢
Aqui, primero pasamos el mensaje a IsDialogMessage( ), si el mensaje esté destinado a nuestra
barra de herramientas (indicado por el handle que le pasarnos) el sistema realizar los procesamientos
por default y retornard TRUE. Si es el caso en que los mensajes ya han sido procesados, entonces no
llamamws a TransalateMessage( } 0D. ©() y si el mensaje es para otra ventana, lo
procesamos de la manera usual.
Mes
Es importante notar que IsDialog¥essaje( ) también puede ser usado con ventanas que no son
didlogos, con la intencién de darles el comportamiento de un didlogo. Recuerda que un didlogo es una
ventana y la mayoria (por no decir todas) ce las API's para didlogos funcionarén correctamente en
cualquier ventana.
Un aspecto para destacar es éque pasa si tienes mas de una barra de herramientas? Bien, una posible
solucién es tener una lista (0 un arreglo) y ciclar en nuestro bucle de mensajes pasando cada handle a
TsDialogMessage( ) hasta que encontremos el correcto y si no lo es, hacemos el procesarriento
regular. Este es un problerma de programacién genético y no uno que esté relacionado con Win32, por
lo tanto se deja como ejercicio para el lector.
Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.
Versién en Espafiol: Federico Pizarro - 2003
hnapdivingrog.orgfutrialesmddess dialogs hint
38ssra2016 CCortrolesestinder:Botones, Eeclén, Calas de lista, Estos
[ contenidos | #winprog ]
Cofrtroles estandar: Botones, Edicién, Cajas de
lista, Estaticos
Ejemplo: ctl_one
Ya hemos usado botones en ejemplos anteriores, por lo ESSN
tanto deberias estar mas o menos familiarizado con ellos,
sin embargo debido a que he usado botones en este bag [This 2stirg F tmes
ejemplo los he agregado como parte del titulo solo con el
propésito de completitud This ating
Thisis 9 sty Aa
Controles Thisiea ting Remove
Thisis sting =
Una cosa para recordar acerca de los controles es que Clea
son solo ventanas. Al igual que cualquier otra ventana
tienen un window procedure, una clase ventana etc... que
es registrada por el sistema, Todo lo que puedes hacer
con las ventanas normales, puedes hacerlo con los
controles.
Mensajes Thisitem wos added 5S times
Come recordarés de las primeras discusiones sobre el
bucle de mensajes, Windows se comunica usando mensajes, enviamos mensajes para que un control
realice algo y cuando un evento ocurre sobre un control, éste nos envia un mensaje de notificacién. Pare
controles estndar esta notificacién sera un mensaje wm_coMMAND, come sucede con los botones y
menties y pare los Controles Comunes, de los cuales habiaré después, esta notificacién sera el mensaje
WM_NOTIFY
Los mensajes varian ampliamente para cada control, y cada control tiene su propio conjunto de mensajes.
En algdin momento el mismo mensaje ser4 usado para mas de un tipo de control, pero en general solo
funcionaran en el control para el cual estén destinados. Esto es especialmente molesto con los mensajes
listbox y cormbobox (LB* y CB* ) los cuales si bien realizan tareas casi idénticas NO SON intercambiables.
Por otro lado, los mensajes genéricos como i
Después de todo un control es una ventana.
‘ex? son soportados por casi todos los controles.
Puedes enviar mensajes usando la API SendMessage( } ¥ usar GetDigItem( } para obtener el handle al
control, 0 puedes usar SenDigitemMessage( } el cual realiza ambos pasos. En ambos métodos el
resultado es el mismo.
Controles Edit
Uno de los controles mas comunmente usados en el entorno Windows, el control EDIT, es usado para
permitir al usuario ingresar texto, modificar, copiar, etc... El Block de Notas de Windows no es mas que
una ventana con un gran control edit dentro de ella,
Aqui est el cédigo usado como interface para el control edit de este ejemplo:
SetDigitemfext (hwnd, ID
XT, “This is a string"
Esto es para cambiar el texto contenido dentro del control (esto puede ser usado para cualquier control
que tenga un valor de texto asociado a él, STATICs, BUTTONS, etc...)-
Recuperar el texto de un control también es facil, si bien es apenas mas trabajo que cambiarlo...
int len = GetWindowPextLength (GetDLgz
if(en > 9)
t
(awa, IDC_TEXT));
Hnapaivingrog.orgutorialesicontels nar 1ssra2016 CCortrolesestinder:Botones, Eeclén, Calas de lista, Estos
char*
buf ~ (char*)GlobalAlloc (G?TR, ten + 1);
GetDigitemfext (hwnd, 19¢ TEXT, buf, Len + 1);
//Rellenamos con texte
Global Free ( (HANDLS) buf) +
Primero que todo, necesitaros reservar memoria para alrracenar el string, para esto necesitamos conocer
cuanta memoria reservar. No hay una funcién Get: gttemTex:tenght ( }, pero existe
Getivindow?exttenght ( 1, por lo tanto todo lo que necesitamos hacer es obtener el handle al control
usando GetDigrTtem( }.
Ahora que sabernos la longitud, podemos reservar la memoria necesaria pare almacenar el string. Aqui, he
agregado un chequeo para ver si existe algin texto (i.e la longitud del string es mayor que cero) para no
estar trabajando sobre un string vacio. Asumiendo que existe dicho texto llamamos a
para reservar la memoria. Si eres usuario de DOS/UNIX GlobalAlioc( ) es equivalente a calloc( ). Esto
reserva algo de memoria, inicializa su contenido a cero (0) y retoma un puntero a dicha memoria. Hay
diferentes flags que puedes pasar en el primer parémetro para hacer que se comporte de manera diferente
para diferentes propésitos, pero esta es la tinica manera en que la voy @ usar durante el resto del tutorial.
Observa que le he sumado 1 a la longitud del string en dos lugares, éPor que? Bien,
TextLenght ( } retorna el niimero de caracteres que contiene el texto del control SIN INCLUIR
el terminador nulo. Esto significa que sino sumarros 7 el texto podria caber pero el terminador nulo
sobrepasaria Ios limites del bloque de memoria, causando una violacién de acceso 0 sobreescribiendo otros
datos. Debes ser cuidadoso cuando trabajas con longitudes de strings en windows, debido a que algunas
APIs y mensajes esperan que las longitudes incluyan el caracter nulo y otras no lo hacen. Sierrpre lee la
documentacién cuidadosamente.
GetWind
Finalmente podemos llamar @ GetDlgitenText ( ) para recuperer los contenidos del control en el buffer de
memoria que acabamos de crear. Esta llamada espera el tarrajio del buffer incluyendo el terminador nulo
El valor, el cual vamos a ignorar aqui, es el nimero de caracteres copiados SIN incluir el terminador nulo...
divertido eh?
Hay un segundo conjunto de APIs llamadas tiocslAlloc( }, TocalFree( }, etc... que son parte de las
APIs de Windows 16-bits, En Win32 las funciones para memoria Local* y Free* son identicas.
Edits con Numeros
Cuando ingresamos texto estd todo bien, épero que pasa si queremos que el usuario ingrese un numero?
Esta es una trea muy comin y afortunadamente hay una API para hacer esto mas simple. Esta API se
encarga de toda la asignacién de memoria, como tambien convertir el string a un valor entero.
BOOL bu:
int aTines eDlg a, 0 ebSuccess, PAL!
Getbigitemint ( ) funciona de manera muy parecida a cetDigitenext ( }, excepto que en vez de
copiar al string a un buffer, lo convierte intermamente a un entero y luego retorna el valor. El tercer
parémetro es opcional y torra un puntero a un B00: Debido a que la funcién retoma 0 en caso de falla,
no hay forma de distinguir si la funcién ha fallado o el usuario ha ingresado como valor el numero 0, por lo
tanto usamos este pardmetro, Pero si te resulta adecuado utlizar el valor 0 en caso de error, entonces
puedes ignorar este pararmetro.
Otra caracteristica muy usada para controles de edicién es el estilo 3_NuésER, el cual perrrite que solo
sean ingresados los caracteres del 0 al 9. Esto el muy Util si solo quieres que se ingresen enteros
positivos, en otro caso no tiene mucha utllidad debido @ que no puedes ingresar caracteres como el menos
(-), el punto decimal (.) 0 la coma (,).
Listas
Otro control muy usado son las listas. Este es el Ultimo control estandar que voy a cubrir por ahora,
debido a que francamente no hay muchos interesantes y si atin no estas aburrido, yo si lo estoy :)
Hnapaivingrog.orgutorialesicontels nar 218ssri216 CCorrlesestinde: Sotones, Eccl, Cals dist, Esticos
Agregado de Items
La primer cosa que quieres hacer con una lista es agregar items en ella.
int index = sendD1gi
Message (hwnd, 1
LIST, LB_ADDSTRING, 0, (LPARAM) "Hi.
ere");
Como puedes ver, esta es una tarea muy simple, Sia lista tiene el estilo L85_so2?, el nuevo item sera
agregado en orden alfabético, en otro caso simplemente seré agregado al final de la lista.
Este mensaje retoma el indice del nuevo item, el cual podemos usar para realizar alguna tarea sobre el
item, como asociar algunos datos a él. Generalmente esto es como un puntero a una estructura
conteniendo mas informacién, 0 quizas un ID que usaras para identificar el item.
SendDigitenMessage (hwnd, 19¢_1
VATA, (WPARAM) index, (LPARAM) nTimes
Notificaciones
El proposito de las listas es permitir al usuario seleccionar cosas de una lista. Sin embargo, a veces
queremos ser capaz de hacer algo en el acto, quizas mostrar informacién diferente o actualizada, basada
en los items que fueron seleccionados. Para hacer esto necesitamos procesar los mensajes de notificacién
que nos envia la lista. En este caso estamos interesados en LSN SELCHAKGS, el cual nos dice que la
seleccién ha sido modificaca por el usuario, LBN_SELCHANGE es enviado via w™_comvAND, pero a diferenecia
del procesamiento de los mensajes w'_co“MaD que hacemos con los botones y menies, que son
generaimente en respuesta a un click, una lista envia el mensaje w_COMMAND por varias razones y
necesitamos un segundo chequeo pare averiguar que nos esta diciendo. El Cédigo de Notificacién es
pasado como el HTWORD de wParam, la otra mitad del parémetro nos da el ID del control
wiM_coMmAN!
fh (LOWORD (wParam) )
2 IDC_LIST:
// Tks our listbox, check
switch (HIWORD(w?
ram) }
case LEN_SELCHANG!
{7 Se
break;
anged, do stuff bh
break;
V+. other controls
break;
Obtener los datos de una lista
Ahora que sabernos que la seleccién ha cambiado, necesitamos obtener dicha seleccién de la lista y
realizar algo util con ella
En este ejemplo he usado una lista multi-seleccién, por lo tanto obtener la lista de items seleccionados es
un poco mas complicado, Si fuera una lista de seleccién simple, podriamos simplemente enviar
1_GRTCURSET, para recuperar el indice del item,
Primero, necesitamps obtener los indices de los items seleccionados para que podamos reservar un biiffer
de memoria en donde guardar los indices.
HWND hList = GetDlgttem(hwad, TB¢_LIST) +
int count = SendMessage(hList, LBLGETSSLCOUNT, 0
ov;
Luego, reservarmos un biffer basado en el numero de items, y enviamos el mensaje Lb_GE'T'SSL.ITEM para
llenar el arregio.
int “bi
s
f = GlobalAlloc (GP9R, sizeof (in
endMessage (hist, LB GBTSELITEMS, (W2ARAM)
ye
int, (LPARAM) bi
Hnapaivingrog.orgutorialesicontels nar 3ssra2016 CCortrolesestinder:Botones, Eeclén, Calas de lista, Estos
Jf... Do stuff with indexes
ree (aut);
el segundo, etc.. siendo el ultimo indice bu:
En este ejemplo, buf [0] es el primer indice, butt
Una de las cosas que podrias querer realizar con la lista de indices, es recuperar los datos asociados con
cada item y luego hacer algiin proceso con éste. Esto se hace simplemente enviando otro mensaje.
int data = SendMessage(hList, L8 GETITSMDATA, (WPARAM) index, 0) ;
Si los datos tienen algin otro tipo de valor (cualquier cosa distinta de 32-bits) simplemente debemos
convertirlo al tipo apropiado. Por ejemplo si almacenamos HBITMAPS en vez de ints...
HBITMAP hData = (HBITMAP) SendMessage (hL LB_GETITEMDATA,
"ARAM) i
Estaticos
Al igual que jos botones, los controles estticos son ampliamente triviales, pero una vez mas por el
propésito de completitud, voy a incluirlos aqu’, Los controles estaticos son generalmente aquello que es,
estatico, significando que no cambian 0 no hacen nada en especial. Son muy usados para mostrar texto al
usuario, sin embargo puedes hacer de ellos algo un poco mas util asignéndoles un Unico ID (VC-++ asigna
tun ID por default, r5¢_s"arrc, que tiene un valor de -1 y significa "Sin 1D") y luego asignarles el texto en
tiempo de ejecucién para presentar texto dindrrico al usuario
En el ejemplo, he usado uno para mostrar los datos de los Items seleccionados en la caja de lista,
asurriendo que se puede seleccionar solo un item
SetDlgitemIat (hwnd, IDC SHOWCOUNT, data, FALSE);
Copyright © 1998-2003, Brook Miles (theForaer). All rights reserved.
Versién en Espafiol: Federico Pizarro - 2003
Hnapaivingrog.orgutorialesicontels narssra2016 Tutorial - Win82: Didlogos FAQ
[contenides | #winproa }
Dialogos FAQ
Eemplo: dlg_three
No confundan, esto es un Tutorial, no una Referencia, pero las preguntas de
2lgunas personas son tan frecuentes que he pensado que serfa bueno inclifas
aqui
Cambio de los colores
En general la Gnica razén por la cual querrias hacer esto debe ser para simular un link en un didlogo o alguna taree similar,
porque en otro caso estarias haciendo que tu programa luzca horroroso a la vista si agregas una gran cantidad de colores a
los didlogos. De todas maneras esto no impide a las personas de hacerlo y hay algunas razones validas, aqui van :)
Windows envia a nuestro dialog procedure una gran cantidad de mensajes relacionados a los colores y procesando dichos
mensajes poderms cambiar el color de clertas cosas. Por ejemplo para cambiar el color de el didlogo en si mismo poderros
procesar el mensaje a CTLCOLORDLG, para cambiar el color de un control estatico pademes procesar el mensaje
fM_CTLCoLoRstarre, ete.
Primero debemos crear un pincel (Brush) para pintar el fondo y luego alracenadto para poder usarlo después, | mensaje
SM_CTLCOTORDIG y otros mensajes relacionados serén llamados atin durante el trancurso de Ia ejecucién de nuestro
programa y si cada vez que esto sucede crearmos un pincel podriamos desperdiciar una gran cantidad de RAM con pinceles
muertos. De esta forma tenemos mas control y podemes borrarlo cuando el didlogo es destruido, es decir, cuando ya
saberos que no lo vamos a necesitar mas.
NBRUSE g_HbrBackground = CreatesolidBresh(RGS(0, 0, 0))7
case WM_CTLCOLORDLG:
fetire (TONG) g_hbrBackyround:
i
HDG des:
SetTextColor (hdcStatic, RGS(258, 285, 258)};
(ONG) §_ HbrBackground
Observa la linea que fija el color de fondo en modo transparente.,. si quitamos esta linea el fondo seré llenado con el pincel
que herros especificado, pero cuando el control dibuje el texto, éste serd escrito encima con el color de fondo por default!
Poniendo el modo de dibujo de texto en transparente corregimos este problema. La otra opcién seria usar So=BkColor( }
para poner comp color de fondo el mismo color del pincel, pero prefiero la otra solucién.
Cambiar los colores en la mayoria de los controles estandar funciona de la rrisma manera, solo hay que observar los,
mensajes ws1_cricoLon* en la referencia de Win32. Observa que un control Edit enviard Un mensaje wn cTucoLonstar re si
5 de solo lectura y wi _crucoLoxspir si no bo es.
Sitienes mas de un control estatico y quieres que tengan diferentes colores, entonces cuando procesas los mensajes,
necesitarés chequear el ID del control que envia el mensaje para identificar de que control proviene y asi carrbiar los colores
al control adecuado. Tenerrns el simio del control en “Paran y a partir de este podemos obtener el 1D del control usando
GetoigctriD( ). Observa que el editor de recursos les da a los controles estéticos un ID ipc_svaric igual a -1, por lo
nto si necesitamos distinguirios debemos asignartes nuevos IDs.
Poner un icono en el Didlogo
Es una tarea simple, solo necesitamas enviar a nuestro didlogo el mensaje » n. Sin embargo, debido a que Windows
utliza dos iconos, necesitamos enviar el mensaje dos veces: una vez para el Icono pequefio mastraco en la esquina de la
ventana y otra vez para el icono grande mostrado cuando presionamos Akt=Tab. Podemos enviar el mismo handle ambas
veces, a menos que tengamos iconos de diferentes tamafios.
Para poner el icono de aplicacién por default, podemos usar el siguiente cédigo:
Senditessage (hwnd, WM SETICON, ICON SMALL, (LPARAM) LeadIcon (NULL, MAKSINTRESOURCE (1D1_APPSICATION) }) ¢
ndMessage (hwnd, WOSPTICON, TCONAIG, (LPARAM) leadicon (NUul., MAKETNTRESOURCE (1DI_APPLTCATTON) }) 2
Cuando reemplazes tu propio icono por uno por default, recuerda cambiar el pardmetro HINSTANCE de LoadTeon ( ) por el de
Ia instancia de tu aplicacién (sino le tienes almacenado en winkiain( }, puedes obtener éste larmando &
GetMedulefandle( )).
hnapaivingrog.orgutorialesidlgtan Hint 12ssra2016 Tutorial - Win82: Didlogos FAQ
éPor que no funciona mi Combo Box?
Uno de los problemas mas comunes que las personas tienen cuando agregan un Combo Box a sus dialogos es que no pueden
darse cuenta por qué la lista no puede ser mstrada cuando corren sus programas y hacen click en la flecha pequefia. Esto
fs entencible, debido a que la solucién no es muy intuitiva,
Cuando crearmos un combo box y especificames su altura, estamos especificando Ia altura entera, con la lista despelegada
Incluida, NO la ature del control cuando no esté desplegado, ya que ésta es determinada por el sistema de acuerdo al
‘armafio'de la fuente usada
Por ejemplo, si le damos al control una altura de 100 pixeles, el sisterra ajusta el tamafio del control con un valor por defauit
(digamos 30 en este caso) y cuando hacemes click en la flecha la lista desplegada podria ser de una miximo de 70 pixeles,
haciendo un total de 100 pixeles.
Si utllzas el editor de recursos de VC+ para ubicar el combo box dentro de tu dialogo, observaris que no puedes cambiar
el tamafo vertical. A menos que hagas click en la flecha en el editor y éste cambiaré el foco para indicar que estas
cambiando el tarefio de la lista desplegada, Luego puedes especificar el ancho con el valor que quieras.
Que pasa con todos los otros controles?
Bien, podria dar ejemplo de todos los otros controles, pero es lo que hacen MSDN y Petzold :) Si no puedes darte cuenta de
‘como usarlos, probablemente necesites re-leer algunas partes de este tutorial, o conseguir un libro que explique las cosas
mas amrpiarente,
Quiero darte un link 2 una pagina muy buena en MSDN, pero Microsoft parece estar determinado 2 impeditme dar links @
paginas individuales, debido a que carmbian répidamente © no funcionan por un periodo. Por lo tanto tendrés que darte
cuenta como llegar vos rrisrro, busca las secciones como User Interface Services y Windows Controls, a veces dentro de la
seccién Plataform
MSDN - Windows Controls
Copyright © 1998-2003, Brook Miles (theForaer), All nghts reserved,
Vesrsién en Espafol: Federico Pizarro
hnapaivingrog.orgutorialesidlgtan Hint
2Rser22016 ‘Aplicacion Part 1: Crear Controles en Tie de Ejecucién
[ contenidos | #winprog J
Aplicacién Parte 1: Crear Controles en Tiempo de
Ejecucién
Example: app_one
Pienso que dar un ejemplo sobre crear controles en ejecucién, si bien es muy usado, podria
ser en vano @ menos que la aplicacin realrrente haga algo util, por lo tanto en esta
sseccidn voy a comenzar can el desarrolo de un editor de texto y Io iremos desarrollando
hasta que alcanzemos un programa Gtil que soporte abrir, edtar y guardar archivos de
textos.
El primer paso, el cual es cubletto por esta seccién, seré simplemente crear la ventana y el
control EDIT que serviré como centro de nuestro programa
Comenzaremes con el esquelto del cédigo de la aplicacién Simple Window, agregaremas un
define para el ID de nuestro control y los siguientes dos manejadores de mensajes en
nuestro window procedu
fdeSine IDC MAIN =DIP 102
cane mt
HeoND hevofaulty
low agate:
hedit ~ createWindow®s(wS_SX CLISNTEDGE, "EDIT",
We_CHILD | WS_VISTBLE | Rs VECROLL | M3_HSCROLL' | Es MULTILINE | =5_AUTOVSCROLL | E5_AUTOHSCROLL,
0, 0, 100, 100, hwnd, (HMENU) IDC MAIN 517, GetNoduleHandte (MULL), NULL);
irdweaie’== NOLL}
ReseageBoxthwnd, *
fd not create aa:
box.) "Eevee", MB_OK | MS_ICONERROR) 7
hedefault = GetStockOdject (DEFAULT GUT FONT)
Sencliescage (REdit, WM_SETFONT, (PARAM) nfbefault, MAKELPARAM(FALSE, 0);
1
breaks
case
UND nedite
Rect reclient;
GotclientRect (hwnd,
schient)s
hidlt = GecDlgttenthvnd, TDC MAIN_EDIT) +
SetWindowPos (hEdit, WOLD, 0, 0, reChient.right, reClient hott
NozoaDea)
1
beeak
Crear los controles
Para crear los controles, al igual que Io hacemos para cualquier otra ventana, utilzamns la API croateWlindowEx( }. Pasarros una
Clase pre-registrada, en este caso la clase del control "EDIT", y obtenemos un control edit esténdar. Cuando usarmos diélogos para
Crear nuestros contfoles, basicamente estamos escribienda una Ista de controles a crear, tal que, cuando larmamos a Dsalogtox(
} 0 cCeeateDialag( } el sistema lee la ista de controles en el recurso didlogo y por cada uno llame a createiindowsx( }, con la
posicién y estilo con que fueron defindos.
> createWindowi WS EX CLIENTEDGE, "EDIT", *™,
S_CHTLD | RS_VISTBLE | WS_VSCROM. | WS_HSCE
0, 0, 100, 108, hwne, (HMENU) 1DC_MATN EDIT,
Asthpcte’ — wont)
MessageBox (hwnd, “Could not create edit box.", "Error", MB_OK | MB_ICONERROR) 2
| RS_MULTTLINE | RS_AUTOVSCROLL | FS_AUTORSCROLL,
Puedes ver que esta larrada a cresteitindowix{ } especitica clerta cantidad de estiloe y no es extrane tener
algun mas, espectalmense para los controls comunes los cuales ticnen una larga lista de opciones. Lo
prineros cuatro estilos WS deberian ser obvios, estamos creanda <1 control cone un hijo de nusstra vent
(uerenoe que sea visible y tenga barras de desplazamiento vertical y horizontal. Los tres eatilos que son
eepeciticas de los controles FOTT (PS MILTILINE | FS AUTOVSCROG | FS_AUTOESCROGL) eapecifican que el contro!
edit dese poseer méltiples lineas de texto y desplazarse autoniticanente cuando tipeamos nas alla éel limite
inferior de la ventana y del limite derecho de la misma.
tos estilos regulares (¥S_*) puedes encontrariog ent Listed hare, y los estilo extendides (WS_RX_*) son
explicadas en} crastebinaowisl| estas referencias con de MSDN conde también puedes encontrar links a los
estilos especitices de cada control (:
sen a1 caso del control EDIT)
fo window handle
Henos esp so padre del contyol y Le asignanos un ID de IDC_NAIN EDIT, el
hnapaivingrog.orgfutrialleslapp_one Hind orser22016 ‘Aplicacion Part 1: Crear Controles en Tie de Ejecucién
cual usarenos Iuego para referirsos al control, de 1a misma manera que lo harianos si el control hubiera side
creado en un diaiogo. Los parénetros de posicisn y tamato no inporzan mucho por el momento, debido a que
cambiarencs el taaro del control dindmicanesta con el mensaje WH SIZE para que éste siempre entre cn nuestra
Cambio del tamafio de controles creados dinmicamente
Senerainente, si ol tamaho do nuestra ventana puedo ser carbiads, cendrenos aigin eédigo para ropesicionar o
ajustar el tanano de los controles que hexos creado dentro de ella, para que siempre scan nostrades
apropiadarente:
GetclientRect (hwnd, arcclient) ;
Edit = GotDigivem(hwnd, IDC MAIN EDIT) ;
SetwindowPos (nkdit, NUlily 0, 0, réCliont.right, roClient.bottom, SHP_NOZOSDER)
Debido a que por ahora solo tenenos un concrol, 1a tarea es relativamente simple. Usamos GetClientsect ( )
para obtener 122 dimensiones del Area Cliente de 1a ventana, 1a gran azea vacla (por ahora) que ao inleleye
Tos hordes, neni o titule. Rete Llenard nuestra estructera RECT can valeres. loa valores left y tep siempre
soran 0, por 1o tanto pustes :gnorarlos. Los valores right and bovrom indican el anche y e2 alto del area
elienze’
A Cantinuseién, simplenente abtencnos el handle a auesteo contcol EDIT usando GetDlgiten{ }, el cual funeiona
ion ep 1as vontanas regulares como er los didlogos y 1a llamada SotWindowos( | para mover y ajustar o
tapano para lienar todo el érea cliente. ver su pucsto, puades cambiar los valores pasades on SetMindow?os{ }
pare hacer algo como sélo Ilenar In mitad del alto de le ventana, dejando liare la parte inferior pare ubicar
otros controles.
Crear otros controles en tiempo de ejecucién
No voy a dar ejerplos de cono crear dindnicamente los otros controles, cone Listas, botones, etc... debide a
que eg basicanente lo misno. $i visitas 10s links anteriores en MSDN o buscas en ta referencia de Win32 APL,
encotraras toda la informacién necesaria para crear cualquiera de Lo% otros controles estandar:
Vorenos mas sohze esto con les controles comnes, on 1a siguiontes secciones, donde obtendras mas prictica.
‘eprright © 1998-0605, Brock Miles Ghataranr). ALL right csverwed
hnapaivingrog.orgfutrialleslapp_one Hindssra2016 Aalicacén Pare 2: Uso de Archives yDidlogos Comins
[ contenidos | #winproa }
Aplicacién Parte 2: Uso de archivos y Dialogos
Comunes
Ejemplo: app_two
Didlogos Comunes para archivos
El primer paso para abrir 0 guardar archivos es obtener el
nombre del archivo a usar... aunque, simplemente
podriamos poner el nombre del archivo en el cédigo de
nuestro programa, pero honestamente esto no es muy Util
en le mayoria de los programas.
Debido a que esto es una tarea bastante comin, existen
didlogos predefinidos por el sistema que pueden ser usados
Para permitir al usuario seleccionar el nombre de un archivo.
Los didlogos mas comunes, para abrir y para guardar
archivos, son accedidos a través de cetopenFileName( )
and GetSaveFiteName( ) respectivamente, ambos de los
cuales utilizan una estructura 01 JENANE,
OPENFILENAME ofn;
char s2FileName|[MAX_PATH] = "
ZeroMenory(sofn, sizeof (ofn));
StructSize = sizeof(ofn); // Mira la nota a continuacién
ofn.hundOwner = hwnd;
ofn.IpstrFilter = "Text Files (*.txt)\0*.tat\OAll Files (*.*)\o*.*\o"
pstrfile = szPileName;
xFile = MAX_PATE;
:Flags = OFN_EXPLORSR | OFN_FITEMUSTEXTST | OFN_STOERE
pstrDefixt = "txt";
-RDONTY;
(GetopenFileName (sofn))
// do something usefull with the filename stored in szPileName
Observa que en a estructura llamamos a zeroveory( ) para inicializarla a 0. Esto es generalmente una
préctica prudente, dado que muchas APIs suelen asignar los campos que no utilizamos con nuit. y de esta
forma no necesitamos asignar cada rriebro que no utilizarros.
Puedes encontrar facilmente el significado de cada uno de los campos de la estructura en la
documentacién, El campo IpstrFilter apunta a un string doblemente terminado en Nutt, puedes ver en el
ejemplo que hay varios "\o" a través de éste, incluyendo uno al final... el compilador agregaré el segundo
al final, como siempre lo hace con los strings constantes (esto es por lo que generalmente no necesitas
ponertos tu mismo). Los wwii en este string lo dividen en fitros, donde cada uno tiene dos partes. El
primer filtro tiene la descripcién "rext Files (*.txt)", la parte con la especificacién (*.txt) no es
necesaria aqui. La segunda parte es la especificacién real del primer filtro, "*.txc". Hacemos lo rrismo con
el segundo filtro, excepto que es un filtro genérico para todos los archivos. Podemos agregar tantos filtros
como deseemos,
El campo Ipst File apunta al buffer que hemos asignado pare almacenar el nombre del archivo, debido a
que [a longitud del nombre del archivo no puede ser mayor que waxPars, éste es el valor que escogeremos
para el tamafio de dicho biiffer.
Los flags indican que el didlogo sélo debe permitir al usuario ingresar nombres de archivos que ya existen
(esto es porque queremos abririos, no crearlos) y para ocultar la opcién de abririos en modo de séio lecture,
opcién que no queremos prover, Finalmente proveemos una extensién por default, por lo tanto si el usuario
hrapaivingrog.orgutcrialleslapp two nar 1ssra2016 Aalicacén Pare 2: Uso de Archives yDidlogos Comins
tipea "zoo" y el archivo no es encontrado, intentaremps abrir "too.
Cuando seleccionamos un archivo pare guardar en lugar de abrir, el cédigo es bastante parecido, excepto
que llamamos a GetSaveFile( ) y ademés necesitamos cambiar los flags para poner opciones rras
apropiadas al guardar archivos.
ofm.Flags = OFN EXPLORER | OFW_PATI
TEXIST | OFN_HTDI
‘ADONLY | OFN_Ov
-RARTTEPROMPT;
En este caso no necesitamos que el archivo ex’sta, pero si necesitamos que el directorio exista debido a
que no vamos a crearlo sino existe. Ademis, si el archivo existe, preguntas al usuario si est seguro
que desea sobreescribirlo,
NOTA: MSDN establece lo siguiente para el campo 1structsize:
Istructsize
Especifica la longitud de la estructura en bytes.
Windows NT 4.0: En una aplicacién que es compilada con WINVER y _WIN32_WINNT >= 0x0500,
usar para este carpo el valor OPENFILENAME_SIZE_VERSION_ 400.
Windows 2000/X:
Usar para este parémetro el valor OPENFILENAME,
Basicamente, esto significa que, como Windows 2000 ha agregado algunos miembros a esta estructura, su
tamafio ha cambiado. Si el cédigo anterior no funciona, posiblemente sea porque probablemente no
coincidan el tamafio que tu compilador usa y el tamafio que tu sistema operative espera (Windows 98,
Windows NT4), por lo tanto la llamada falla. Si esto sucede, intenta usar
OPENPILENAME. S126 TON_400 en lugar de sizeot (ofn). Gracias a las personas que me han notificado
esto.
Lectura y Escritura de Archivos
En windows tenemos algunas opcciones para especificar de que manera querermos acceder a los archivos.
Podemos usar la vieja libreria ic.h open()/read() /uzite (), 0 podemos usar la libreria stdio.h
fopen ()/fread()/furite(), y por Ultimo si usamos C++ podemos usar iostreams.
Sin embargo, en Windows, todos éstos métodos finalmente llaman a las funciones de la API Win32, las
cuales usaremos aqui. Si estés utilizando otro método para la entrada/salida de archivos, entonces te
resultard facil entender el método que vamos a ver.
Para abrir archivos, podemos usar openrile( ) 0 CreateFile( ). MS recomienda usar solamente
Creaverile( } debido a que OpenFile( ) es “obsoleta”. Createrle( } es una funciém mucho més
versatil y provee un gran control sobre la forma en que abrimos nuestros archivos.
Lectura
Digamos, por ejemplo, que le has permitido al usuario seleccionar un archivo usando Get opentileName (
Dew
BOOL LoadText#ileTordit (HAND hEdit, LPCTSTR pszFileName)
HANDLE hPile;
BOOL Success = FALSE;
atePile(pszFileName, GENERIC READ, FILE SHARE READ, NULL,
EXISTING, 0, NULL);
t= INVALID HANDLE VALUE)
DAORD dwFileSize;
WEilesize = GetFilesize(hFile, NULL);
ize != OxFPFFEEFF)
Lf (dw?le:
STR ps2Filevex
psz¥ilevext = GlobalAlloc(GPIR, dwFilesize + 1);
£(psz¥ilefext != NULL)
hrapaivingrog.orgutcrialleslapp two nar 218ssra2016 Aalicacén Pare 2: Uso de Archives yDidlogos Comins
ORD dwRead:
£(ReadFile(hFile, pszFileText, dwFileSize, gdwRead, NULL))
pa2FileText [dwFilesize] = 0; // Add null terminator
£(SetWindowText (nEdit, ps2FileText))
bSuccess = TRUE; // funcioné!
GlobalFree (pszFilevText) ;
CloseHandle (hFi le);
return bSuccess;
Hay una funcién completa para leer un archivo de texto dentro de un control edit. Esta funcién, toma como
parémetro el handle al control edit y el nombre del archivo a leer. Ademis esta particular funcién tiene un
buen chequeo de errores, debido a que la entrada/salida de archivos esté expuesta a una gran cantidad de
ellos y por lo tanto, necesitamos chequearios
Nota: No usaremos le variable dwRead, excepto como prémetro en ReadFile( ), Este parémetro DEBE ser
provisto, de no ser asi la llamada fallaré.
En [a llamada a creat:
© ), GENERTC_READ significa que queremos acceso de sélo lectura
HARE_READ significa que otros programas pueden abrir el archivo al mismo tiempo que nosotros lo
hacemos, pero sélo si quieren leerlo, no queremos que escriban en el archivo mientras estamos leyendo. Por
Ultimo, OPN_FXTSTING significa que sélo abriremos el archivo si existe, no querermos crearlo.
Una vez que hemos abierto el archivo y hemos chequeado si createFile( ) se ejecuté con éxito,
chequeamos el tarrafio del archivo para averiguar cuanta memoria necesitaros reservar para poder leer el
archivo entero. Cuando reservamos dicha memoria, chequearms para aseguramos que se hizo con éxito, y
luego llamamos a ReadFile( } para cargar el contenido del disco dentro del buffer en memoria. Las
funciones de la API pare archivos no entienden sobre Archivos de Texto, por lo tanto no proveen
mecanismos para leer una linea de texto, o agregar terminadores wu1., al final de nuestros strings. Esta es
la razén por la que heros asignado un byte extra, después de leer el archivo agregamws el terminador nu=1,
para que luego podamos pasar el buffer de memoria como si fuera un string a SetWindowsText ( )
Una vez que todo esto se ha ejecutado con éxito, ponemps en nuestra variable booleana bSucces (que
indica si la operacién se ha ejecutado con éxito) el valor TRUE. Por Uiltimo, liberamos el buffer de memoria y
cerramnos el archivo.
Escritura
BOOL SaveText#ileProm@dit (HWND hEdit, LPCTSTR pszFileName)
ANDLE hFile;
cess = FALSE;
reate¥ile(pszFileName, GENERIC WRITE, 0, NULL,
CREATE_ALWAYS, FILE ATTRIBUTE NORMAL, NULL);
£(nFile != INVALID_HANDLE VALUE)
DWORD dwextLength;
GwlextLength - GetWindowvextuength (hedit) ;
// No need to other if the:
£ (dwTextLength
e's no te
LPSTR pszText;
WORD dwBufferSize = dwrextLength + 1;
psztext = ClobalAlloc(GPYR, dwaufferSize);
Af(ps2ext != NULL)
hrapaivingrog.orgutcrialleslapp two nar 3ssra2016 Aalicacén Pare 2: Uso de Archives yDidlogos Comins
F(GetWindowText (hFdit, pszText, dwauffersize))
DWORD dwWiritten;
F(Writerile(hFile, pszText, dwTexthength, sdwiritten,
Success = TRU
Global Free (psztext) :
Closeflandle (nFi le)
return bsuccess;
Es muy similar a leer archivos, pero con algunos cambios. Primero que todo, cuando llamamos a
as#ile( } especificamos que queremos acceso de lectura (Read), que el archivo deberia simpre ser
creado nuevamente (y si existe deberia ser borrado cuando es abierto) y si no existe que sea creado con
los atributos de los archivos normales.
A continuacién obtenemos del control edit, la longitud necesaria del buffer de memoria, debido a que éste
es la fuente de los datos. Una vez que hemos asignado la memoria, solicitarnos el string al control edit
usando GetWindowText( ) y luego lo escribimos al achivo con writerile( ), Nuevamnete, al igual que con
Read#ile( ), eS necesario el parémetro que retorna cuanto fué escrito, alin cuando no Io users.
Copyright © 1998-2003, Brook Miles (theForaer). All rights reserved.
Versién en Espafiol: FedericoPizarro - 2003
hrapaivingrog.orgutcrialleslapp two narssra2016 Aalicaién Pare 3: Barras de Herrarientasy Barras de Estado
[ contenidos | #winprog ]
Aplicaci6n Parte 3: Barras de Herramientas y
Barras de Estado
Ejemplo: app_three
Una Palabra IMPORTANTE en los Controles =o) x)
Comunes Bie
Al igual que con todos los controles comunes, debemos Lila ll
llamar a InitCommonControl ANTES de intentar usarlos. Tis document dines the Olen Potoca and assunes (a
Necesitaremos #include para poder usar | readeis amar with the IFC Aichtectue (RCARCH).
esta funcién y para obtener la funciones y declaraciones
necesarias para el uso de los controles comunes. También
Table of Conterts
necesitaremos agregar comet132.1ib en la configuracién | +. Label
de nuestro enlazador (linker), si no se encuentra 1.7 Sewers
configurado de esa manera, Observa que ‘
InisConronControls( ) e$ una vieja APL, para obtener 35 eae
mes control poderms usar InitCommonControlsx( ) la
cual es requerida por los més recientes controles comunes. Sin embargo, debido a que no voy a usar
guna de las caracteristicas avanzadas, InitCommonControls( ) seré adecuado y mas simple.
Barras de Herramientas
Podemos crear una barra de herramientas usando createvoolbart
necesitamos hacer es crear la barra de herrarientas..
pero no es la idea.
Lo que
Tool = CreateWindowEx (0, TOOLBARCLASSNAME, NULL, WS_CKT
hwnd, (HMENU)IDC_MAIN_TOOL, GetModuleHandle (NULL), Mi
Esto es bastante simple, POOLBARCLASSNAME es una constante definida por los encabezados (headers) de
los controles comunes. hwnc es la ventana padre, la ventana en la que aparecerd la barra de
herramientas, 10¢ MAIN YooL es un identificador que podemns usar posteriormente para obtener el sw
de la barra de herramientas por medio de GetDigtcem( )
the 7B BUTTONSTRUCTSIZE mess:
ward compatibility
ssage(hTool, T3_BUTTONS'
which is require
RUCTSIZS, (WPARAY
sizeof (TBBUTTON), 0);
Este mensaje es necesario para permitirle al sistema darse cuenta que versién de la libreria de controles
comunes estamos usando. Debido a que nuevas versiones agregan cédigo a la estructura, dando el
tamafio de la misma el sistema puede darse cuenta que comportamiento estamos esperando.
Botones de la barra de Herramientas
Los botones con bitmaps para las barras de herramientas vienen en dos variedades, los botones estandar
que son provistos por comct!32 y los botones definidos por el usuario, que son creados por él mismo.
NOTA: Los botones y bitmaps son agregados a las barras de herramientas en forma separada... primero
agregamos una lista de imagenes a usar y luego agregames la lista de botones. Por tltimo especificamos
que imagen usar en cada botén.
Agregado de Botones Estandar
Ahora que hemos creado una barra de herremientas, necesitamos agregarle algunos botones. Los bitmaps
mes comunes estan disponibles dentro de Ie libreria de los controles communes, por lo tanto no necesitarnos
crearlos 0 agregarlos en cada archivo .exe que los usa.
Primero declaramos TSBUTTON y TBADDSITMAP
hnipaivingrog.orgfutorialeslapp tree hind 1ssra2016 Aalicaién Pare 3: Barras de Herrarientasy Barras de Estado
315
TRADDBITMA? tbabs
y luego agregamos los los bitmaps estandar a la barra de herrarrientas usando la lista de imagenes en la
libreria de los controles comunes..
thab.hinst = HINST COMMCTRL;
tbab.nID = IDB_STD SMALE COLOR;
SendMessage (hTeol, TB ADDBITMAP, 0, (LPARAM) éUbab);
Ahora que hemos cargado nuestras imagenes, podemos agregar algunos botones que las utilicen...
sizeof (tbs) );
STD _PILENEW?
TBSTATE ENABLED;
TSSTYLE_BUTTON;
= 1D_STLE_NEW;
EOPEN;
TBSTATE_ENABLED;
TRSTYLE_BUTTON;
= ID PILE OPEN;
tbb (2
thb 12 TATE_ENASLED;
thb {2 TYLE_BUTTO!
tbb(2 TD_PILB_SAVEAS;
SendMessage(hTool, TB_ADDBUTTONS, sizeof (tbb) /sizeof(TBSUTTON), (LPARAM) «tbb) ;
‘Aqui hemos agregado los botones New, Open y Save As usando las imégenes esténdar, lo cual es una
buene idea debido 2 que las personas suelen verlas y ya saben que significan,
Los indices de las lista de imagenes estan definidos en los encabezados de los controles comunes y
también son listados en MSDN.
;, etc...) lo cual es idéntico a los IDs de los items
Hemos asignado a cada botén un ID (1:
equivalentes en el Mend, Estos botones generardn mensajes w™_covMAND identicos a los del Mend, por lo
tanto no se requiere algtin procesamiento extra! Si fueramos a agregar un botén para un comando que no
aparece en el Menii, simplemente definimos un nuevo ID y agregamos un manejador a ws COMMAND.
Si te estas preguntando por qué he pasado wParam a TB_ADDEUTTONS, bueno, esté realizando un céleulo
del nimero de botones en el arreglo tbb para que no necesitermos especificar un valor. Si en lugar de esto,
lo que hacemos es poner un 3, bien, estar‘a correcto pero que sucede si queremps agregar otros
botones... deberiamos cambiar este numero por 4 y en prograrmacién eso no es muy bueno... queremos
que los cambios provoquen la menor cantidad de cambios. Por ejemplo, si el tamefio de ‘ssuroN tiene 16
bits, debido a que tenemos 3 botones el tamafio del tbb deberia ser de 16*3 = 48, Del mismo modo, 48/16
da el ntimero de botones, 3 en este caso.
Barras de Estado
Algo que se suele encontrar a menudo en las aplicaciones son las barras de estado, la pequefia barra en la
parte inferior de la ventana que muestra informacién. Son muy facil de usar, solo hay que crearlas...
hStatus = CreateWindowkx(0, STATUSCLA
Ws_c WS_VISTBLE | SBARS,
(SMENU) TDC_MAIN_STAT(
IAME, NULL,
STZEGRIP, 0, 0, 0, 0,
tModule#andle (NULL), NULL) +
Y luego (opcionalmente) especificamos el numero de secciones que deseamos. Si no especificamos
ninguna, simplemente tendré una que usa el ancho entero de la barra, Podermos especificar o recuperar el
texto usando Setivindow?ext ( ) como con lo haciamos con muchos de los otros controles. Si
especificamos mis de una seccién, necesitamos especificar el ancho de cada una y luego usar
58_SETTEXT para especificar el texto de cada seccién.
hnipaivingrog.orgfutorialeslapp tree hind 28ssri22016 Aalcacin Parte 3: Barras de Herravintas yBarras de Estado
Para definir los anchos decleramos un arreglo de enteros donde cada valor es el ancho en pixeles de una
seccién. Si queremos que una seccién use el espacio restante fijamos corp valor ~
El wParam nuevamente es para calcular cuantos elementos hay en el arreglo. Una vez que hemos
terminado de agregar secciones, fijamos el primer valor (de indice 0) para verla en accién.
Tamaijio Apropiado
A diferencia de los Mendes, las barras de estado y de herramientas son controles separados que viven
dentro del drea cliente de la ventana padre. Por lo tanto si solo dejamos nuestro cédigo w"_stz= anterior,
se van a solapar con el control edit que hemos agregado en los ejemplos anteriores, En wi_S125, ponemos
las barras de estado y de herrarrientas en posicién y luego restarms el alto del area cliente pare que
podamos mover nuestro control edit para rellenar el espacio restante...
EWND heol;
RECT reTool;
int iToolHeigh!
EWND hstatu:
RECT reStatus;
int istatusteight;
FWND hEdit;
int iBditHeigh’
RECT reClient;
// Size toolbar and get height
hiool = GetDigitem(hwnd, 19¢_MAIN TOOL);
SendMessage(hTool, TB_AUTOSTZE, 0, 0);
GetWindowRect (
el, ereTool) 5
tops
// Size status bar and get heis
GetDigitem(hwnd, ID N_STATUS) ;
WM_sIZE, 0, 0)7
GotWindowRect (hStatus, areStatus) 7
istatusHeignt = reStatus.nottom - rcStatus.top;
// Calculate remaining height and size edit
GetClientRect (hwnd, éreClient)
indi tei Client bottom ~ iToolHeight - istatusHeight;
hEdit = GetDigitem(hwad, 1D¢ MAIN EDIT);
etWindowPos (hEdit, NULL, 0, iTool#eight, reClient.right, iEditieight, SWE _NOZORDEA) ;
Desafortunadamente esta es una porcién de cédigo bastante larga, pero es simple... las barras de
herramiermtas se autoposicionarén cuando enviemos el mensaje "3 _AuYoSrz# y las barras de estado
haran |o mismo cuando envieros w_s12=. (las librerias de los controles comunes no entienden nada a
cerca de consistencia)
Copyright © 1998-2003, Brook Miles (theForaer), All rights reserved.
Versién en Espafiol: Federico Pizarro - 2003
hnipaivingrog.orgfutorialeslapp tree hind 38ser22016 Aplicacén Pare 4: Interfaces con Miltiples Documentos
[Loontenidas | swinprog }
Aplicacion Parte 4: Interfaces con Multiples Documentos
Ejerrplo: app_four
Ses sioid
Beit eon |
Introduccién a IMD
Primero un poco de repaso... Todas las Ventanas tlenen un Area lente, aqui es donde la
mayoria de los programas dibujan imagenes, ubicen los controles, etc... el Aree Cliente no est
‘separada de la ventana, simplemente es una peque'ia regién especializada de ls ventana, A
veces una ventana puede ser todo el area cliente, y veces nada, 2 veces el area cliente puede
hacerse ras pequefia par caber en rents, titules, barras de desplazamiento, etc.
En términos de IMD (en Inglés, MDI - Multiple Document Interface), nuestra ventana principal es
llamada Frame y ésta es probablemente la Unica ventana que podrlames tener en un programa
JUD (Interface con un Unico Documento), En IMD hay une ventena adicional mada Ventana
lente IMD, la cual es hja de nuestro Frame y a diferencia del Area Ciente, es una ventana
‘completa y separada de las derrés que tiene un érea clente y provablemente algunos pixeles
para un borde. Nunca procesaremos mensajes directamente cel Cliente IMD, esto es hecho por
fa clase ventana pre-definida "wi_cl 4". Poderres comunicamos y ranipular el érea cliente IMD, com asf también ls ventanas que
‘sta contiene, através de mensajes.
Cuando entrerms @ la ventana que muestra nuestro documento, o fo que sea que muestre el programa, enviaros un mensaje al Clente
IMD para decire que cree una nueva ventana del tipo que hemos especificado, La nueva ventana es creada com una ventana hija del
Cliente IMD, no de nuestro Frame Window. Esta nueva ventana es una hija TMD (IMD Chi). La hije IMD Child es hija del Cliente IMD, el
‘cual a su vez, es hijo del Frame IMD... La Hija TMD pronablemente tenga sus propias ventanas hijas, por ejemplo el cantral edit en el
program del ejemplo de esta seccién
Tenemos que escribir dos (0 mas) Window Procedures. Uno, corro siempre, para nuestra ventana principal (el Frame) y uno ms pare le
bia IMD. Poderos tener también rras de un tipo de hia, en cuya caso escriiremes window procedures separados para cada tipo.
Site he confundide hablando de Clientes IMD y todo eso, quizés este clagrame puede ayudarte a aclarar un poco las cosas.
Ble Eck Window
Ble a wisoyy
client AREA
Complete MDI window *
the client AREA ie covered up by the MDI
Client WINDOW
window MI FRAME Window
Eu
MOI CLIEND Window
J-- Mor CHILD Window
Ahora si, IMD
{IMD requlere algunos cambios astutos a lo largo de un prograrra, por lo tanto lee esta seccién culdadosamente... situ programma no
funciona o tlene un comportariento extrafio es porque erraste alguna de las alteraciones que vamos @ hacer al programma regular.
Ventana Cliente IMD
‘Antes de que creerros nuestra ventana IMD necesitamos hacer un cambio al procesariento por default que utlizamros en nuestro
‘Window Procedure... debido a que estaips creando un Frame que residré en un Clente IMD, necesitarres cambiar fa lairada
DetiindomPtoc( } 8 DazPeane2vect( ) la Cual agrega un procesamiento especialzado de mensajes para Frames,
return Defframevroc(hend, 9 RMDICIient, meg, wlaram, LParam):
Fi présimo paso es crear la ventana Clente IMD, como una hija de nuestro Frame. Fsto lo hacemos en el av cREANE, como antes.
hnapaivingrog.orgucriaeslapp four 18ser22016 Aplicacén Pare 4: Interfaces con Miltiples Documentos
CLIENDEREATESTRUCT cc
con.bwindontions = GetSubyons (GetMem (hwnd) » 207
com id aretChild ~ TD_NDz_PIRSTCHILD?
UNWS_CUELD | WS_CLIFCUCUOREN | WS_VSCROLL | 5_HSCROLL | WS_VI
had, REND) 19CSAIN_MDT, Gotwoduletandle (WLZ), {LEVOID) Sece)
El menu handle es el handle al meni desplegable en el que ol Cente IMD agregars items para representar cada ventana que es creada,
permitiéndole al usuario elegir desde el mend la ventana que quiere activar. Por lo tanto, agregaremos funcionalidad para manejar este
caso. En este ejemplo, es [a tercera (de indice 2) debido 2 que la he agregade al Mend después de Fle, Edt y Window.
ccs. icirstcrile es el nmero para usar comm el primer 1D para ls items que el Clente agrega @ la Ventana meni. quererros que
‘esto sea facilmente distinguible de nuestros ident ficadores de mendes para que podamas procesar los comandos del meni y pasarlos
desde la ventana @ derPrsmeP=oc( } para que los procese. En el ejemplo he especticado un ientificader definide coma 50000, que es
lo suficlentemente grence para que ninguno de les Identiicadares de los comandos del mend sean mayor que éste.
‘Ahora para que este mend funcione apropiadamente, necesitamos agregar alain pracesarriento especial a nuestro manejadar del
mensaje my commas:
svSEch (LOWORD (wParanl
PostNescage (nund, WM CLOSE, 0, 0) +
1 ve handle other regular 19s
{1 Aianele MOT Window commands
t
Ae (uonoao(wearae) >> 1D MoT PrasrcurLDy
DesPzanePsoe tnwad, 9 AMDICLLeat, eg, wParam, Laren) ;
1
else
i
UND Hoh Id = [HWND) SoneNessage(g_HMDICHient, HM_MOIGETACTIVE, 0,0)
SF (ache)
‘
Sendiessage(nChild, WL
SBAND, wParam, 1Paran 2
,
brea
He agregado en el cave un caso mis, el cerasit:, el cual atrapard todos los comandos que no proceserrns directamente y realzard un
‘chequeo para ver siel valor es mayor o igual a 19. sos_FznszcHrL9, Silo es, entonces el usuario ha clickeado en uno de los items del
rend de la ventana y enviamos el mensaje a Def ranwPreci ) para que lo process,
‘Sino es un alguno de Jos IDs de Ia ventana, entoces obtenerms el handle de la ventana hijo que esté activa y fe envianmos el mensaje
para que lo procese. Esto nos permite delegar la responsabliéad de realizar clertas acciones a la ventana Hija y nos permite que
‘diferentes ventanas Hjas procesen corrancos de ciferentes formas, si asi se desea. Fn el ejerpln, en el Frame window procedure solo
proceso los comandos que son glabales al prograrra y envio los corrandes que afectan a clerto documento o una ventana Hija hacia
‘icha ventana Hija para que los procese.
‘También necesitamos modificar un poco nuestro Loop de Mensajes,
hide (Gettessage (seg, KULL, 0, 09)
‘
Af ("ranslaceuDisysaccel (g huDIClient, ssa)
qi
cMessage (aMsa) 5
skessage (5883) 7
Teanslat
Hemos agregado un paso extra (rranslatemo:sysnceel ()), que chequea por el acelerador de teclas pre-defindo, Ctrl+F6 cambia 2 la
Siguiente ventana, Ctrl1F4 ciera [a ventana Hijo, etc... Si no quieres agregar este cheque impedirés a los usuarios de usar a
Ccomportarianto estindar que estn acostumbrados 2 usar, o tendrés que implerentarlo manualmante.
Clase Ventana Hija
‘Aderrés de Ia ventana principal de un progreme (el Frame), necesitarms crear una nueva clase ventana para caéa tipo de ventana hija
‘que necesitamns. Por ejemplo, poderos tener una que muestre texto y otre para mostrar gréficos 0 fotos. En este ejemplo solo
Ccrearerms un tipo de hijo, el Cual serd coma el ecitar de los ejemplos anteriores.
00L SetupMDIChiLaWindowClass (HINSTANCE hinstance)
hnapaivingrog.orgucriaeslapp fourssr22016 Aplicacén Pare 4: Interfaces con Miltiples Documentos
wevensize = aizeor rmnpecassex):
ke = CS_HREDRAW | c3_VREDRAW:
we. apévn! = mpicnsiasna?cocs
te 0
wen 0
welnintance binetances
welarcon, = Loaaicon Nii, ID1_APPLICR
welncurss = Tonacuraor (Jit, 19C_ARROW):
NelnbrBackssound * (ESAUSI) (COLOR SOFACES2)2
NelipssteniNane > NULL:
we.ipsuClassilane = g_szchitdclasstiane;
wevniconse © deacon ince, 151 APPL:
AE (/RegistarClasstx (gee) }
‘
Messagoox(0, "Could Nor Register Child Window
HR_TCONEXCLAMATION | ¥8_OK)7
oh ob
ele
:
Esto es basicamente idéntico a resistrar nuestro Frame, no hay flags particularmente especiales para usar con IMD. Deberos poner
nuestro meni en NULL y el window procedure apuntando al window procedure ce la ventana hija que escribremas a continuacién,
IMD Procedure Hijo
Fl window procedure para una IMD Hija es parecido a culaquier otro pero con algunas excenciones, Primero de todo, los mensajes por
‘default, en lugar de ser pasacos a be-Wincow?roc| ), Son pasacos a De-W9IChsIdProct )
En este caso particular, también queremps ceshabiltar los mendes Edt y Window cuando no se necesitan (sélo porque es bueno
hacerlo}, por lo tanto procesando el mensaje am_NoracTrvxT# lag hablitares o Ceshablltamos depenciendo si fa ventana esté activa 0
ra. Si tenems mrikiples tipos de ventana ho, aqui es donde podemos poner el cécige para cambiar completamante el meni, la barra de
herramentas 0 hacer alteraciones a otros aspectos del programe para reflejar las acciones y commandos que son espectficos del tioo de
ventana que esté siendo activada,
Para ser ain mas completes, poderros deshabiltar los tems Close y Open del ment, debido @ que no serén de utlidad cuando las
ventanas no estén activas. He deshablitado todos estos items por default en el recurso, por lo tanto no necestamns agregar cédigo
‘extra para hacer esto cuando la aplcacién se ejecuta por primera vez.
IAESULT CALLEACK MOICRiIdWndProe (HMD tnd, UINT ang, NPARAM wParam, LEARAK LParen}
t
eesten (neg)
‘
care we
FONT nébefacite
ARND nEaits|
11 Creams @1 control Fait.
DEAL ~ Createwindowie WS 2X CLIBNTEDSE, "EDIT™, ™,
SCHILD | RE_VESIBGE | WS_VSCROLL | 45 SCROLL’ | ES MULCZLINE | ES _AUTOVSCROLL | 5 _AUTOH
2p 9) 100, 200, had, (ENGHIC} 10¢_CHELD EDIT, CetModuletiandle INULL), NULL +
sstagaie’ = seLL)
MessageBex(hund, "Could aot exeace eaLt nox.", “EEEoe", MB OK | MB LCONESROR) »
hfvefault = Goestockobjoct {UBPAULT GUI_FONT) ;
Sendonsage (hEdit, AM_SETFONT, (WPARAM) REDefauit, MAKELPARAM(PALSE, 07) 1
UINT Enableflag
Inuenss = Getttont (g_niiadnWindow) 2
Ef (mre == (EHND) Tearan)
(eres eats active, activanos les senvés
'
‘ Feuande se des
Enableflay = ME_GRAYED;
iva, desactivanes Lose:
'
Enabloteralten(htiony, 1, MP BYPOSITION | HnabieFlaq!
EnabloMorazten (heen, 2, MP_BYROSITION | EnabloPiag!
hnapaivingrog.orgucriaeslapp four 38,ser22016 Aplicacén Pare 4: Interfaces con Miltiples Documentos
nbLlenens = Geesubitens thtiensy 0):
EnableMerurten(heiieMena, ID’PTLS SAVEAS, M®_SYCOMMAND | rab:
a9):
EnabloMenutte(hEiteMony, ID FILE CLOSE, HE_SYCOMMAND | EnabLeElas) +
Enabloeruiten(heileMona, ID Pike CLOSEALL, MF BYCONMAKD | Snabletlag)s
>
‘41 Een (LOWORD (wParae) )
‘DoF Leopan then #
aki
1o_FILE_SAVEAS:
DoPiTesae na) 2
sendbigitentessage (hwnd,
___sendblgitenttessage {hzdy
ak
ase, _€D°7_PASTE
sendDigitont
{ELD_EDIT, WM_CUT, 0, 0)
) EDIT, WM_COPY, 0, 0);
jethwnd, IDG_CHILD_BDIT, WM_PASTE, 0, 0)
'
breaks
ane
RECH vecisanty
Gotctientiect end, grectient)?
nbate = GetDigtees (hwnd, 19C_cHILD_EDr7):
SetiiiadowPes (heat, MULL, 0,0, reCiient right, reClient.bettor, SWP_NOZORDER) 7
return DefMDiChiléProcthnd, msg, wParam, LParan);
default:
return DsfMDIChitderoe thund, msg, wParam, Param};
>
,
be implementado como comandos File Open y Save, DoFiiecpen( ) y DeFteSavet ) son igual que en los ejemplos anteriores con el 1D
del control edit cambiado y acicionalmente he puesto como titulo de la Ventana Hija IMD el nombre del archivo.
Los cotrandos edit son féciles, deblda a que el control elt est desarrllado pare soportarles, sélo le decitras que hacer.
zRecuerdas que he mencionado que hay algunas cosas que necesitas recordar 0 tu aplicacién se comportard de manera extraha?
‘oserva que he lamado Det¥D~Ch* 1a S128, esto es importante porque de no ser asi, el sisters no tendré Ia
chance de hacer su propio procesamiento sobre el mensaje. Puedes buscar sobre Derwdichsid?roc( } en MSDN para encontrar una
Ista sobre los mensajes que procesa y siempre asegurarte de pasdrselos
Crear y Destruir Ventanas
Las ventanas Mijas IMD nos son creadas directarrente, al contrero, le enviamos a la ventana cliente el mensaje i MorcREATE
diciéndole que tipo de ventana queremos crear relenando los miembros de une estructura NozcREAT==7RUCT, Puedes ver los dstintos
rrierores de ésta estructura en la documentacién. El valor de retorno del mensaje WY_NDICREATE es el handle a fa queva ventana.
HWND CreateNewVDTChi1E HNO RYDTCLientd
‘
WND henilds .
tle = *[uaticied:
fg s2chi loci aseNane;
Getwodvietiandle (NULL)?
x = CH_USEDEEAULTS
child = (Wip) SendMeceage (MDICLLent, M_MDICI
secinensLa)
‘
ERTE, 0, (LONG) sxe9)
Meseagston (DNOICIsent, “MDI Chile ereazion fatled.", "oh Oh...)
ME_TCONEXCLAMATION | YOK);
hnapaivingrog.orgucriaeslapp four 45ser22016 Aplicacén Pare 4: Interfaces con Miltiples Documentos
{que no usemes pero puede ser muy
Iles el 1vazn, Este puede ser usado para enviar
‘cualquler valor de 32 bits (un puntero) a la ventana hija que estés creando con el propésito de proveerle cualquier informacion. En el
rranejador my cReaTE de nuestre ventana hie, el valor Peram pare el mensaje wx Cs+aTt apuntard a la estructura MDICK=AT#S"RICT
El micmbro ipCroateParans de dicha estructura apuntard a MSICASATESRUCT que enviares junto con WH_MOICREATS. Pore tanto para
‘acceder al valor param de la ventana hija necesitarms hacer algo comp esto en el window procedure de dicha ventana.
onee
CREATESTRUCT® poreazestruct;
MDICREATESTRUCT* puDICreates
poreatestruct = (CREATESTRUCTS) 1Parem
puOrCreateSLruct = (MDICREATESTRUCT# | pCreateStrust~plpCreateParansz
/
phbrcveateStruct new points to the sane MOICREATESTRUCT that you
int abong with the. WR MODCREATS
fees the lParam.
‘nessage and you can use it
)
break
Sino quieres complcarte con esos dos punters extras, puedes acceder a [Param en un solo paso de la siguiente manera:
(ISDICREAT2STAUCT®) { (CRENTESTAUCT®) 1Paran) =>ipCreateParama) =>" Param
‘Nnora pode-ras implerentar os comandos File del men en nuestro Frame window procedure:
‘CreateNewtDIGhild(g RMDICLient) 5
breaks
cane 10,
HiND NGniLd ~ CeeateNewDIChildtg HMDICLient) z
ar(ochita)
fi
oFLLeopen(nchaial
break
cate 10,
chose:
Find Noni Ne = (HNO) SendMassage(g_ ANDTCLient, WH_MOTGSTACTIVE, 0, 0)
ar(ochita)
fi
SenaMessage (RCni2d, ME CLOSE, Oy 0)
bres
También podemos proveer alain pracesarriento IMD por default al ordenarriente de la ventana para nuestro Window Ment, debide a que
IMD soporta esto, no es mucha trabajo.
Sendvessage {g AMDICIsens, WH MOITILE, 0, 017
SendMessega {a AMDICLLent, WH MDICASCADE, 0, 0);
breaks
Copyright © 1998-2003, Brook Miles ({heForass). All rights reserved
Versiin en Espafil: Federico Pizarro ~ 2003
hnapaivingrog.orgucriaeslapp fourssra2016 Bitmans Transparertes
[ contenidos *| #winprog ]
Bitmaps Transparentes
Ejerplo: bmp_two
Transparencia =1D1x)
Darles a los bitraps la apariencia de tener secciones transparentes es
bastante simple e involucra el uso de una Mascara blanca y negra junto al
color de la imagen que querermos que luzca transparente.
Para que el efecto funcione apropiadamente se necesitan reunir las ®@
siguientes condiciones: Primero la imagen debe ser de color negro en
todas las areas que queremos que luzca transparente. Segundo, La
mascara debe ser blanca en las dreas que queremos que sean
transparentes y negro en caso contrario. La Imagen y la méscara son mostradas en la figura incluida como
ejerrplo en esta seccién y corresponden a las dos imégenes que se encuentran alineadas mas a la izquierda.
Operaciones BitBIt
éComo hace esto para proveernos transparencla? Primero usarmos Bit#1t( ) sobre la mascara usando la
operacién sRCAND como Ultimo parémetro y luego encima de esto usamos 8itBic( } sobre la imagen usando le
operacion skceaiN?. El resultado es que las dreas que queremos que sean transparentes no cambian en el
destino mientras el resto de la imagen es dibujada de forma usual.
Selectobject (hdcMem, g_hbmiask) ;
BieBlt(hde, 0, 0, bm.bmiwidth, bm.baileight, o, -AND) 5
Lectok
BisBlt (hdc, 0,
g_hbnBall} ;
int, bm.bnitidth, bm.brieig
| hdetem, 0, 7;
Bastante simple eh? Afortunadamente lo es, pero resta una pregunta... éde donde viene la méscara? Hay
basicamente dos formas de obtener la mascara...
+ Hacerla uno mismo en un prograrra de edicion ce imagenes. Esta es una decisién rasonable si estamos
usando una cantidad limtada de Imagenes. De esta forma podermos agregar el recurso miscara a nuestro
programa y cargarlo usando Lioadsitmap( ).
+ Generarta cuando se ejecuta nuestro programa seleccionando un color en la imagen original com el "color
transparente" y crear una miscara que sea blanca donde aparezca dicho color y negra en caso contrari,
Debido @ que la primer opcién no es nada nuevo, deberias ser capaz de hacerlo si lo deseas. La segunda forma
proviene de aplicar algunos malabares usando 5::51t ( ) y por lo tanto voy a mostrar una forma de realizaria.
Como crear una Mascara
La forma mis simple de hacerlo es recorrer cada pixel en la imagen, chequear su valor y luego poner en el
correspondiente pixel de la méscara el color blanco 0 negro. Si usammos B:tFit( ) (usando saccoPy) desde un
OC que referencia una imagen a color dentro de un HDC que referencia una imagen blanco y negro, éste
chequearé que color esta especificado como color de fondo en la imagen a color y pondra todos aquellos pixeles
en Blanco, culaquier otro pixel que no sea del color del fondo terminaré siendo Negro.
Esto funciona perfectamente, debido a que todos lo que necesitamos hacer es poner el color de fondo con el
color que queremos transparentar y hacemos BitBit( ) desde nuestra imagen a la méscara, Observa que esto
solo funciona con una mascara monocromo (blanco y negro)... esto es, bitmaps que solo tienen 1 bit per
pixel para definir el color. Si intentamos esto con una imagen a color que sélo tiene pixeles blanco y negro, pero
que el tamafio para definir el color por pixel es mayor que un bit, digamos 16 bits 0 24 bits, esto no funciona.
éReceverdas la primer condicién anterior para obtener mascaras exitosas? Esta decia que nuestra imagen
necesitaba ser negra donde queriamos que fuera transparente, Debido a que el bitmap que he usado en este
ejemplo ya cumple esa condicién, no necesita ningin tratamiento especial, Pero si deseas usar este cédigo en
ota imagen que tiene un color diferente para transparentar (rosa es una eleccién comin), entonces
necesitamos tomar un segundo paso, esto es, usar la miscare que acabamos de crear para alterar la imagen
original, donde todo lo que queremes transparentar es negro. No hay probleme si otros lugares de la imagen
también son de color negro, debido a que esos lugares no son blancos en la miscara y por lo tanto no serén
trenparentes. Podemos realizar esto utllizando +r31t ( ) desde le nueva méscara @ la imagen original, usando
napaivingrog.orgucriaVlestransparencytard 1s22016 Bitmap Trensparertes
la operacién scrrvext, la cual fija todas la reas que son blancas en la mascara a negras en la imagen a color.
Esta es una parte de un proceso complejo, por lo tanto es bueno tener a mano una funcién muy Util que hace
todo esto por nosotros, aqui esté:
HBITMA2 CreateBitmapMask (HBITMAP hbmColour, COLORREF crTransparent)
‘
d
HDC AdcMom, hdcMem?
UBITMAR horas’
BITMAP
/forea una mascara monocromo (1 Dit).
Gevobject (homColour, sizeof (BITMAP), bm);
hbaMask = CreateSitmap (bm.bnWidth, bm.bmieight, 1, 1, NULL);
/fOptiene algunos ADCs qu
son compatibles con la driver de video
hdeMem = CreateCompatiblepC(0) ;
hdeMem2 = CreateConpatiblens (0) ;
SelectBitmap (hdctfem, nbnColour) ;
SelectBitmap(hdelen?, herMask) :
//zonenos ¢1 color de fondo de 1a imagen a color con el color que
/querenos que sea transparente
SetBkColor (hdeMen, cxTransparent) ;
//Copiamos los bits de 1a imagen a color a la mascara
//Blanco y Negro... todo le que sea del color de fondo termina siendo
/Mplanco y code io demas termina siendo negro... Como querianos
BitBlt (hdcWen2, 0, 0, bm.bnididth, br.onleight, hdeMen, 0, 0, SRCCOPY);
//Tonanos la aueva méseara y la usamos paca poner el color transpazente
//en dentro de la imagen a coler original, de esta manera el efecto
//de transparencia funcionara bien
BicBlt (hdcWem, 0, 0, bm.brilidth, bm.bmieight, héeMen?, 0, 0, SRCTNVERT)
If Liberamos.
Deletepc (hacMem) ;
Deletes (ndeMen2) ;
return hbntias!
NOTA: Esta funcién llama a Selectobjecs( ) para seleccionar terrporalmente el bitmap a color que le pasamos
dentro de un 0c. Un bitmap mo puede seleccionarse en mas de un #0c a la vez, por lo tanto asegarate que el
bitmap no esté seleccionado por otro HDC cuando lames a este funcién, de ser asi la funcién fallard:
Ahora que tenemos nuestra super funcién, poderos crear una mascara a partir de la imagen original una vez
que la hallamos cargado:
case WM CREATE:
g_hbmBal1 = LoadBitmap (GetModuleHandie (NULL) , MAKBTNTRESOURCE (IDB_BAL) ) +
i f(g_hbaBall == NULL)
MessageBox{hwnd, "Could not load IDB SALLI", “Er:
+ MB_OK | MB_ICONSXCLAMATTON) ;
g_hbmask = CreateBitmapMaskig_abmBall, RGB(O, 0, 0));
25 (g_hbnMask == NULL)
MessageBox (hwnd, "Could not create mask!", "Erro:
MB_OK | MB_ICONEXCLAMATTON) ;
break:
El segundo pardmetro es, por su puesto, el color de la imagen original que queremos que sea transparente, en
este caso es negro,
napaivingrog.orgucriaVlestransparencytardssrazo16 Bitmans Tersparertes
éComo funciona todo esto?
.. te estarés preguntando. Seguramente tu experiencia con C y C+ significa que comprendes operaciones
binarias como OR, XOR, AND NOT etc... No voy a explicar este proceso completamente, pero intentaré
mostrarte como lo he usado en este ejemplo, Si mi expicacién no es lo suficienterente clara, puedes leer algo
sobre operaciones binarias, que seguramente te ayudaré a entenderlo. Comprender esto no es determinante
para usar esta funcién y puedes ignorar como funciona confianco siempre en que lo hard correctamente.
SRCAND
La operacién SCRAND o ROP (Raster Operation) para BitB.+( 1, significa combinar los bits usando AND. Esto
es: solo los bits que valen 1 en la imagen fuente Y en la imagen destino quedan el el resultado final. Usamos
esto con nuestra méscara para poner en negro todos los pixeles que tendrén eventualmente un color de la
imagen original. La mascara tiene el color negro (en binario, todos 0) donde queremos que halla color y blanco
(todos 1 en binario) donde queremos que halla trasnsparencia, Cualquier valor combinado con 0, usanco AND, es
Oy por fo tanto todos los pixeles que son negros en la méscara son puesto en 0 en el resultado final, Cualquier
valor que es combinado con 1 usando AND permanece inaltereble, por lo tanto si valla 1 al corrienzo, finaliza
valiendo 1 y si valia 0 finaliza valiendo 0... Por lo tanto todos los pixeles que eran blanco en nuestra mascara, no
alteran su valor después de la llamada a BitBIt(). El resultado es el del la imagen de la esquina derecha de!
ejemplo.
SCRPAINT
SCRPAINT usa la operacién OR, por lo tanto si al menos uno de los 2 bits vale 1, entonces el resultado valdré 1.
Usamos esto en la imagen a color. Cuando la parte negra (transparente) de nuestra imagen a color es
combinada con los datos en el desitno usando OR, el resultado es que los datos permanecen inalterados, debido
que cualquier valor combinado con 0 usando la operacién OR permanece igual.
Sin embargo, el resto de la Imagen a color no es negro, y él si destino tampoco es negro, entonces tenemos una
combinacidn de los colores fuente y destino. El resultado puede verse en la segunda bola de la segunda fila de
la figura ejemplo. Esta es la raz6n por la cual primero usamos la mascara para poner en negro los pixeles que
queremos que tengan color, ya que cuando usamos OR con la imagen a color, los pixeles coloreados no se
mezclan con lo que haya dentro de ellos,
SCRINVERT
Esta es la operacién XOR usada para poner en negro el color transparente en nuestra imagen original (Sino ya
no es negro). Combinando un pixel negro de la mascara con un pixel que no sea del color de fondo en el destino
lo deja inalterable, rrientras que al combinar un pixel blanco de la miscara (que generarmos definiendo un color
particular como "fondo") con el color de un pixel del fondo del destino, esto hace que se cancele y se ponga en
negro,
Ejemplo 2
El cédigo ejemplo en el proyecto brp_two que viene junto con esta seccién contiene el cécigo para la imagen
del ejemplo de esta seccién. Este consiste en primero dibujar la mascara y la imagen a color exactamente como
son usando snccory, luego usa en cada una por separado las operaciones SCAND y repectivamente y
finalmente las combina para producir el resultado final,
El fondo en este ejemplo es gris para hacer mas obvia la transparencia ya que usar estas operaciones sobre un
fondo blanco o negro hace dificil darse cuenta si est funcionando o no.
Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.
Versién en espafiol: Federico Pizarro + 2003
napaivingrog.orgucriaVlestransparencytard 38ssra2016 Bitraps, Dispsitvos de Contero yBABt
[ contenidos | #winproa]
Bitmaps, Dispositivos de Contexto y BitBIt
Ejerplo: brp_one
GDI
Una cosa buena que tiene MS Windows, a diferencia de DOS, es que para mostrar
gréficos no necesitames conocer nada acerca del hardware de video que estamos
Usando. Por el contrario, Windows provee una API armada Graphics Device
Interface (Interface del Dispositive Gréfico) o GDI, en forma abreviada. LA GDI usa
Un conjunto de objetos gréficos genéricos que pueden ser usados para dbujar en
la pantalla, en la memoria, o en la impresora.
Dispositivo de Contextos
La GOI gira entomo a un objeto llarrado Dispositive de Contexto (DC), representado por el tipo de dato noc
(Handle al Dispositivo de Contexto). Un bc es basicamente un handle 2 algo en lo que poderms cibujar, puede
representar la pantalla entera, una ventana corrpleta, el drea cliente de una ventana, un bitmap almacenado en
memoria 0 puede ser una impresore, La parte buena es que no necesitamos saber a cudl de todas estas cosas
hace referencia, se usa basicamente de la misma manera, lo cual es muy ttl para escribir funciones de dibujo
que podermns usar en cualquiera de estos dispositivos sin necesidad de cambierla para cada uno de estos.
Un ec, al igual que la mayoria de los objetos GOI, es opaco. Esto significa que no podems acceder a sus datos
de forme directa... pero podemos pasérselo a varias funciones GDI que operarén en sus datos, ya sea para
dibujar algo, para obtener informacion a cerca de éste, o para cambiar de alguna manera el objeto.
Por ejemplo, si queremos cibujar en una ventana, primero deberms obtener un
que represente la ventana,
para esto utilizamos cet3c( ). Luego podermos usar cualquiera de las funciones GDI que torran un HDC, como
Por ejemplo sicsit ( ) para dibujar imigenes, vextout ( ) para dibujar texto, Linevo( } para lineas, etc...
Bitmaps
Los bitmaps puden ser cargados en su mayoria come los iconos de los primeros ejemplos. Hay una funcién
Eoad3itmap ( ) para simplemente cargar un recurso bitmap y una funcién noadrnase( } para cargar bitmaps
desde cualquier archivo *.bmp.
Una de las sutilezas de la GDI es que no podemos dibujar directamente sobre objetos bitmap (#arT™aP).
Recuerda que las operacioens de dibujo son abstractas y dicha abstraccién la realiza el Dispositivo de
Contextos, por lo tanto para usar cualquiera de estas funciones de dibujo sobre un bitmap necesitars crear un
Memory DC y luego seleccionar el xS1T™MAP dentro de éste usando Selectobjecz( ). El efecto es que el
“dispositvo" al cual el HOC hace referencia es el bitmap en memoria y cuando operamps sobre el 0c los
resutados de las funcioens graficas son aplicados al bitmap. Corro vimos, esto es una forma muy convenient de
hacer las cosas porque poderms escribir cédigo que dibuja sobre un #0c y luego usario sobre un Window DC 0
sobre un Memory DC sin tener que realizar cambios 0 chequeos.
Tenemos la opcién de manipular nosotros rrisrms los datos del bitmap en memoria. Podemos hacer esto con el
Dispositivo de Bitmaps Independientes (DIB) y podemos ain combinar operaciones GDI y operaciones manuales
en el DIB. Sin errbargo, por el momento, esto esté mas alld del alcance de este tutorial bésico y por ahora sélo
Cubriremos las operaciones GDI mas simples.
Liberacién de Objetos GDI
Una vez que hemos finalizado de usar un soc es muy importante Iberarlo (cémo lo hacemos depende de cémo lo
obtuvimns, hablarermos de esto dentro de un momento). Los objetos GDI estén limitados en ndmero. En
versiones de Windows anteriores a Windows 95, no sdlo estaban increfblemente limitados siné que también
compartian ampliamente los recursos del sistema, por lo tanto, si un prograrma usaba derrasiados, ninguno de los,
dems programas podia ser capaz de dibujar nada! Afortunadamente, en Windows 2000 o XP, esto ya no sucede
mis y podemos obtener una gran cantidad de recursos sin que suceda algo malo... pero atin asi es facil
olvidarse de liberar objetos GDI y répidamente podemos obtener un agotamiento de los recursos en sistemas
como Windows 9x. Tedricamente no deberiamos poder agotar los recursos GDI en sisterras basados en
tecnologia NT (NT/2K/XP) pero puede llegar a suceder en casos extremos, o si acertamos en el bug apropiado...
Si nuestro programa se ejecuta bien algunos rrinutos y luego corienza a dibujar extrafiamente o de forma
incompleta, es una buena sefial de que estés reteniendo objetos GDI. Los #0¢s no son los Unicos objetos con los
hapaivingrog org riaVesitmans rant 1ssra2016 Bitraps, Dispsitvs de Conterto yBHBt
que necesitamos ser cuidadosos con respecto a su liberacién, en general es bueno mantener cosas como
bitmaps y fuentes hasta el final de la ejecucién del programa, debido a que es mucho més eficiente que
recargarlos cada vez que los necesitarws.
Actualizacién Importante: Windows tiene asignados (retenidos) ciertos objetos por default en los HDC, pero
hay unos pocos objetos que nunca utiliza para esto y puedes buscar cuales son en MSDN. Debido a esto, tenia
la incertidumbre si #B77™A? era uno de ellos, ya que parece no haber una documentacién definitiva sobre éste y
los ejemplos (ain los de Microsoft) suelen ignorar el bitmap por default. De la escritura del tutorial original hace
varios afios, me han confirmado que hay un bitmap que necesita ser liberado. Esta informacién es cortesia de
Shaun Ivory, un ingeniero de software de MS y un arrigo mio de #winprog.
Aparentemente hay un bug en un protector de pantalla escrito por MS y esto se debe a que el bitmap por
default no fué reemplazado 0 destruido y eventualmente provoca un desborde de los recursos GDI. Debes estar
atentos!, es un error facil de cometer.
Como Mostrar Bitmaps
Ok, volvamos al tema. Las operaciones més simple de dibujo sobre una ventana se realizan procesando el
mensaje it, . Cuando nuestra ventana es mostrada por primera vez, 0 es restaurada o minimizaca, 0 es
descubierta luego de haber estado tapada por otra ventana, Windows le envie a la ventana el mensaje
‘we_PATN? para hacerle saber que necesita redibujar su contenido. Cuando dibujares algo sobre la pantalla NO
ES permanente, simplemente permanece hasta que algo se dibuja encima y luego, cuando se destapa, sélo es
necesario redibujarlo.
HBITMA? g_hbm@all = NULL:
g_bbmfall = Loads: Le (NULL), MAKEINTRESOURCE (IDB_BALL)) ;
#(g_hbnBall ==
MessageBox (hwnd, "Could not load IDB_BALL!", "E: MB_OK | MB_ICONEXCLAMATION) ;
El primer paso, por supuesto, es cargar el bitmap. Esto es simple cuando tenemos un recurso bitmap ya que no
hay diferencias significantes de cuando cargamos otro tipo de recursos.
case WM_PAINT
MAP ome
PRINTSTRUCT ps7
ADC hde = BeginPaint (hwnd, pe);
ADC hdeMer
ABIIMAP hbmo:
fompatiblepc (hde) ;
Selectobject (hdctiem, ¢_hbmBall) 7
Getopject (gb
Ball, sizeof (bm), ébm);
BitBlL(hde, 0, 0, bn.bawidth, bm.bmHeight, RdeMen, 0, 0, SRCCOPY);
Selectobject (hdeMem, hbmOld) ;
DeleteDC (hdcMer) ;
nézainc (hwnd, gps);
Obtener el Window DC
Para comenzar, declaramos algunas variables. Observa que la primera es un BITMAP y No un HBITMAP. BITMAP es
una estructure que almacena informacién acerca de un ara? el cual es el verdadero objeto GDI, Necesitamos
una forma de obtener el alto y ancho del us1rma2, para esto usamos Geto ject ( ), el cual, contrariamente a
su nombre, no obtiene un objeto pero si obtiene informacién acerca de uno ya existente. "GetObjectInfo" podria
haber sido un nombre mis apropiado. Getonject ( ) funciona con varios tipos de objetos GDI, que puede
distinguir usando el segundo parémetro, el tamafio de la estructura,
es una estructura que contiene informacién acerca de la ventana que esta siendo pintada, que es
\da con el mensaje paint. Para las tareas més simples, podermos ignorar la informacién que esta
hapaivingrog org riaVesitmans rant 28ssr22016 Btraps Dspciivs de Corto yBiit
estructura contiene, pero ésta es necesaria en la llamada a Heginfaint( }. Beginzaint( ) esta disefiada para
procesar especificamente el mensaje w_2ATWr. Cuando no procesamos el mensaje si_PAINT podemos usar
Gex0¢{ }, el cual veremos en los ejemplos de la animacién dentro de un momento... pero en el mensaje
M_PAIN? es importante usar BeginPaint( ) y EndPaint ( ).
Segin2aint ( ) retoma un Hoc que representa el Hatio que le pasarms, el de la ventana que est procesando el
wu_PaiN?. Cualquier operacién de dibujo que realizemos sobre este HDC seré inmediatamente mostrada en la
pantalla
Preparando un Memory DC para el Bitmap
Como mencioné anteriormente, para poder dibujar sobre bitmaps © con bitmaps, necesitamos crear un DC en
mermpria... la forma més facil de hacero aqui es usando Createcompatibletc( ) con el que ya tenemos. Con
esto obtenemns un Memory DC que es corrpatible con el color y las propiedades de dibujo del 5c de la
ventana.
Ahora llamamos a Selectobject |) para seleccionar el bitmap dentro del DC, siendo cuidadosos de almacenar
el bitmap por default para que podamos reemplazarlo posteriormente y no retener objetos GDI.
Dibujo
Una vez que hemos llenado los valores ancho y alto dentro de la estructura srr“ar podermos llamar a Biss:
) para copiar la imagen desde nuestro Memory DC hasta el Window DC y luego mostrarlo en pantalla. Comp
siempre puedes buscar cada parémetro en MSDN, pero acé estén resurridos: El destino, la posicién, el tamafio,
el origen y la posicién origen y finalmente el Raster Operation (cédigo ROP), el cual especifica como hacer la
copia. En ese caso queremos una copia exacta del recurso, nada mis.
BitBlt probablemente sea la funcién mas feliz de toda la API Win32 y es la dieta pricipal de todos aquellos que
estn aprendiendo a escribir juegos o alguna otra aplicacién grafica en Windows. Esta fué probablemente la
primer API de la cual he memorizado todos los parémetros.
Limpieza
Hasta este punto el bitmap deberia estar en pantalla y es necesario que lo borremas nosotros rrismos. Lo
primero que hay que hacer es restaurar el Memory DC al estado en el que estaba cuando lo obtuvimos, para
esto, reemplazamos nuestro bitmap con el bitmap por default que hablamos guardado. Luego poderos borrarlo
por completo usando peleteDc (
Finalmente liberamos el Window DC que obtuvimos con Besin2aint ( ) usando EndPaint( )
Destruir un HDC a veces es un poco confuso debido a que hay al menos 3 formas de hacerlo que dependen de
come lo obtuvimos. Aqui hay una lista de los métodos mas comunes para obtener un D¢ y luego liberario.
+ GetDC() - ReleaseDC()
+ BeginPaint() - EndPaint()
+ CreateCompatibleDC() - DeleteDc()
Por iltimo, al final de nuestro programa, liberamas todos los recursos que tenemos asignados. Técnicamente
hablando, esto no es absolutamente necesario debido a que las plataformas modemas de Windows son muy
buenas liberando todos los recursos cuando nuestro programa termina, aunque siempre es conveniente hacerlo
uno mismo ya que hay viejas versiones de Windows que no hacen esto de manera efectiva.
case WM_DESTROY:
Deleteobject (g_hbmBall) +
Postoui Message (0) ;
Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.
Versién en Espafiol: Federico Pizarro - 2003
hapaivingrog org riaVesitmans rant 38ssra2016 ‘Timers yArimaciones
[contenidos | #winorea 1
Timers y Animaciones
Ejemplo: anim_one
Antes de comenzar.. CE aici
‘Antes que veamos cosas animradas necesitamos crear una estructura para alacenar la posicién
de Ia esfera entre les actualizaciones, Esta estructura almacenaré le posicién actual y el tamario
de la esfera, como asi también los valores deka (cuanto queremos mover la esfera en cada
frame),
Una ver que hemos declarade el tipo de la estructura, también declarames una instancia global de
Ta misrra. Esto esté bien, debico a que solo tenemos Una esfera, si fuerames a animrar varias ce
elas deberiamps probablemente usar un arreglo 0 una Ista para contenerias de una forma mas
conveniente.
También hemos definide una constante 2A12._Nov=_peurn la cual Indica cuan lejos queremos que la esfera se desplace en cada
actualizacién, La razén para almacenar deltas en bb estructura BALLIMFO es que queremes mover la esfera hacia arriba 0 hacia
abajo, hacia izqulerda o derech, de forma Independiente, axis. yove_osita es sélo un nombre significative que nos faciltaré la
tarea de cambiar el valor en el futuro.
‘Ahora necesitams iniclalzar la estructura después de que cargamas nuestros bitmaps:
Suballingo.éy = BALL MOVE DEL;
La esfera comienza en le esquina superior izquierda, moviéndose hacia le derecha y bajando de acuerdo @ los miembros dx y dy de le
estructura sazi2K0,
Configurando el Timer
La forma mis fécll de agregar un tlmer dentro de un programma en Windows es con setTiner (), aunque no es la mejor forme y
tampoco es la forma recomendada para aplcaciénes multimedia o juegos, sin ermbargo es lo suficientemente buena para aplicaciones
simples com ésta. Cuando necesites algo mejor, échale una mirada @ tineSetfvent {) en MSDN, la cual es mas apropiada,
‘Aqui heros declarado un ID para el timer para que podamns referimos a éste posteriormente (para elirinarla) y pusimas el timer en
el manejador del mensaje me canntz de nuestra ventana principal. Cada vez ue transcurre un determinade tiempo, el timer envia
Un mensaje iw_tzéen a la ventana y pasa como pardmetro en «Pavan su ID, Deblde a que sélo tenemos un timer no necesitamas el
1D, pero es uy Gti si utilzamos mas de uno.
Hemos configurade ol timer para que envie una sefial cada 50 rilsegundos, lo cual resulta en aproximadamente 20 cuadros por
segundo. He dicho aproximadamente debide a que, comma die, Set! ne { ) eS un poco irpresiso, pero este no es un cédigo crtico
donde milsegundos mas milsegundos menos causardn estragas, por lo tanto podemes usaro,
Animacién en WM_TIMER
‘Ahora, cuando obtenemos el mensaje vt_s2¥e% queremas calcularla nueva posicién de la esfera y dioujasa en la nueva posici.
‘
Hapaivingrog org riaes/animston Hind 1ssra2016 “Timers yArimaciones
)
breaks
He puesto el cédigo pars actualizar y dibujar la esfera en sus respectivas funciones. Esto es una buena préctica y nos permite
dloujar la esfera fuera de sz TINGE 0 dem eArNT sin necesidad de duplicar el cédigo. Observa que el métode que utilzams para
obtener el HOC en cada caso es diferente , por lo tanto es major dejar este cédigo en los manejadores de mensajes y pasar el
resultado dentro de la funcién Drawsaii ( )
void updatewalliazer* pre
goballinse-y += 3 ballingo.ay:
f(g ballinto.e < 0)
g_ballinfo.dx = BALL_MOVE_DBLTA:
d
else f(g ballinto.x + g ballinto.width > pro-oright)
‘
g.allinfo.x = prowright - q ballinto widths
Glbailinfo.de = SAUL, MOVE_DBDTAT
)
ifte_palltate.y < 0)
‘
g ballinto.y = 0;
gballintordy = BALL MOVE |
d
elee Lt(y_ballinge.y + g ballinto.height > pre->bottes}
‘
g ballinfo.y = pro-svortom - g ballinfo.neiehts
@palllnto.dy = ~SALL MOVE_DELTA?
'
Todo Io que esto hace es basicamente matematico, sumarres el valor delta a Ia posicién x para mover la esfera. Si el valor pasa
fuera del grea clente lo ponemras nuevarente en el rango y carmbiammos el valor delta a la diteccién opuesta para que le esfera
*rebote” en los bordes.
void Deawiell (HDC nde, RECT* peat
HOC AdoPufFer = CreateCanpatibienc (hdc)
ABIIVAP RimBuSter - CroateconpazipleBstmae (nde, pro->eight, pre-vbotton) s
MBITYAP nbnOlazurfer SelectOsject (necBut fer, howButter)
HOC hdoMem = CreateCompatiblede thde)
MBIIVAP Hbmold = SelectObject (rdctem, g Hontask)s
Filinact (ndeauffer, pee, GatstockObjact (WHITE_BRUSH))
BSESIE [hdcRUFfer, g ball TaFo.%, g bal lInfo.y, gq ballTafo.widen, gballTnfo.height, hdcMor,
2, BRAND) +
Selectonject (hdowen, ¢_hbmall)
Bitsle(hdcsufter, g/ballinro.x, qballinto.y, gballtnfo.wideh, gballtnfo.height, hdcMar,
BStALE Ihde, 9, 0, pre“sright, pre-Sbetton, necd!
Selectobject (hdcten, nonold)
Deletenc (nectar) 7
Seloctopject indcbutfer, hpnOl
Deletene (ndosufter!:
Deletednject (nanBurter) +
'
Este es escenclalmente el mismo cécigo de dibujo ce los ejemplos anteriores, con la excepcién de que abtiene la posicién y las
dimensiones de la esfera a partir de la estructura Hai IN=0, Sin embargo hay una diferencia impartante..
Doble Buffering
Cuanco dibujamos directamente al soc de la ventana, es totalmente posible que le pantalla se actualce antes de que lo hagarmos,
Por ejemplo, después de que dibujeros la méscara y antes que dibujeros la iragen original encirs el usvario puede ver un parpadeo
Gel fondo antes de que nuestro programa tenga una chance de de dibujar sobre éste Ia imagen en color, Mientras ras lerca sea la
computadara y mientras mas operaciones de dibujo realcemos, apareceran mas parpadeos,
Esto es tomriblemente drdstico y podemos resolve simplemente haciendo primero todos los dibujos en rremoria y luego copiar a la
pantalla la obra maestra cormpleta en un solo 31 () para que la pantalla sea actualzada directamente de dicha kragen, sin que
‘sean visiles ninguna de las operaciones intermedias.
Hapaivingrog org riaes/animston Hind 20ssra2016 “Timers yArimaciones
Para hacer esto creamos en memoria un ii? 7™AP temporal que es del tamafo exacto cel érea que varros a dibujar sobre la pantalla.
‘También necesitamos un HDC para que podamos usar sitsi=() sobre el bitmap.
suffer = CreateConp
ABITVAP hbnolaautfer = Sel clndesurfer, ‘sbwbutter) 7
‘Ahora que tenemos un lugar en memaria donde cibujar, todas las operaciones de dibujo usan hdowurrer en vez de née (la ventana)
Y los resultados son almacenados sobre el bitmap en memoria hasta que hallaros terminade. Luego poderras copar dicha imagen a
la ventana en un solo “dispar!
Por ditimo podemmos eliminar nuestro Hoc y KEIIMAP de manera usual
Doble Buffer
1g Veloz
En este ejemplo estoy creando y destruyendo el bitmap usado para el doble buffering con cada frame, hice esta porque es més facil
siempre crear un nuevo boffer que registrar cuando cambia la posicién de la ventana y cambiar el tarrafo del Differ. Pero podria ser
mis eficiente crear un doble batfer glabal y no perritir cambiar el Lamafio de Ia ventana a sélo carrbiar el tamafia del bimap cuando
8 cambiado el tarrafo de la ventana, en vez de crearlo y destruilo todo el tiempo. Se deja como tarea al lector implerrentar esta
optimizacién si desea mejorer el dbujado sobre la pantale, pare Un juego o algo par el est,
Eliminando el Timer
Es una buena idea liberar todos los recursos cuando la ventana es destruida y en este caso también se incluye el timer que hemos
usado. Para deteness, simplemente lamarres @ K:11Timor( } y fe pasarms el ID que hemos usado para crearlo.
KiLitiner (twee, 1077
a
Copyright © 1998-2003, Brook Miles (the=araer). Allrights reserved.
Versién en espat
I: Eederico Pizarro - 2003
Hapaivingrog org riaes/animston Hind 38ssra2016 “Tutoral- Win: Texto yFusrtes
[ contenidos | #winprog }
Texto y Fuentes
Ejermplo: font_one
Carga de Fuentes lolx!
File Format
La GDI Win32 tiene algunas capacidades admirables para trabajar con [These are the dimensions of your
diferentes tipos de estilos de presentacién, lenguajes, conjuntos de Jient area:
caracteres etc. Uno de los Inconvenientes de esto es que trabajar con “0, 0.225, 96)
fuentes luce algo intimidatorio para el que recién se inicia
CreateFont (), la principal API para trabajar con fuentes tiene 14
parémetros, los cuales especifican el alto, estilo, ancho, familia y otros
varios atributos
Afortunadamente no es tan dificil como parece y una gran parte del trabajo es realizada con valores por
default. Todos menos dos de los valores de Create¥ont () pueden ser puestos en 0 0 en NULL para que el
sistema use los valores por default dando como resultado una fuente plana y comin.
CreateFont ( ) crea un row, un handle a la Fuente Légica en memoria. Los datos referenciados por este
handle pueden ser recuperados dentro de una estrucura Locrowr usando la operacién Getobsec=( ), dela
misme manera que lo haciamos con los bitmaps donde la estructura BITMAP era rellenada a partir de un
EBITMAP. Los membros de LoGFONT son idénticos a los parémetros de CreaseFont () y por conveniencia
podemos crear directamente una fuente apartir una estructura LocrowT ya existente, usando
CreateFont indirect ( ), Esto es muy util debido a que hace ras fécil la tarea de crear una nueva fuente a
Partir de un handle cuando solo queremos alterar ciertos aspectos de ésta. Para rellenar los campos de la
estructura LOGFONT Usamos GetObject ( ), alteramos los campos que desearmos y creamos una nueva
fuente usando CreateFontTadivect ( }
FONT hy
"DC hdc;
long lfHeigh
hde = GetDC (NULL) ;
Lffeight = -MulDiv(12, GetDe
ReleaseDC (NULL, hd
hf = Crea’ 3, 0, 0, 0, 0, “Times New Roman
iftnc)
(
Deletedeject (g_hfFont) ;
gunffont = hf;
MessageBox (hwnd, "Fon!
eation failed!", 1) MB_OK | MB_ICONEXCLAMATION) ;
:
Este es el cédigo usado para crear la fuente en la imagen ejerrplo. La fuente es Times New Roman de 12
puntos con el estilo Itd/ica El flag para obtener la fuente en itdlica es el sexto pardmetro de CreateFont( ),
el cual se puede apreciar que est puesto en 1206, EI ultimo pardmetro indica el nombre de la fuente que
queremos usar.
La Unica parte tramposa de este cédigo es el valor usado para el tamefio de la fuente, el parémetro
Ifzeight de createront ( ). Generalmente las personas, cuando trabajan con fuentes, estén
acostumbradas a trabajar con tamafios especificados en Puntos, Tamafio 10, Tamafio 12, etc... Sin
embargo, == (} no acepta tamafios en puntos,solo acepta Unidades Légicas, las cuales son
diferentes en nuestra pantalla que en nuestra impresora y atin entre impresoras y pantallas.
La razén de que exista esta situacién es porque la resolucién de los diferentes dispositivos es bastante
diferente... las impresoras pueden imprimir de 600 a 1200 pixeles por pulgada, mientras que una pantalla con
Hapaivingrog.orgutorialestons rar 1ssra2016 “Tutoral- Win: Texto yFusrtes
suerte llega a mostrar unos 200. Si usaros el mismo tamafio para la pantalla que para la impresora, quizés
no podamos ver algunos caracteres en particular.
Todo lo que tenemos que hacer es convertir el tamafio punto al tamafio légico para un dispositivo
determinado. En este caso el dispositivo es la pantalla, por lo tanto obtenemos el HOC de la pantalla y
obtenems el ntimero de pixeles légicos por pulgada usando cezdev: cecaps () y metemos esto dentro de la
formula provista tan generosamene en MSDN la cual us@ MulDiv( } para convertir nuestro tarrafio punto, en
este caso 12, al tamafio l6gico que CveateFont( ) espera. Luego alrracenamos este en 1f Height y lo
pasamos como primer parémetro de Cratevont ( ).
Fuentes por Default
Cuando llamamos por primera vez a Getoc( ) para obtener el anc de nuestra ventana, la fuente por default
que es seleccionada es System, la cual para ser honesto, no es muy atractiva. La forma més simple de
obtener una fuente mejor sin tener que pasar por Create¥ont.() es llamar a GetStockobject () y preguntar
por la DEFAULT_svT_Fonr,
Este es un objeto del sistema y podemos usarlo todas las veces que deseamos sin necesidad retener
memoria, podemos llamar a Deleted ject () para borrarlo pero esto no causaré efecto, lo cual es una buena
idea porque no tendremos que averiguar si dicha fuente fué creada con CreateFont( ) 0 con
GetStockodject ( ) antes de intentar elirrinarta
Mostrando Texto
‘Ahora que tenemos una "linda" fuente, écomo hacemos para mostrar alguin texto en la pantalla? Esto es,
asumiendo que no queremns usar un control Edit o un control Static.
Las opciones basicas son rextout( } y DrawText( }, TextOus () es mas simple pero tiene menos opciones
y no hace cubrimientos de palabras 0 alineacién por nosotros.
client area:"
"these are the dimensions of ¥:
WONT hEO1d = SelectObject (hde, hE);
sei
SetTextcolor (nde,
kCoLor (hdc, g_rabBackgzound) ;
Text)
if (g_bopaque)
BkMode (héc, OPAQUE) +
TRANSPARENT) ;
+ pee, DI_WORDBRBAK) ;
woprintt(szSize, "{8e, 44, Sd,
seSize, -1, pre,
Deft, pro->tep,
INE | DT_CENTER
ide, nfOle) ;
La ptimer cosa que hacerms es usar selectobject ( ) para obtener le fuente que queremos usar dentro de
nuestro 9c y para preparamos para dibujar. Todas las operaciones de texto usaran, de aqui en mas, esta
fuente hasta que se seleccione alguna otra.
Ahora vamos a definir los colores del texto y del fondo, Cuando fijamos el color de fondo no cambiamos todo
el fondo a un determinado color, solo afecta a ciertas operaciones que dibujan texto usando el color de
fondo. Esto también depende del Modo de Fondo (o Background Mode) que esté seleccionado actualmente.
Si esté definido como opagut: (el default) entonces cada vez que se escriba texto, el color de fondo de!
texto serd el que esté seleccionado como background color. Si esta definido como TaaNsPar=Nr, el texto es
dibujado sin color de fondo y quedaré como fondo del texto lo que sea que encuentre dibujado debajo de
éste,
Ahora realmente dibujamos el texto usando Draw7ext ( ), pasamos el HDC a usar y el string a dibujar. El
Hapaivingrog.orgutorialestons rar 28ssri216 “Teoral- Wins: To y Fetes
tercer pardmetro es le longitud del string, pero hemos pasado -1 debido a que Drawrext( } es lo
suficientemente astuto para darse cuenta cual es la longitud del texto. En el cuarto pardmetro pasarmos pre,
el puntero al cliente 2c, Drawext ( ) dibujaré dentro de este rectdngulo basdndose en los otros flags que
le pasamos.
En la primer llermada especificamos p7_woRnarFax, el cual alinea por default el texto a la izquierda y
acomodaré el texto autométicamente a los limites del rectdngulo... muy Util
Para la segunda llamada solo imprimimos una sola linea sin ajustar el texto al rectdngulo y a centramos
horizontalmente y verticalmente (lo cual hard Drawrex: () cuando dibujermos una sola linea).
inte
Redibujar el Cli
‘Sélo una nota sobre el programa ejemplo... cuando sNociass es registrada he puesto los estilos cs_VREDAAN
y C3_iREDRAN, Esto provoca que el area cliente entera sea sea redibujada si la ventana cambia su tamafio,
rrientras que por default solo se dibujan las partes que han cambiado. Esto luce algo malo debido a que el
texto centrado es movido cuando la ventana cambia su tamafio pero no se actualiza como esperamos.
Eleccién de Fuentes
En general cualquier programa que utiliza fuentes le permite al usuario escoger su propia fuente, como
también el color y los atributos de estilo a usar.
Al igual que los didlogos comunes para abrir y guardar archivos, hay un diélogo comin para escoger una
fuente. Este es llamado Chooseront ( } y funciona junto con la estructura cHoosm Fon.
FONT g_hZFont = Getstockobject (
COLORREF g_rgbText = RGB(O, 0, 0)
void Dese
i
sat (HAND hwnd)
CHOOSEFONT cf = {sizeof (CHOOSSEONT) };
LOGFONT Lf;
Getobject (q_hf¥ent, sizeof (LOGEONT), &1£);
cf.Plags = CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
cf.pwndOwner = hwnd;
cf.lphogFont = slf;
cf.rgbColors = g_rgbText;
Lf (ChooseFont (Se£})
{
HEONT hE = Creat
tnt)
alt);
one
creation failed!", "Error", MB_OK | MB_TCONEXCLAMATTON) +
rgbColors;
El nwad en esta llamada es simplemente la ventana que vamos a usar como padre del didlogo.
La forma mas facil de usar este didlogo es junto con una estructura Locron™, la cual es bastante parecida a
la estructura HFONT que veniamos usando. Hacemos que el campo IpLogFont apunte a LOGFONT, que
acabarmos de llenar con informacién y también agregarms el flag cF_INTTTOLOGFONTSTRUCT para que
ChooseFont ( ) use este campo. El flag cr EFFECTS le dice a Chooseront ( ) que permita al usuario
seleccionar un color como asi también efectos de subrayado y tachado.
Hapaivingrog.orgutorialestons rar 38ssr22016 “Teoral- Wins: To y Fuses
Los estilos Negrita e Itdlica no se cuentan como efectos, son considerados partes de la fuente y de hecho
algunas fuentes solo vienen en Negrita 0 en Italica. Si queremos chequear o prevenir al usuario de
seleccionar una fuente negrita o italica, podemos chequear los campos ifWeight y 1frtalic,
respectivarente, de la estructura Locronr después de que el usuario ha hecho su eleccién. Luego se le
puede preguntar al usuario si desea hacer otra seleccién 0 algtin cambio a los carrpos antes de llamar a
CreateFont Indirect ()
El color de una fuente no esta asociado con un Fon” y por lo tanto debe ser almacenado por separado. EL
campo rebColors de la estructura clloos Fon’? es usado para pasar el color inicial y para recuperar luego el
nuevo color.
CF_SCREFNFONTS Indica que queremos fuentes disefiadas para funcionar en le pantalla y no fuentes
disefiadas para funcionar en una impresora. Algunos soportan ambas, otros soportan una u otra.
Dependiendo de para que vas a usar la fuente, este y muchos otros flags pueden ser encontrados en MSDN
para definir exactamente que fuentes deseamos que el usuario sea capaz de seleccionar.
Eleccién de colores
Para permitirle al usuario cambiar el color de la fuente, existe el didlogo comin choosecotor
cédigo usado para permitirie al usuario seleccionar el Color de fondo en programa ejemplo.
Este es el
(258, 255, 258);
COLORREF g_rgbBackgzound
conor:
AEP g_rgecuston|16]
of (CHOOSECOLOR) }
CC_LRGBINIT | CC_FULLOPEN | Cc_ANYCOLOR;
cc-hwndOwner = hwnd;
cc.rgbResult = g_rgbBackground;
ec. 1p
ustColors = g_rabCustor
if (Choosece:
(
(ce)
g_tgbBackground = ce.rgbRes
}
Esto es algo complicado, nuevamente estamos usando el parémetro hwnd comp padre del didlogo. El
Pardmetro cc_RCBINI? indica que debemos comenzar con el color que pasamos en el campo rebResult, el
cual también es donde obtenermos el color que el usuario ha seleccionado cuando el didlogo se clerra.
El arreglo g_rqhCustom de 16 COLORRE#S es necesario para almacenar cualquier valor que el usuario decide
poner dentro de la tabla de colores personalizados incluida en el didlogo. Podemos potencialmente almacenar
estos valores en otro lugar que no sea el registro, porque de no ser asi dichos valores se perderan al cerrar
el programa. Este parametro no es opcional.
Cambio de la Fuente en los Controles
Algo que podernos hacer en aigin punto es cambiar la fuente de los controles en los didlogos o ventanas.
Generalmente este es el caso cuando usarros CreatsWindow( } para crear controles como lo hemos hecho
en los ejemplos anteriores. Los controles al igual que las ventanas usan la fuente System por default, por lo
tanto usamos ‘i_SBTFON? para definir un nuevo handle a la fuente (de GetStockobject ()) para que use el
control. Podems usar este metodo con fuentes que creamps usando Createront (). Simplemente pasamos
el handle a la fuente como wParam y ponemos =Param en TRUS para hacer que el control se redibuje.
He realizado e:
muy breve:
‘en los ejemplos anteriores, pero tiene sentido mencionarlo aqui debido a que es relevante y
sendb:
gItemMessage (hwnd, IDC_OF_YOUR_CONTROL, WM_SETFONT, (WEARAM)hfFont, TRUE);
Donde nr¥ont es, por su puesto, el HFONT que queremos usar y 12¢_oF_YOUR.
al cual le queremos cambiar la fuente.
‘ONTROL es el ID del control
Hapaivingrog.orgutorialestons rar 45ssra2016 “Tutorial Wins - Herramientas de Linea de Commando para VO
[ contenidos | #winproa ]
Herramientas de Linea de Commando para
VC++
Cémo Obtenerlas
Microsoft ha lanzado completamente las herramientas de su cormpilador de linea de comando y del
enlazador como parte de .Net Framework SDK. El Framework SDK viene con todo lo necesario para
el desarrollo .NET (compilador C#, etc...), incluyendo el compilador de linea de comando cl.exe,
el cual si bien esta destinado a ser usado con .NET Framework, es el mismo compilador que viene
con Visual C++ Standard.
NET Framework SDK
Debido a que este es el .Net SDK, no viene con los headers (*.h) y las librerias necesarias para el
desarrollo Win32 API, ya que estos son partes de la plataforma SDK. De todas maneras, la
plataforma SDK es gratuita, solo necesitas bajar el Core SDK y si deseas puedes descargar
algunos otros cormponentes,
Platform SDK
Come bonus, si descargas la documentacién de la plataforma SDK (lo cual es altamente
recomendado), tendras una referencia completa y actualizada sobre Win32 la cual es MUCHO mas
facil de usar que MSDN online,
Recuerda chequear las opciones de Register Enviroment Variables en ambos SDKs, de no
ser asi, necesitaras chequear la ruta y otras variables antes de que las herramientas funcionen en
la linea de comando.
Como Usarlas
Debido a que se provee una comprensiva documentacién, también accesible via MSDN online
necesitards estudiarla un poco para aprender acerca de los corpiladores VC+ y sus
herramientas. Para comenzar aqui estan las formas basicas de crear un programa...
Para hacer una simple aplicacién de consola:
Y para crear un aplicacién simple en Windows como los ejemplo de éste tutorial:
Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.
Versién en Espafiol: FedericoPizarro - 2003
Hapdivingrog.org utiles hind 1"ssra2016 “Toral Wind2- Libros Recomendads y Referencias
[ contenidos | #winproa ]
Libros Recomendados y Referencias
Libros
Si esperas que alquien online te trate con respeto mientras estas aprendiendo, entonces
NECESITAS un buen libro de donde aprender. Estamos aqui para dirigir y explicar aquellas cosas
que necesitan explicacién, no para ensefiarte paso por paso.
Puedes encontrar mas libros recomendados y links donde comprar en #Winprog Store.
Programming Windows
by Charles Petzold. &/ libro para empezar con Win32 API. Si quieres escribir programas
usando solo la API (la cual cubre este tutorial), entonces necesitas este libro.
Programming Windows with MEC
by Jeff Prosise. Si quieres aventurarte en la MFC (DESPUES de aprender a usar la API
Win32), este libro es para vos. Sino te gusta la MFC pero quieres buscar trabajo
desarrollando aplicaciones en Windows, entoces consigue este libro de todos modos.
Programming Applications for Windows
by Jeffrey Richter. No es para principientes, cubre el manejo de procesos y threads, dils,
manejo de memoria, manejo de excepciones y otros.
++ Windows Shell Proarammin
by Dino Esposito. Para aquellos que estan interesados en los aspectos visuales
interfaces arrigables en Windows, este libro cubre extensiones de la interfaz de Windows,
cémo trabajar eficientemente con archivos y drag and drop (arrastrar y soltar con el
mouse), cémo personalizar la barra de tareas y el Explorador de Windows y otra gran
cantidad de aspectos. Ideal para aquellos que desean escribir aplicaciones con Interfaces
Graficas al Usuario en Windows.
Network Programming for Microsoft Windows:
Informacién actualizada de programacién en redes, que incluye NetBIOS, mailslots y pipes y
por su puesto, los siempre importantes, Windows sockets incluyendo winsock2 y raw
sockets. También contiene informacién especifica sobre las distintas plataformas incluyendo
2000 y CE.
Links
MSDN Online
Este sitio tiene referencias a todas las tecnologias imaginables de Microsoft, incluyendo
Win32 API y documentacién MFC. Si no es incluida con tu compilador (VC-++) puedes
conseguir esta informacién de manera gratuita.
#winprea homepage
Mira en FAQ y Store
Vis
Copyright © 1998-2003, Brook Miles (theForger). Alll rights reserved.
Versién en Espafiol: FedericoPizarro - 2003,
hipaivingrog.orgutorialesieferences ant 1"ssra2016 “Tutoral- Win82: Por qué deberias aprender la APL antes que la MFC
[ contenidos | #winproa ]
Porqué deberias aprender la API antes que
la MFC
La Controversia
Muchas personas entran al IRC y preguntan "éQue es mejor, MFC 0 API?" y muchas otras personas
estan dipuestas a responder "MFC es unm..." 0 "API es una m...", ya sea porque quizés una o lo
otra estubo involucrada en algtin evento traumatico en su infancia, o simplemente porque todos
dicen que una es mejor que la otra.
Los argumentos estandar son:
+ APLes muy dificil
* MFC es demasiado confusa
+ APLes mucho cédigo
+ MFC esta mal disefiada
+ API no es orientada a objetos
+ MFC pated a mi perro
+ API se robé a mi novia
Y asi...
Mi Respuesta
Primero de todo vamos a aclarar que son la API y la MFC. API es un término genético que significa
Aplication Programming Interface (Interface para la Programacién de Aplicaciones). Sin embargo,
en el contexto de la programacién en Windows, esto se refiere especificamente a la API de
Windows, la cual es el nivel mas bajo de interaccién entre las aplicaciones y el sistema operativo.
Los drivers, por su puesto, tienen atin niveles inferiores y trabajan con diferentes conjuntos de
llamadas a funciones, pero para la gran majoria de desarrollo en Windows esto no es relevante.
MFC es una Libreria de Clases, un conjunto de clases C+ que han sido escritas para reducir la
cantidad de trabajo que tormaria realizar ciertas cosas utilizando la API. La MFC ademas introduce
dentro de la aplicacién un esqueleto (0 framework) Orientado a Objetos, de cual puedes tomar
ventaja 0 ignorarlo si deseas, como lo hacen la mayoria de los principiantes debido a que este
framework no estd apuntado a escribir reproductores de mp3, clientes IRC 0 juegos.
Entonces, éCual es el framework correcto?... Para principiantes, aquellas personas que
simplemente estan aprendiendo a programar, creo que deberian trabajar con la API hasta que
estén a gusto con la manera en que funcionan las aplicaciones y corrprendan todos los
mecanismos basicos que hay detrés de cosas com el Loop de Mensajes, GDI, Controles y quizés
atin mutlithreading y sockets. De esta forma corprenderan los cimientos de todas las aplicaciones
en Windows y podradn aplicar este conociriento comin a MFC, Visual Basic 0 cualquier otro
framework que no sporte todo lo que la API hace, simplemente porque dicho framework ya hace
una gran cantidad de cosas y no puede soportar todas esas pequefias cosas que nadie usa.
Entonces, cuando necesites usar esas cosas, es ahi dinde puedes agregarlas tu mismo utilizando
tus conocimiento sobre la API.
éPero la MFC no es mas facil? En cierta forma, es mas facil cuando muchas tareas comunes son
realizadas por uno mismo, reduciendo la cantidad de cédigo que realmente necesitamos tipear. Sin
embargo, menos cédigo no significa "mas facil” cuando no entendemas el cédigo que necesitamos
escribir, 0 cuando no entendemos cémo funciona dicho cédigo. Generalmente los principiantes que
utilizan los wizards para comenzar sus aplicaciones, no tienen idea que hace la mayoria del cédigo
que se ha generado y pierden una gran parte del tiempo intentando darse cuenta donde agregar
cosas 0 que cambios deben hacer para para alcanzar un cierto resultado. Si comienzas a escribir
tus programas desde cero, ya sea utilizando la API o MFC, entonces comprenderds todo lo que
hntpavingrog.orgucriaeslapvrfe nt 12ssra2016 “Tutoral- Win82: Por qué deberis aprender la AP! antes quela MFC
esta puesto alli ya que fué puesto por vos mismo y solo usaras las cosas que entiendes como
funcionan.
Otro factor importante es que la mayoria de las personas que estén aprendiendo por primera vez
la API Win32, no tienen una fuerte base en C++. Intentar comprender la programacién en Windows
utilizando la MFC y aprender C+ al rrismo tiempo puede ser una tarea monumental. Si bien no es
imposible, estard lejos de ser mas productivo que hacerlo teniendo conocimientos sobre C++ y la
API.
Entonces, basicamente...
Lo que pienso es que deberias aprender la API hasta que te sientas comodo con ella y luego
intentar con la MFC. Si luego, pareciera ser que tiene sentido y puedes ahorrar tiempo con la MFC,
entonces tisala.
Sin embargo, y esto es importante. .. si quieres trabajar con la MFC sin entender la API y luego
buscas ayuda por algo y la respuesta que obtienes hace referencia a la API (como por ejemplo
“Usa el HDC provisto en el mensaje WM_CTLCOLORSTATIC"), seguramente quedes colgado ya que
no entiendes como traducir un objeto de la API a uno de la MFC. En ese momento estards en
problemas y las personas se frustraran contigo por no aprender todo aquello que necesitas de la
API antes de intentar usar la MFC.
Personalmente prefiero trabajar con la API, solo me parece mejor. Pero si tengo que escribir una
terminal para una base de datos o un host para un conjunto de controles ActiveX, consideraria
seriamente usar la MFC ya que podria eliminarme una gran cantidad de cédigo que, en otro caso,
deberia reinventar,
Copyright © 1998-2003, Brook Miles (theForger), Alll rights reserved,
Versién en Espafiol: Federico Pizarro - 2003
hnapaivingrog.orgucoriaeslapfe ant 2Rssra2016 ‘Tutorial Wind2- Notas sobre Archivos de Recursos
[ contenidos | #winprog ]
Notas sobre Archivos de Recursos
Argh!
La Unica cosa que realmente odiaba cuando cambié mi entorno de programacién, de Borland C++ a
MS VC++, fué la forma en que VC++ maneja los scripts de recursos (archivos.rc).
En Borland C+ era libre de controlar la disposicién y el contenido de los archivos .rc y cuando
usaba el editor de recursos, sélo las cosas que yo especificamente cambiaba eran cambiadas en el
archivo. En cambio, el editor de recursos de VC+4, reescribe completamente el archivo .rc y
posiblemente destruye o ignora los cambios que personalmente realizo
En un principio esto fué terriblemente frustrante, pero basicamente he aprendido a trabajar con él
y después de todo no es TAN malo, debido a que, en general no escribo tanta cantidad de
recursos a mano, salvo aquellos con cambios menores que quizés no pueden realizarse por
completo en el editor de recursos.
Compatibilidad
Un pequefio cambio que se hizo en este tutorial fué lograr que los archivos de recursos compilen
de forma correcta bajo VC++ y BC+ sin realizar cambios. En el tutorial original he usado la
convencién de nombres para los encabezados de recursos utilizada por Borland, la cual era
nombre_proyecto.rh. Sin embargo, por default, en VC++ este encabezado SIEMPRE es llamado
resource.h, por lo tanto por simplicidad he adoptado este Ultimo para la revision actual del
tutorial que no impacta en BC-+ (no del todo).
Para los curiosos, es posible cambiar el nombre del recurso que usa VC++ editando manualmente el
archivo .rc y cambiando el nombre en dos lugares, primero donde es infcuido (#inciuce) y
segundo en el recurso 73x INCLUDE donde est contenido.
El segundo problema es que, por default, VC-++ requiere que sea incluido dentro del archivo de
recursos el archivo afxres.h , mientras que BC++ tiene todas las macros del preprocesador que
se necesitan, definidas de forma automética y por lo tanto no requiere dicha inclusién. Otra cosa
asombrosa es que afxxes.n solo es instalado si instalas MFC, lo cual no todo el mundo hace, aun
cuando estés creando una aplicacién API la cual solo requiere winres.h, que siempre es instalada.
Debido a que he trabajado con VC++ y he usado su editor de recursos, pude resolver este
problema alterando ligeramente cada archivo .rc incluyendo lo siguiente:
#ifndef _ BORLANDC
finclude "winres.h”
fend
Lo cual bajo circunstancias normales prodria leerse:
finclude
res.h"
Para aquellos que estén usando VC++ pueden encontrar la opcién de cambiar este texto dentro
del IDE através de "View > Resource Includes’, Generalmente, en la practica normal, no hay
necesidad de hacer siempre esto, sélo es una forma que he usado para solucionar el problema de
trabajar con VC-++ y BC+ al mismo tiempo.
Para aquellos que usan BC+, pido disculpas por toda la basura que es generada por el editor de
recursos de VC+, pero no puedo hacer nada al respecto.
hnapaivingrog org utriaesiesnotes ink 12