En este tutorial, aprenderemos como "pintar" texto en el área cliente de una ventana. También aprenderemos sobre el contexto del dispositivo.
Puedes bajar el código fuente desde aquí.
El texto en Windows es un tipo de objeto GUI. Cada carácter está compuesto por numerosos pixeles o puntos (dots) que están amontonados dentro de un patrones distintos. Por eso hablamos de "pintar" en vez de "escribir". Normalmente, pintas texto en tu propia area cliente (relamente, puedes también pintar fuera del area cliente, pero eso es otra historia). En Windows, poner texto en la pantalla es algo radicalmente distinto a DOS. En DOS, puedes pensar en la pantalla como una dimensión de 80x25. Pero en Windows, la pantalla es compartida por varios programas. Algunas reglas deben ser reforzadas para evitar que los programas escriban sobre la pantalla de otros. Windows asegura esto limitando el área para pintar de cada ventana a su área cliente solamente. El tamaño del area cliente de una ventana tampoco es constante. El usuario puede cambiarla en cualquier momento. Así que hay que determinar dinámicamente las dimensiones del área cliente de las ventanas.
Antes de que puedas pintar algo sobre el área cliente, debes pedir permiso a Windows. Eso es correcto, ya no tienes el control total sobre el monitor como lo tenías con DOS. Debes pedir permiso a Windows para pintar tu propia area cliente. Windows determinará el tamaño de tu área cliente, de la fuente, los colores y otros atributos GDI y regresará un manejador del contexto de dispositivo a tu programa.
Luego puedes emplear tu contexto de dispositivo como un pasaporte para pintar tu área cliente.
¿Qué es un contexto de dispositivo? Es sólo una estructura de datos que Windows mantiene en su interior. Un contexto de dispositivo está asociado con un dispositivo en particular, tal como una impresora o un monitor de video. Para un monitor de video, un contexto de dispositivo está normalmente asociado con una ventana particular en el monitor.
Algunos valores en el contexto de dispositivo son atributos gráficos como colores, fuentes etc. Estos son valores por defecto que se pueden cambiar a voluntad. Existen para ayudar a reducir la carga de tener que especificar estos atributos en todas las llamadas a funciones GDI.
Puedes pensar en un contexto de dispositivo como un ambiente por defecto preparado para tí por Windows. Luego puedes anular algunos de los elementos establecidos por defecto si quieres.
Cuando un programa necesita pintar, debe obtener un manejador al contexto de dispositivo. Normalmente, hay varias maneras de realizar esto.
Hay algo que debes recordar para después de que tengas el manejador [handle] del contexto de dispositivo, y que debes realizar para el procesamiento de cualquier mensaje: no obtener el manejador en respuesta a un mensaje y emplearlo como respuesta a otro.
Windows envía mensajes WM_PAINT a la ventana para notificar que es ahora el momento de volver a pintar su área cliente. Windows no salva el contenido del área cliente de una ventana. En vez de eso, cuando ocurre una situación que garantiza que se va a volver a pintar el área cliente (tal como cuando una ventana ha sido cubierta por otra y luego descubierta), Windows pone el mensaje WM_PAINT en la cola de mensajes de ese programa. Es responsabilidad de Windows volver a pintar su propia área cliente. Debes reúnir toda la información sobre cómo volver a pintar el área cliente en la sección WM_PAINT de tu procedimiento de ventana, así que tu procedimiento de ventana puede volver a pintar tu area cliente cuando llega el mensaje WM_PAINT.
Otro concepto que debes tener en consideración es el de rectángulo inválido. Windows define un rectángulo inválido como el área rectangular más pequeña que el área cliente necesita para volver a ser pintada. Cuando Windows detecta un rectángulo inválido en el área cliente de una ventana, envía un mensaje WM_PAINT a esa ventana. En respuesta al mensaje WM_PAINT, la ventana puede obtener una estructura paintstruct que contiene, entre otras cosas, la coordenada del rectángulo inválido. Puedes llamar a BeginPaint en respuesta al mensaje WM_PAINT para validar el rectángulo inválido. Si no procesas el mensaje WM_PAINT, al menos debes llamar a DefWindowProc o a ValidateRect para validar el rectángulo inválido, sino Windows te enviará repetidamente el mensaje WM_PAINT.
Estos son los pasos que deberías realizar en respuesta a un mensaje WM_PAINT:
.386
.model flat,stdcall
option casemap:noneWinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eaxWinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endpWndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
Estas variables
locales son usadas por las funciones GDI en tu sección WM_PAINT. hdc
es usado para almacenar el manejador al contexto de dispositivo regresado por
la llamada a BeginPaint. ps es una estructura PAINTSTRUCT. Normalmente no tienes
que usar los valores en ps. Es pasado a la función BeginPaint y Windows
la llena con valores apropiados. Luego pasa ps a la función EndPaint
cuando terminas de pintar el área cliente. rect es una estructura RECT
definida así:
RECT Structleft y top son las coordenadas de la esquina izquierda superior de un rectángulo. right y bottom son las coordenadas de la esquina derecha inferior. Debe recordarse que: El origen de los ejes x-y está en la esquina superior izquierda. Entonces el punto y=10 está DEBAJO del punto y=0.
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
En respuesta al mensaje WM_PAINT, llamas a BeginPaint pasando como parámetros al manejador de la ventana que quieres pintar y una estructura PAINTSTRUCT no inicializada. Después de una llamada exitosa, eax contiene el manejador al contexto de dispositivo. Luego llamas a GetClientRect para recobrar la dimensión del área cliente. La dimensión es regresada en la variable rect variable que tú pasas a DrawText como uno de sus parámetros. La sintaxis de DrawText es:
DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD
DrawText es una función de la API de alto-nivel para salida de texto. Maneja algunos detalles tales como ajuste de línea, centramiento, etc. así que puedes concentrarte sólo en la cadena que quieres pintar. Su hermana de bajo nivel, TextOut, será examinada en el próximo tutorial. DrawText formatea una cadena de texto para fijar dentro de los límites de un rectángulo. Emplea la fuente seleccionada en el momento, color y fondo (en el contexto de dispositivo) para dibujar texto. Las líneas son ajustadas para fijarla dentro de los límites del rectángulo. Regresa la altura del texto de salida en unidades de dispositivo, en nuestro caso, pixeles. Veamos sus parámetros:
Después de terminar de pintar el área cliente, debes llamar a la función EndPaint para liberar el manejador del contexto de dispositivo.
Eso es todo. Podemos hacer un sumario de los puntos relevantes:
[Iczelion's Win32 Assembly HomePage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por: n u M I T_o r [ kUT ]