Hay muchas peticiones de tutoriales sobre controles RichEdit. Finalmente creo que puedo escribir algo sobre ellos. Así que auí está el: primer tutorial sobre el control RichEdit, que describe aproximadamente todo lo que se conoce sobre él al menos lo que yo conozco. La cantidad de información es bastante grande así que lo he dividido en varias partes, este tutorial es la primera parte. En este tutorial, aprenderás que es un control RichEdit, como crearlo y como cargar/salvar datos a/de él.
Baja el ejemplo.
Se puede pensar en un control richedit como en un control edit más potete. Nos provee de muchas características de las que nos gustaría disfrutar y que están ausentes en un control edit simple; por ejemplo, la habilidad de usar múltiples tamaños y tipos de fuentes, asi como niveles múltiplesS de Deshacer/rehacer, operaciones de busqueda de texto, objetos OLE incrustados [OLE-embedded], soporte arrastrar-y-soltar [drag-and-drop] de edicion, etc. En definitiva, el control richedit tiene muchas características interesantes, almacenadas en una DLL aparte. Esto también significa que para usarla no basta sólo con llamar a InitCommonControls como otros controles comunes. Tienes que llamar a LoadLibrary para cargar la DLL de richedit.
El problema es que hasta ahora hay tres versiones del control richedit. La versión 1, la 2, y la 3. La tabla de abajo muestra el nombre de la DLL para cada versión.
| Nombre de la DLL | Versión de RichEdit | Nombre de clase del control Richedit |
|---|---|---|
| Riched32.dll | 1.0 | RICHEDIT |
| RichEd20.dll | 2.0 | RICHEDIT20A |
| RichEd20.dll | 3.0 | RICHEDIT20A |
Puedes notar que la versiónes 2 y 3 usan el mismo nombre de DLL. ¿También usan el mismo nombre de clase! Esto puede suponer un problema, si quieres usar características especificas del richedit 3.0. Hasta ahora no he encontrado ningún método oficial para diferenciar entre la versión 2.0 y la 3.0. Sin embargo, hay uno posible que te mostrare después.
.data RichEditDLL db "RichEd20.dll",0 ..... .data? hRichEditDLL dd ? .code invoke LoadLibrary,addr RichEditDLL mov hRichEditDLL,eax ...... invoke FreeLibrary,hRichEditDLL
Cuando es cargada la DLL del control richedit, registra su clase de ventana RichEdit. Así que es imperativo que cargues la DLL antes de crear el control. Los nombres de clase del control richedit también son diferentes. En este momento puedes hacerte una pregunta: ¿Cómo puedo saber qué versión de richedit debo usar? Usar la última versión no siempre es lo mas apropiado si no requieres las características extras. La tabla de abajo muestra las características provistas con cada versión del control richedit.
| Cracteristica | Versión 1.0 | Versión 2.0 | Versión 3.0 |
|---|---|---|---|
| Barra de selección | x | x | x |
| Edición 'unicode' | x | x | |
| Formato caracter/parágrafo | x | x | x |
| Búsqueda de texto | Hacia adelante | adelante/atras | adelante/atras |
| OLE embedding | x | x | x |
| Edición Arrastrar y Soltar [Drag and drop] | x | x | x |
| Deshacer/Rehacer | nivel-simple | multi-nivel | multi-nivel |
| Reconocimiento automático de URL | x | x | |
| Soporte para teclas aceleradoras | x | x | |
| Operación Windowless | x | x | |
| Salto de Línea | CRLF | CR only | CR only (can emulate versión 1.0) |
| Aumento [Zoom] | x | ||
| Numeración de Parágrafos | x | ||
| Tabla simple | x | ||
| Estilos normal y cabeceras | x | ||
| Subrayado coloreado | x | ||
| Texto oculto | x | ||
| Fuente de encuadernacion | x |
La tabla de arriba puede no ser del todo comprensible: sólo he incluido una lista de las características importantes.
Tras cargar la DLL del richedit, puedes llmar a CreateWindowEx para crear el control. Puedes usar estilos del control edit y estilos comunes de ventanas en CreateWindowEx excepto ES_LOWERCASE, ES_UPPERCASE y ES_OEMCONVERT.
.const RichEditID equ 300 .data RichEditDLL db "RichEd20.dll",0
RichEditClass db "RichEdit20A",0 ... .data?
hRichEditDLL dd ? hwndRichEdit dd ? .code ..... invoke LoadLibrary,addr RichEditDLL mov hRichEditDLL,eax
invoke CreateWindowEx,0,addr RichEditClass,WS_VISIBLE or ES_MULTILINE or WS_CHILD or WS_VSCROLL or WS_HSCROLL, \ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,RichEditID,hInstance,0 mov hwndRichEdit,eax
Puedes tener problemas poniendo color al texto y al fondo en un control edit, pero este problema ha sido remediado con el control richedit. Para poner el color de fondo de un richedit, envía EM_SETBKGNDCOLOR al richedit. Este mensaje tiene la sintaxis siguiente.
wParam == opción de color. Un valor de cero en este parámetro
especifica que Windows usa el valor del color en lParam como color de fondo. Si este valor es distinto de cero,
Windows usa el color de fondo del sistema. Una vez que enviamos este mensaje
para cambiar el color de fondo, debemos pasar cero en wParam.
lParam == especifica la estructura COLORREF del color que quieres poner si wParam es cero.
Por ejemplo, si queremos poner el color de fondo azul puro, debo emitir la línea siguiente:
invoke SendMessage, hwndRichEdit, EM_SETBKGNDCOLOR, 0, 0FF0000h
Para poner el color del texto, el control richedit provee otro nuevo mensaje para esta tarea, EM_SETCHARFORMAT. Este mensaje controla el formato del texto de un rango de caracteres seleccionados o todo el texto del control. Este mensaje tiene la siguiente sintaxis:
wParam == opciones de formateo:
| SCF_ALL |
La operación afecta a todo el texto en el
control.
|
| SCF_SELECTION |
La operación afecta sólo al texto seleccionado
|
| SCF_WORD or SCF_SELECTION |
Afecta a las palabras seleccionadas. Si la selección
está vacía, por ejemplo, el cursor está junto a la
palabra, la operación afecta a esa palabra, debe usarse la bandera [flag]
SCF_WORD con SCF_SELECTION.
|
lParam == Puntero a una estructura CHARFORMAT o CHARFORMAT2 que especifica el formato de texto que sera aplicado CHARFORMAT2 esta disponible sólo para el richedit 2.0 y superior. Esto no significa que debas usar CHARFORMAT2 obligatoriamente con RichEdit 2.0 o superior.Todavía puedes usar CHARFORMAT si las características aņadidas de CHARFORMAT2 no te son necesarias.
CHARFORMATA STRUCT cbSize DWORD ? dwMask DWORD ? dwEffects DWORD ? yHeight DWORD ? yOffset DWORD ? crTextColor COLORREF ? bCharSet BYTE ? bPitchAndFamily BYTE ? szFaceName BYTE LF_FACESIZE dup(?) _wPad2 WORD ? CHARFORMATA ENDS
| Nombre de campo | Descripcion | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cbSize |
El tamaņo de la estructura. RichEdit usa este campo
para determinar la versión de la estructura si es CHARFORMAT o CHARFORMAT2
|
||||||||||||||||||||
| dwMask | Bit indicadores [bit flags] que determinan cuáles
miembros son válidos.
|
||||||||||||||||||||
| dwEffects | Los efectos de caracteres. Pueden ser una combinación
de los valores siguientes
|
||||||||||||||||||||
|
yHeight
|
Altura de los caracteres, en twips (1/1440 de pulgada
o 1/20 de puntos de impresora).
|
||||||||||||||||||||
|
yOffset
|
Desplazamiento de
los caracteres, en twips, desde la linea base. Si el valor de este miembro
es positivo, el caracter es un superscript; si es negativo es un subscript.
|
||||||||||||||||||||
|
crTextColor
|
Color del Texto. Este miembro es ignorado si se ha
especificado el efecto de caracter CFE_AUTOCOLOR .
|
||||||||||||||||||||
|
bCharSet
|
Valor del Caracter 'set'
|
||||||||||||||||||||
|
bPitchAndFamily
|
Fuente "family and pitch".
|
||||||||||||||||||||
|
szFaceName
|
Caracter terminado en NULL, array de caracteres
especificando el nombre de la fuente
|
||||||||||||||||||||
|
_wPad2
|
Relleno
|
Examinando la estructura, verás que podemos cambiar los efectos del texto (negrita, itálica, tachado, subrayado), el color de texto (crTextColor), el tamaño de la fuente tamaņo, etc.. Una bandera [flag] notable es CFE_RPOTECTED. El texto con esta bandera está marcado como protegido, lo cual significa que cuando el usuario intente modificarlo, se enviará el mensaje de notificación EN_PROTECTED a la ventana padre. Y aquí, puedes permitirlo o no.
CHARFORMAT2 aņade mas estilos de texto tales como fuente, altura, espaciado, color de texto y de fondo, etc. Si no necesitas estas caracterrísticas extras simplemente usa CHARFORMAT.
Para poner texto formateado, has de pensar en el rango del texto que quieres aplicarle. El Richedit introduce la noción de Rango de caracter del texto. El Richedit da a cada caracter un número comenzando desde cero: El primer caracter en el control tiene el ID número cero, el segundo el uno y así sucesivamente. Para especificar el rango de un texto, debes dar al richedit dos números, el ID del primero caracter del rango y el del último. Para aplicar el formato de texto con EM_SETCHARFORMAT, tienes al menos tres opciones:
La primera y segunda opción son "straightforward" (Directas). La última opción requiere una aclaratoria. Si la selección actual sólo cubre uno o mas de los caracteres en la palabra pero no la palabra completa, especificando la bandera SCF_WORD+SCF_SELECTION se aplicará el formato de texto a la palabra completa, incluso si no hay una selección en ese momento, por ej. Sólo el cursor está colocado en la palabra. La tercera opción también aplica el formato de texto a la palabra completa.
Para usar EM_SETCHARFORMAT, necesitas rellenar varios miembros de la estructura CHARFORMAT (o CHARFORMAT2) . Por ejemplo si queremos poner el color del texto, llenaremos la estructura CHARFORMAT como sigue:
.data?
cf CHARFORMAT <>
....
.code
mov cf.cbSize,sizeof cf
mov
cf.dwMask,CFM_COLOR
mov cf.crTextColor,0FF0000h
invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr
cf
El recorte de código de arriba pone el
color de texto del control richedit en azul puro. Nota que si no hay texto en
el control richedit cuando EM_SETCHARFORMAT sea emitido, el texto introducido en el control richedit
seguirá al mensaje que usará el formato de texto especificado
por el mensaje EM_SETCHARFORMAT.
Aquellos que han usado el control edit seguramente estarán familiarizados con los mensajes WM_GETTEXT/WM_SETTEXT, que significan obtener texto/poner texto del control. Este método aún funciona con el control richedit pero puede no ser eficiente si el archivo es muy grande. El control Edit limita el texto que puede ser introducido a 64KB, pero el control richedit puede aceptar una cantidad de texto mucho mayor. Sería muy engorroso localizar un bloque de memoria muy grande (tal como 10 MB) para recibir el texto usando el mensaje WM_GETTEXT. El control richedit ofrece una nueva aproximación a este método, por ej. tratamiento del texto como un stream (flujo continuo) de datos [text streamming]. (NOTA: la palabra 'stream', que se puede traducir como "flujo", designa aquí una cadena continua de datos, con un tamaño determinado).
Para ponerlo simplemente, provees la dirección de una función callback para el control richedit, y éste la llamará pasándole la dirección del buffer cuando esté listo. La función callback llenará el buffer con los datos deseados para enviar al control o leer los datos desde el buffer y entonces espera la siguiente llamada hasta que la operación termina. Este paradigma es usado por ambos streams: introducir el stream [streaming in] y sacando el stream [streaming out]. Verás que estos métodos son mas eficientes: el buffer es provisto por el control richedit así que los datos son divididos en pedazos. Las operaciones involucran dos mensajes: EM_STREAMIN y EM_STREAMOUT
Ambos EM_STREAMIN y EM_STREAMOUT usan la misma sintaxis:
wParam == opciones de formateo.
|
SF_RTF
|
Los datos en el texto enriquecido están en
formato RTF
|
|
SF_TEXT
|
Los datos están en formato de texto plano
|
|
SFF_PLAINRTF
|
Sólo las palabras claves comunes a todos los
lenguajes son introducidas [streamed in].
|
|
SFF_SELECTION
|
Si es especificada, el objetivo de la operación
es el texto actualmente seleccionado. Si tu vuelcas el texto en el control
richeedit (stream in), el texto reemplazará la selección
actual. Si sacas ( stream out ) el texto, sólo el texto seleccionado
actualmente es sacado (streamed out). Si esta bandera no está especificado
la operación afectará a todo el texto del control.
|
|
SF_UNICODE
|
( Disponible en el RichEdit 2.0 o posterior ) Especifica
el texto unicode.
|
lParam == Puntero a una estructura EDITSTREAM la cual tiene la definición siguiente:
EDITSTREAM STRUCT
dwCookie DWORD ?
dwError DWORD ?
pfnCallback DWORD ?
EDITSTREAM ENDS
|
dwCookie
|
Valor definido por la aplicación que será
pasado a la función callback y que está especificado en
el miembro pfnCallback de abajo. Normalmente pasamos algún
valor importante a la función callback tal como el manejador [handle]
del archivo para que sea usado en el procedimiento stream-in/out (lectura
/escritura de stream).
|
|
dwError
|
Indica el resultado de las operaciones de stream-in
(lectura) o stream-out (escritura). Un valor de cero indica que no hay
error. Un valor que no sea cero puede ser el valor de retorno de la función
EditStreamCallback o un código indicando que el control ha encontrado
un error.
|
|
pfnCallback
|
Puntero a una función EditStreamCallback la
cual es una función definida por la aplicacion que controla las
llamadas para transferir datos . El control llama a la función
callback repetidamente transfiriendo una porción de los datos en
cada llamada
|
La función callback del editor de streams tiene la siguiente definición:
EditStreamCallback proto dwCookie:DWORD,
pBuffer:DWORD, NumBytes:DWORD, pBytesTransferred:DWORD
Puedes crear una función en tu programa con el prototipo de arriba y pasarle la dirección de EM_STREAMIN o EM_STREAMOUT a través de la estructura EDITSTREAM.
Para operaciones stream-in (Poner el texto en el control richedit):
dwCookie: El valor definido por la aplicación que pasas a EM_STREAMIN a través de la estructura EDITSTREAM. Casi siempre pasamos aquí el handle del archivo que queremos poner su contenido al control. pBuffer: puntero al buffer provisto por el control richedit que recibirá el texto desde tu función callback. NumBytes: El número maximo de bytes que puedes escribir al buffer (pBuffer) en esta llamada. Siempre DEBES obedecer este límite, por ej. se pueden enviar menos datos que el valor en NumBytes pero nunca mas. Puedes pensar en este valor como el tamaņo del buffer en pBuffer. pBytesTransferred: Puntero al dword en el que debes poner el valor indicando el número de bytes realmente transferidos al buffer. Este valor es usualmente idéntico al valor en NumBytes. La excepción es cuando los datos enviados son menores que el tamaņo del buffer provisto, como cuando se halla el final del archivo .
Para operaciones stream-out (obteniendo el texto del control richedit):
dwCookie: Igual que en la operación stream-in. Usualmente pasamos el handle del archivo al que queremos escribir los datos en este parámetro. pBuffer: puntero al buffer provisto por el control richedit que es llenado con los datos del control richedit. Para obtener este tamaņo debes examinar el valor de NumBytes. NumBytes: El tamaņo de los datos en el buffer apuntado por pBuffer. pBytesTransferred: Puntero a un dword en el que debes poner el valor indicando el número de bytes actualmente leídos del buffer.
La función callback devuelve 0 para indicar éxito y el control richedit continuara llamando a la función callback si todavía hay datos para leer/escribir. Si ocurre algún error durante el proceso y quieres parar la operación, devuelve un valor distinto de cero al control richedit y descartará el dato apuntado por pBuffer. El valor de error/éxito será puesto en el campo dwError de EDITSTREAM, así puedes examinar el estado de error/éxito de la operación con el stream, después que regresa la llamada a SendMessage .
Este ejemplo es un simple editor, con él puedes abrir un archivo de de código fuente en asm, editarlo y guardarlo. He usado control richedit versión 2.0 o superior.
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\gdi32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
.const
IDR_MAINMENU equ 101
IDM_OPEN equ 40001
IDM_SAVE equ 40002
IDM_CLOSE equ 40003
IDM_SAVEAS equ 40004
IDM_EXIT equ 40005
IDM_COPY equ 40006
IDM_CUT equ 40007
IDM_PASTE equ 40008
IDM_DELETE equ 40009
IDM_SELECTALL equ 40010
IDM_OPTION equ 40011
IDM_UNDO equ 40012
IDM_REDO equ 40013
IDD_OPTIONDLG equ 101
IDC_BACKCOLORBOX equ 1000
IDC_TEXTCOLORBOX equ 1001
RichEditID equ 300
.data
ClassName db "IczEditClass",0
AppName db "IczEdit version 1.0",0
RichEditDLL db "riched20.dll",0
RichEditClass db "RichEdit20A",0
NoRichEdit db "Cannot find riched20.dll",0
ASMFilterString db "ASM Source code (*.asm)",0,"*.asm",0
db "All Files (*.*)",0,"*.*",0,0
OpenFileFail db "Cannot open the file",0
WannaSave db "The data in the control is modified. Want to save it?",0
FileOpened dd FALSE
BackgroundColor dd 0FFFFFFh ; blanco por defecto
TextColor dd 0 ; negro por defecto
.data?
hInstance dd ?
hRichEdit dd ?
hwndRichEdit dd ?
FileName db 256 dup(?)
AlternateFileName db 256 dup(?)
CustomColors dd 16 dup(?)
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke LoadLibrary,addr RichEditDLL
.if eax!=0
mov hRichEdit,eax
invoke WinMain, hInstance,0,0, SW_SHOWDEFAULT
invoke FreeLibrary,hRichEdit
.else
invoke MessageBox,0,addr NoRichEdit,addr AppName,MB_OK or MB_ICONERROR
.endif
invoke ExitProcess,eax
WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:DWORD
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,IDR_MAINMENU
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,0,0,0
.break .if (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain endp
StreamInProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesRead:DWORD
invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0
xor eax,1
ret
StreamInProc endp
StreamOutProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesWritten:DWORD
invoke WriteFile,hFile,pBuffer,NumBytes,pBytesWritten,0
xor eax,1
ret
StreamOutProc endp
CheckModifyState proc hWnd:DWORD
invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0
.if eax!=0
invoke MessageBox,hWnd,addr WannaSave,addr AppName,MB_YESNOCANCEL
.if eax==IDYES
invoke SendMessage,hWnd,WM_COMMAND,IDM_SAVE,0
.elseif eax==IDCANCEL
mov eax,FALSE
ret
.endif
.endif
mov eax,TRUE
ret
CheckModifyState endp
SetColor proc
LOCAL cfm:CHARFORMAT
invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor
invoke RtlZeroMemory,addr cfm,sizeof cfm
mov cfm.cbSize,sizeof cfm
mov cfm.dwMask,CFM_COLOR
push TextColor
pop cfm.crTextColor
invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cfm
ret
SetColor endp
OptionProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL clr:CHOOSECOLOR
.if uMsg==WM_INITDIALOG
.elseif uMsg==WM_COMMAND
mov eax,wParam
shr eax,16
.if ax==BN_CLICKED
mov eax,wParam
.if ax==IDCANCEL
invoke SendMessage,hWnd,WM_CLOSE,0,0
.elseif ax==IDC_BACKCOLORBOX
invoke RtlZeroMemory,addr clr,sizeof clr
mov clr.lStructSize,sizeof clr
push hWnd
pop clr.hwndOwner
push hInstance
pop clr.hInstance
push BackgroundColor
pop clr.rgbResult
mov clr.lpCustColors,offset CustomColors
mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
invoke ChooseColor,addr clr
.if eax!=0
push clr.rgbResult
pop BackgroundColor
invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
invoke InvalidateRect,eax,0,TRUE
.endif
.elseif ax==IDC_TEXTCOLORBOX
invoke RtlZeroMemory,addr clr,sizeof clr
mov clr.lStructSize,sizeof clr
push hWnd
pop clr.hwndOwner
push hInstance
pop clr.hInstance
push TextColor
pop clr.rgbResult
mov clr.lpCustColors,offset CustomColors
mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
invoke ChooseColor,addr clr
.if eax!=0
push clr.rgbResult
pop TextColor
invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX
invoke InvalidateRect,eax,0,TRUE
.endif
.elseif ax==IDOK
;==================================================================================
; Guardar el estado del control richedit porque al cambiar el color del texto se cambia el
; estado de modificación del control richedit.
;==================================================================================
invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0
push eax
invoke SetColor
pop eax
invoke SendMessage,hwndRichEdit,EM_SETMODIFY,eax,0
invoke EndDialog,hWnd,0
.endif
.endif
.elseif uMsg==WM_CTLCOLORSTATIC
invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
.if eax==lParam
invoke CreateSolidBrush,BackgroundColor
ret
.else
invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX
.if eax==lParam
invoke CreateSolidBrush,TextColor
ret
.endif
.endif
mov eax,FALSE
ret
.elseif uMsg==WM_CLOSE
invoke EndDialog,hWnd,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
OptionProc endp
WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL chrg:CHARRANGE
LOCAL ofn:OPENFILENAME
LOCAL buffer[256]:BYTE
LOCAL editstream:EDITSTREAM
LOCAL hFile:DWORD
.if uMsg==WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr RichEditClass,0,WS_CHILD or WS_VISIBLE or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_NOHIDESEL,\
CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,RichEditID,hInstance,0
mov hwndRichEdit,eax
;=============================================================
; Se establece el límite del texto. El valor por defecto es 64K
;=============================================================
invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
;=============================================================
; Se establece el color del fondo del texto por defecto
;=============================================================
invoke SetColor
invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0
.elseif uMsg==WM_INITMENUPOPUP
mov eax,lParam
.if ax==0 ; file menu
.if FileOpened==TRUE ; a file is already opened
invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED
.else
invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED
.endif
.elseif ax==1 ; edit menu
;=============================================================================
; Chequear si hay algún texto en el clipboard. Si es así, habilitamos pegar [paste] en el ítem del menú
;=============================================================================
invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
.if eax==0 ; no text in the clipboard
invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
.else
invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
.endif
;==========================================================
; Chequear si la solicitud de deshacer [undo] está vacía
;==========================================================
invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0
.if eax==0
invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED
.else
invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED
.endif
;=========================================================
; check whether the redo queue is empty
;=========================================================
invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
.if eax==0
invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED
.else
invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED
.endif
;=========================================================
; Chequear si hay una selección actual en el control richedit.
; Si lo hay, habilitamos los ítems de menú cut/copy/delete [cortar/copiar/suprimir]
;=========================================================
invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
mov eax,chrg.cpMin
.if eax==chrg.cpMax ; no current selection
invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED
.else
invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED
.endif
.endif
.elseif uMsg==WM_COMMAND
.if lParam==0 ; órdenes de menu
mov eax,wParam
.if ax==IDM_OPEN
invoke RtlZeroMemory,addr ofn,sizeof ofn
mov ofn.lStructSize,sizeof ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter,offset ASMFilterString
mov ofn.lpstrFile,offset FileName
mov byte ptr [FileName],0
mov ofn.nMaxFile,sizeof FileName
mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
invoke GetOpenFileName,addr ofn
.if eax!=0
invoke CreateFile,addr FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
mov hFile,eax
;================================================================
; introducir el stream de texto en el control richedit
;================================================================
mov editstream.dwCookie,eax
mov editstream.pfnCallback,offset StreamInProc
invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream
;==========================================================
; Inicializar el estado de modificación como falso
;==========================================================
invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
invoke CloseHandle,hFile
mov FileOpened,TRUE
.else
invoke MessageBox,hWnd,addr OpenFileFail,addr AppName,MB_OK or MB_ICONERROR
.endif
.endif
.elseif ax==IDM_CLOSE
invoke CheckModifyState,hWnd
.if eax==TRUE
invoke SetWindowText,hwndRichEdit,0
mov FileOpened,FALSE
.endif
.elseif ax==IDM_SAVE
invoke CreateFile,addr FileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
@@:
mov hFile,eax
;================================================================
; llevar el stream de texto al archivo
;================================================================
mov editstream.dwCookie,eax
mov editstream.pfnCallback,offset StreamOutProc
invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT,addr editstream
;==========================================================
; Inicializar el estado de modificación como falso
;==========================================================
invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
invoke CloseHandle,hFile
.else
invoke MessageBox,hWnd,addr OpenFileFail,addr AppName,MB_OK or MB_ICONERROR
.endif
.elseif ax==IDM_COPY
invoke SendMessage,hwndRichEdit,WM_COPY,0,0
.elseif ax==IDM_CUT
invoke SendMessage,hwndRichEdit,WM_CUT,0,0
.elseif ax==IDM_PASTE
invoke SendMessage,hwndRichEdit,WM_PASTE,0,0
.elseif ax==IDM_DELETE
invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0
.elseif ax==IDM_SELECTALL
mov chrg.cpMin,0
mov chrg.cpMax,-1
invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg
.elseif ax==IDM_UNDO
invoke SendMessage,hwndRichEdit,EM_UNDO,0,0
.elseif ax==IDM_REDO
invoke SendMessage,hwndRichEdit,EM_REDO,0,0
.elseif ax==IDM_OPTION
invoke DialogBoxParam,hInstance,IDD_OPTIONDLG,hWnd,addr OptionProc,0
.elseif ax==IDM_SAVEAS
invoke RtlZeroMemory,addr ofn,sizeof ofn
mov ofn.lStructSize,sizeof ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter,offset ASMFilterString
mov ofn.lpstrFile,offset AlternateFileName
mov byte ptr [AlternateFileName],0
mov ofn.nMaxFile,sizeof AlternateFileName
mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
invoke GetSaveFileName,addr ofn
.if eax!=0
invoke CreateFile,addr AlternateFileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
jmp @B
.endif
.endif
.elseif ax==IDM_EXIT
invoke SendMessage,hWnd,WM_CLOSE,0,0
.endif
.endif
.elseif uMsg==WM_CLOSE
invoke CheckModifyState,hWnd
.if eax==TRUE
invoke DestroyWindow,hWnd
.endif
.elseif uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
and eax,0FFFFh
shr edx,16
invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
end start
;===================================================================
; El archivo de recursos
;===================================================================
#include "resource.h"
#define IDR_MAINMENU 101
#define IDD_OPTIONDLG 101
#define IDC_BACKCOLORBOX 1000
#define IDC_TEXTCOLORBOX 1001
#define IDM_OPEN 40001
#define IDM_SAVE 40002
#define IDM_CLOSE 40003
#define IDM_SAVEAS 40004
#define IDM_EXIT 40005
#define IDM_COPY 40006
#define IDM_CUT 40007
#define IDM_PASTE 40008
#define IDM_DELETE 40009
#define IDM_SELECTALL 40010
#define IDM_OPTION 40011
#define IDM_UNDO 40012
#define IDM_REDO 40013
IDR_MAINMENU MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", IDM_OPEN
MENUITEM "&Close", IDM_CLOSE
MENUITEM "&Save", IDM_SAVE
MENUITEM "Save &As", IDM_SAVEAS
MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_EXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "&Undo", IDM_UNDO
MENUITEM "&Redo", IDM_REDO
MENUITEM "&Copy", IDM_COPY
MENUITEM "C&ut", IDM_CUT
MENUITEM "&Paste", IDM_PASTE
MENUITEM SEPARATOR
MENUITEM "&Delete", IDM_DELETE
MENUITEM SEPARATOR
MENUITEM "Select &All", IDM_SELECTALL
END
MENUITEM "Options", IDM_OPTION
END
IDD_OPTIONDLG DIALOG DISCARDABLE 0, 0, 183, 54
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_CENTER
CAPTION "Options"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,137,7,39,14
PUSHBUTTON "Cancel",IDCANCEL,137,25,39,14
GROUPBOX "",IDC_STATIC,5,0,124,49
LTEXT "Background Color:",IDC_STATIC,20,14,60,8
LTEXT "",IDC_BACKCOLORBOX,85,11,28,14,SS_NOTIFY | WS_BORDER
LTEXT "Text Color:",IDC_STATIC,20,33,35,8
LTEXT "",IDC_TEXTCOLORBOX,85,29,28,14,SS_NOTIFY | WS_BORDER
END
El programa primero carga la dll
correspondiente al control richedit, que en este caso es riched20.dll. Si no
puede ser cargada la dll saldrá a Windows.
invoke LoadLibrary,addr RichEditDLL .if eax!=0 mov hRichEdit,eax invoke WinMain,hInstance,0,0, SW_SHOWDEFAULT invoke FreeLibrary,hRichEdit .else invoke MessageBox,0,addr NoRichEdit,addr AppName,MB_OK or MB_ICONERROR .endif invoke ExitProcess,eax
Después que la dll es cargada con éxito procedemos a crear una ventana normal que será la ventana padre del control richedit. Dentro del manejador del mensaje WM_CREATE, creamos el control richedit:
invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr RichEditClass,0,WS_CHILD or WS_VISIBLE or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_NOHIDESEL,\ CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,RichEditID,hInstance,0 mov hwndRichEdit,eax
Nota que especificamos el estilo
ES_MULTILINE sino sería un control de sólo una linea
[single-lined].
invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
Después que el control richedit es creado, debemos ponerle el nuevo límite de texto. por defecto, el control richedit tiene 64KB de límite de texto lo mismo que un simple control Edit multilínea. Debemos extender este límite para permitir operaciones con archivos grandes. En la línea de arriba he puesto -1 el cual recoge la cantidad de 0FFFFFFFFh, un valor bien grande.
invoke SetColor
A continuación ponemos el
color del texto/fondo. Luego puede ser performateado en otra perte del programa.
He puesto el código en una función llamada SetColor.
SetColor proc LOCAL cfm:CHARFORMAT invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor
Poner el color de fondo del control
richedit es una operación directa: solo envia el mensaje EM_SETBKGNDCOLOR al control richedit. (si usas un control Edit multilínea,
debes procesar WM_CTLCOLOREDIT). El color por defecto es el blanco.
invoke RtlZeroMemory,addr cfm,sizeof cfm mov cfm.cbSize,sizeof cfm mov cfm.dwMask,CFM_COLOR push TextColor pop cfm.crTextColor
Tras haber puesto el color de fondo, llenamos los miembros de CHARFORMAT con el fin de poner el color del texto. Nota que llenamos cbSize con el tamaņo de la estructura, así el control richedit sabe que estamos enviándole CHARFORMAT, no CHARFORMAT2. dwMask sólo tiene una bandera, CFM_COLOR, y que indica sólo queremos poner el color del texto y crTextColor es llenado con el valor del color deseado para el texto.
invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cfm ret SetColor endp
Después de esto tienes vacío el buffer 'undo', simplemente porque el acto de cambiar el color del texto/fondo está habilitado para deshacer [undo]. Enviamos el mensaje EM_EMPTYUNDOBUFFER para lograr esto.
invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0
Tras rellenar la estructura CHARFORMAT enviamos EM_SETCHARFORMAT al control richedit, especificando la bandera [flag] SCF_ALL en wParam para indicar que queremos que el texto formateado sea aplicado a todo el texto en el control.
Nota que cuando creamos elprimero control richedit, no especificamos su tamaņo/posición en ese momento. esto es porque queremos cubrir todo el área cliente de la ventana padre. La redimensionamos cuando cambia el tamaņo de la ventana padre.
.elseif uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
and eax,0FFFFh
shr edx,16
invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE
En el código de arriba, usamos la nueva dimensión del area cliente pasada en lParam para redimensionar el control richedit con MoveWindow.
Cuando el usuario hace click sobre el menú File/Edit, procesamos WM_INITPOPUPMENU así que podemos preparar los estados de los items del menú en el submenú antes de mostrarlos al usuario. Por ejemplo, si un archivo ya está abierto en el control richedit, queremos desactivar el item del menú open y activar el resto de items del menú.
En el caso del menú File, usamos la variable FileOpened como la bandera [flag] para determinar si ya hay un archivo abierto. Si el valor de esta variable es TRUE, sabremos que ya hay un archivo abierto.
.elseif uMsg==WM_INITMENUPOPUP
mov eax,lParam
.if ax==0 ; file menu
.if FileOpened==TRUE ; a file is already opened
invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED
.else
invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED
.endif
Como puedes ver, si un archivo ya está abierto, difuminamos [grayed] el menú open y activamos el resto de ítems del menú. Ocurre lo contrario si FileOpened es false.
En el caso del menú edit, necesitamos mirar primero el estado del control richedit/clipboard.
invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
.if eax==0 ; no text in the clipboard
invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
.else
invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
.endif
Primero revisamos si hay algun texto en el clipboard enviando el mensaje EM_CANPASTE. Si lo hay, SendMessage devuelve TRUE y activamos el menú paste, sino, lo difuminamos [grayed].
invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0 .if eax==0 invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED .else invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED .endif
A continuacion miramos si el buffer 'undo' está vacío enviando el mensaje EM_CANUNDO, si no está vacío, SendMessage devuelve TRUE y activamos el menú undo.
invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
.if eax==0
invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED
.else invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED .endif
Revisiramos el buffer 'redo' enviando el mensaje EM_CANREDO al control richedit. si no está vacío, SendMessage devuelve TRUE y activamos el menú 'redo'.
invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg mov eax,chrg.cpMin
.if eax==chrg.cpMax ; no current selection
invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED
.else
invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED
.endif
por último miramos si existe una selección enviando el mensaje EM_EXGETSEL. Este mensaje usa una estructura CHARRANGE la cual se define como sigue:
CHARRANGE STRUCT
cpMin DWORD ?
cpMax DWORD ?
CHARRANGE ENDS
cpMin contiene el índice de posición del caracter
immediatamente precedente al primer caracter en el rango.
cpMax contiene la posición del caracter inmediatamente posterior
al último caracter en el rango.
Después que EM_EXGETSEL retorna, la estructura CHARRANGE es llenada con con el índice de la posición (starting-ending) principio/final del rango de selección. Si no hay selección, cpMin y cpMax son idénticos y ponemos en gris los menús cut/copy/delete.
Cuando el usuario hace clcik en el menú Open, mostramos el cuadro de dialogo "open file" y si el usuario selecciona un archivo abrimos el archivo y volcamos el contenido al control richedit.
invoke CreateFile,addr FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
mov hFile,eax
mov editstream.dwCookie,eax
mov editstream.pfnCallback,offset StreamInProc
invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream
Una vez que el archivo ha sido abierto exitosamente con CreateFile, rellenamos la estructura EDITSTREAM en preparación para el mensaje EM_STREAMIN. Escogemos enviar el manejador [handle] del archivo abierto a través del miembro dwCookie y pasamos la dirección de la función callback del stream en pfnCallback.
El procedimiento callback del stream es la esencia de la simplicidad.
StreamInProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesRead:DWORD
invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0
xor eax,1
ret
StreamInProc endp
Puedes ver todos los parámetros del procedimiento callback del stream perfectamente adaptado a ReadFile. Y el valor de retorno de ReadFile es "xoredado" con 1 asi que si devuelve 1 (éxito), el valor actual devuelto en eax es 0 y viceversa.
invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0 invoke CloseHandle,hFile mov FileOpened,TRUE
Después que EM_STREAMIN retorna, significa que la operación stream se ha completado. En realidad, debemos mirar el valor del miembro dwError de la estructura EDITSTREAM.
El Richedit (y edit) control soportan una bandera para indicar que el contenido ha cambiado. Podemos obtener el valor de esta bandera enviando el mensaje EM_GETMODIFY al control. SendMessage devuelve TRUE si este ha cambiado. Al volcar el texto al control, es una clase de modificación. Debemos poner la bandera de modificación a FALSE enviando EM_SETMODIFY con wParam==FALSE para comenzar de nuevo después que termine. la operación 'stream-in'. Inmediatamente cerramos el archivo y ponemos FileOpened a TRUE para indicar que el archivo fue abierto.
Cuando el usuario hace click sobre los menús Guardar/Guardar, usamos el mensaje EM_STREAMOUT para sacar el contenido del control richedit a un archivo. Como con la función stream-in callback, la función stream-out callback es simple. Se adapta perfectamente a WriteFile.
Las operaciones tales como cut/copy/paste/redo/undo son faciles de implementar enviando un simple mensaje al control richedit, WM_CUT/WM_COPY/WM_PASTE/WM_REDO/WM_UNDO respectivamente.
Las operaciones 'delete/select all', son como sigue:
.elseif ax==IDM_DELETE
invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0
.elseif ax==IDM_SELECTALL
mov chrg.cpMin,0
mov chrg.cpMax,-1
invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg
La operación 'delete' afecta a la actual selección. Envio el mensaje EM_REPLACESEL con la dirección de la cadena igual a cero o NULL, así el control richedit reemplazará el texto actualmente seleccionado con una cadena nula.
La operación 'select-all', se hace enviando el mensaje EM_EXSETSEL, especificando cpMin==0 y cpMax==-1 lo cual recoge todo el texto seleccionándolo.
Cuando el usuario selecciona la barra de menú 'Option', mostramos un cuadro de diálogo presentando el actual color de fondo/texto.

Cuando el usuario hace click sobre
una de las cajas de color, se muestra el diálogo de 'elegir color' ['choose-color'].
La caja de colores es de hecho un control estático con la bandera SS_NOTIFY y WS_BORDER. Un control
estático con la bandera SS_NOTIFY notificará a su ventana padre con las acciones del ratón,
tales como, BN_CLICKED (STN_CLICKED). Ese es el truco.
.elseif ax==IDC_BACKCOLORBOX
invoke RtlZeroMemory,addr clr,sizeof clr
mov clr.lStructSize,sizeof clr
push hWnd pop clr.hwndOwner
push hInstance
pop clr.hInstance
push BackgroundColor
pop clr.rgbResult
mov clr.lpCustColors,offset CustomColors
mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT
invoke ChooseColor,addr clr
.if eax!=0
push clr.rgbResult
pop BackgroundColor
invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
invoke InvalidateRect,eax,0,TRUE .endif
Cuando el usuario hace click sobre un color, rellenamos el miembro de la estructura CHOOSECOLOR y llamamos a ChooseColor para mostrar el diálogo "choose-color". Si el usuario selecciona un color, el valor 'colorref' es devuelto en el miembro rgbResult y guardamos el valor en la variable BackgroundColor. Después de esto, forzamos que se vuelva a pintar sobre la caja de color llamando a InvalidateRect sobre el manejador [handle] de la caja de color. La caja de color envía el mensaje WM_CTLCOLORSTATIC a su ventana padre.
invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
.if eax==lParam
invoke CreateSolidBrush,BackgroundColor
ret
Dentro del manejador [handle] WM_CTLCOLORSTATIC comparamos el manejador [handle] del control estático pasado en lParam para ambas cajas de color. Si el valor coincide creamos una nueva brocha usando el color de la variable e inmediatamente regresamos. Usaremos la nueva brocha para pintar el fondo.
[Iczelion's Win32 Assembly Homepage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por mnemox, y revisado por n u M I T_o r