0% found this document useful (0 votes)
146 views

Tutorial WinAPI

Tutorial de la API de Windows en c++

Uploaded by

Pablo Perez
Copyright
© © All Rights Reserved
Available Formats
Download as PDF or read online on Scribd
0% found this document useful (0 votes)
146 views

Tutorial WinAPI

Tutorial de la API de Windows en c++

Uploaded by

Pablo Perez
Copyright
© © All Rights Reserved
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 66
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 12 ssra2016 “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 2R ssra2016 “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) 15 ssra2016 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); 28 ssr22016 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! 38 ssr22016 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! 45 ssri22016 “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! 58 ssra2016 “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 1 ssra2016 “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 218 ssri216 “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 3 ssra2016 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 44 ssra2016 “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 1 ss22016 “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 28 ssri22016 “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 38 ssra2016 ‘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 1 ssra2016 ‘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 28 ssra2016 “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 18 ssra2016 “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 20 ssra2016 “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 Hin ssra2016 “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 1 ssra2016 “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 28 ssra2016 “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 38 ssra2016 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 1 ssra2016 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 28 ssri216 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 38 ssra2016 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 1 ssra2016 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 218 ssri216 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 3 ssra2016 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 nar ssra2016 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 12 ssra2016 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 2R ser22016 ‘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 or ser22016 ‘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 Hind ssra2016 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 1 ssra2016 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 218 ssra2016 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 3 ssra2016 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 nar ssra2016 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 1 ssra2016 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 28 ssri22016 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 38 ser22016 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 18 ser22016 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 four ssr22016 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 45 ser22016 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 four ssra2016 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 1 s22016 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.orgucriaVlestransparencytard ssrazo16 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 38 ssra2016 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 1 ssra2016 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 28 ssr22016 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 38 ssra2016 ‘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 1 ssra2016 “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 20 ssra2016 “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 38 ssra2016 “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 1 ssra2016 “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 28 ssri216 “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 38 ssr22016 “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 45 ssra2016 “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 12 ssra2016 “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 2R ssra2016 ‘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

You might also like