Antes de leer este tutorial, te advierto que es un tema complicado: no apto para principiantes. Este es el último de los tutoriales sobre el control richedit.
Baja el ejemplo.
El realce de la sintaxis es materia de gran debate entre los escritores de editores de texto. El mejor método(en mi opinión) es escribir el código de un control edit personalizado y esta es la aproximación tomada por gran cantidad de softwares comerciales. Sin embargo, los que no tenemos tiempo para escribir el código para un control de estos lo mejor que podemos hacer es adaptar el control existente a nuestras necesidades.
Echemos una mirada a lo que nos suministra el control RichEdit y que nos puede ayudar a implementar el realce de sintaxis. Debo dejar establecido en este momento que el siguiente método no es el camino "correcto": sólo quiero mostrar el pitfall en el que muchos caen. El control RichEdit suministra mensajes EM_SETCHARFORMAT que puedes usar para cambiar el colordel texto. A primera vista, este mensaje parecería la solución perfecta (lo sé porque fuí una de las víctimas). Sin embargo, un examen más detenido nos mostrará varias cosas que son indeseables:
Con la discusión anterior, puedes ver que usar EM_SETCHARFORMAT es una elección errónea. Te mostraré la opción "relativamente correcta".
El método que generalmente uso es "realce de sintaxis en el momento" (just syntax hilighting just-in-time). Resaltaré sólo una porción visible del texto. Así la velocidad del realce no será relativa a todo el tamaño del archivo. No importa cuan grande sea el archivo, sólo una pequeña parte de él es visible al mismo tiempo.
¿Cómo hacerlo? La respuesta es simple:
Por suspuesto, el camino no es fácil: todavía hay muchos detalles que cuidar pero el método trabaja muy bien. La velocidad de despliegue es satisfactoria.
Ahora concentrémosnos en los detalles. El proceso de subclasificación es simple y no requiere mucha atención. La parte realmente complicada es cuando tenemos que encontrar una forma rápida de buscar las palabras a ser resaltadas. Esto es complicado porque no se necesita resaltar cualquier palabra dentro del bloque comentado.
El método que uso puede no ser el mejor pero trabaja bien. Estoy seguro que puedes encontrar una forma más rápida. De cualquier manera, aquí está:
WORDINFO struct WordLen dd ? ; tamaño de la palabra: usado como una comparación rápida pszWord dd ? ; puntero a la palabra pColor dd ? ; puntero a la dword que contiene el color usado para resaltar la palabra NextLink dd ? ; puntero a la siguiente estructura WORDINFO WORDINFO ends
Como puedes ver, uso el tamaño de las palabras como la segunda comprobación rápida. Si el primer caracter de la palabra coincide, luego comparamos su tamaño con las palabras disponibles. Cada palabra en ASMSyntaxArray contiene un puntero al encabezado del array asociado WORDINFO. Por ejemplo, la dword que representa el caracter "i" contendrá el puntero a la lista enlazada de las palabras que comienzan con "i". El miembro pColor apunta a la dword que contiene el valor del color color usado para realsar la palabra. pszWord apunta a la palabra a ser realsada, en minúscula.
La lista de palabras es almacenada en un archivo llamado "wordfile.txt" y accedo a él con GetPrivateProfileString APIs. Suministro como 10 diferentes sintaxis de coloración, comenzando por C1 hasta C10. El array de color es llamado ASMColorArray. El miembro pColor de cada estructura WORDINFO structure apunta a una de las dwords en ASMColorArray. Así que es fácil cambiar la coloración de la sintaxis en el momento [on the fly]: sólo cambias la dword en ASMColorArray que usen ese color ahora cambiarán de color inmediatamente.
.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
WORDINFO struct
WordLen dd ? ; tamaño de la palabra: usada para una comparación rápida
pszWord dd ? ; puntero a la palabra
pColor dd ? ; puntero a la dword que contiene el color usado para ; resaltar la palabra
NextLink dd ? ; puntero a la siguiente estructura WORDINFO
WORDINFO ends
.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
IDR_MAINACCEL equ 105
IDD_FINDDLG equ 102
IDD_GOTODLG equ 103
IDD_REPLACEDLG equ 104
IDC_FINDEDIT equ 1000
IDC_MATCHCASE equ 1001
IDC_REPLACEEDIT equ 1001
IDC_WHOLEWORD equ 1002
IDC_DOWN equ 1003
IDC_UP equ 1004
IDC_LINENO equ 1005
IDM_FIND equ 40014
IDM_FINDNEXT equ 40015
IDM_REPLACE equ 40016
IDM_GOTOLINE equ 40017
IDM_FINDPREV equ 40018
RichEditID equ 300
.data
ClassName db "IczEditClass",0
AppName db "IczEdit version 3.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
WordFileName db "\wordfile.txt",0
ASMSection db "ASSEMBLY",0
C1Key db "C1",0
C2Key db "C2",0
C3Key db "C3",0
C4Key db "C4",0
C5Key db "C5",0
C6Key db "C6",0
C7Key db "C7",0
C8Key db "C8",0
C9Key db "C9",0
C10Key db "C10",0
ZeroString db 0
ASMColorArray dd 0FF0000h,0805F50h,0FFh,666F00h,44F0h,5F8754h,4 dup(0FF0000h)
CommentColor dd 808000h
.data?
hInstance dd ?
hRichEdit dd ?
hwndRichEdit dd ?
FileName db 256 dup(?)
AlternateFileName db 256 dup(?)
CustomColors dd 16 dup(?)
FindBuffer db 256 dup(?)
ReplaceBuffer db 256 dup(?)
uFlags dd ?
findtext FINDTEXTEX <>
ASMSyntaxArray dd 256 dup(?)
hSearch dd ? ; handle al cuadro de diálogo buscar/reemplazar
hAccel dd ?
hMainHeap dd ? ; handle del montículo [heap]
OldWndProc dd ?
RichEditVersion dd ?
.code
start:
mov byte ptr [FindBuffer],0
mov byte ptr [ReplaceBuffer],0
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke LoadLibrary,addr RichEditDLL
.if eax!=0
mov hRichEdit,eax
invoke GetProcessHeap
mov hMainHeap,eax
call FillHiliteInfo
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
invoke LoadAccelerators,hInstance,IDR_MAINACCEL
mov hAccel,eax
.while TRUE
invoke GetMessage, ADDR msg,0,0,0
.break .if (!eax)
invoke IsDialogMessage,hSearch,addr msg
.if eax==FALSE
invoke TranslateAccelerator,hwnd,hAccel,addr msg
.if eax==0
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endif
.endif
.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
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
SearchProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.if uMsg==WM_INITDIALOG
push hWnd
pop hSearch
invoke CheckRadioButton,hWnd,IDC_DOWN,IDC_UP,IDC_DOWN
invoke SendDlgItemMessage,hWnd,IDC_FINDEDIT,WM_SETTEXT,0,addr FindBuffer
.elseif uMsg==WM_COMMAND
mov eax,wParam
shr eax,16
.if ax==BN_CLICKED
mov eax,wParam
.if ax==IDOK
mov uFlags,0
invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr findtext.chrg
invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer
.if eax!=0
invoke IsDlgButtonChecked,hWnd,IDC_DOWN
.if eax==BST_CHECKED
or uFlags,FR_DOWN
mov eax,findtext.chrg.cpMin
.if eax!=findtext.chrg.cpMax
push findtext.chrg.cpMax
pop findtext.chrg.cpMin
.endif
mov findtext.chrg.cpMax,-1
.else
mov findtext.chrg.cpMax,0
.endif
invoke IsDlgButtonChecked,hWnd,IDC_MATCHCASE
.if eax==BST_CHECKED
or uFlags,FR_MATCHCASE
.endif
invoke IsDlgButtonChecked,hWnd,IDC_WHOLEWORD
.if eax==BST_CHECKED
or uFlags,FR_WHOLEWORD
.endif
mov findtext.lpstrText,offset FindBuffer
invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,uFlags,addr findtext
.if eax!=-1
invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText
.endif
.endif
.elseif ax==IDCANCEL
invoke SendMessage,hWnd,WM_CLOSE,0,0
.else
mov eax,FALSE
ret
.endif
.endif
.elseif uMsg==WM_CLOSE
mov hSearch,0
invoke EndDialog,hWnd,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
SearchProc endp
ReplaceProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL settext:SETTEXTEX
.if uMsg==WM_INITDIALOG
push hWnd
pop hSearch
invoke SetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer
invoke SetDlgItemText,hWnd,IDC_REPLACEEDIT,addr ReplaceBuffer
.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==IDOK
invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer
invoke GetDlgItemText,hWnd,IDC_REPLACEEDIT,addr ReplaceBuffer,sizeof ReplaceBuffer
mov findtext.chrg.cpMin,0
mov findtext.chrg.cpMax,-1
mov findtext.lpstrText,offset FindBuffer
mov settext.flags,ST_SELECTION
mov settext.codepage,CP_ACP
.while TRUE
invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,FR_DOWN,addr findtext
.if eax==-1
.break
.else
invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText
invoke SendMessage,hwndRichEdit,EM_SETTEXTEX,addr settext,addr ReplaceBuffer
.endif
.endw
.endif
.endif
.elseif uMsg==WM_CLOSE
mov hSearch,0
invoke EndDialog,hWnd,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
ReplaceProc endp
GoToProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL LineNo:DWORD
LOCAL chrg:CHARRANGE
.if uMsg==WM_INITDIALOG
push hWnd
pop hSearch
.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==IDOK
invoke GetDlgItemInt,hWnd,IDC_LINENO,NULL,FALSE
mov LineNo,eax
invoke SendMessage,hwndRichEdit,EM_GETLINECOUNT,0,0
.if eax>LineNo
invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0
invoke SendMessage,hwndRichEdit,EM_SETSEL,eax,eax
invoke SetFocus,hwndRichEdit
.endif
.endif
.endif
.elseif uMsg==WM_CLOSE
mov hSearch,0
invoke EndDialog,hWnd,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
GoToProc endp
PrepareEditMenu proc hSubMenu:DWORD
LOCAL chrg:CHARRANGE
invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
.if eax==0 ; no text in the clipboard
invoke EnableMenuItem,hSubMenu,IDM_PASTE,MF_GRAYED
.else
invoke EnableMenuItem,hSubMenu,IDM_PASTE,MF_ENABLED
.endif
invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0
.if eax==0
invoke EnableMenuItem,hSubMenu,IDM_UNDO,MF_GRAYED
.else
invoke EnableMenuItem,hSubMenu,IDM_UNDO,MF_ENABLED
.endif
invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0
.if eax==0
invoke EnableMenuItem,hSubMenu,IDM_REDO,MF_GRAYED
.else
invoke EnableMenuItem,hSubMenu,IDM_REDO,MF_ENABLED
.endif
invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
mov eax,chrg.cpMin
.if eax==chrg.cpMax ; no current selection
invoke EnableMenuItem,hSubMenu,IDM_COPY,MF_GRAYED
invoke EnableMenuItem,hSubMenu,IDM_CUT,MF_GRAYED
invoke EnableMenuItem,hSubMenu,IDM_DELETE,MF_GRAYED
.else
invoke EnableMenuItem,hSubMenu,IDM_COPY,MF_ENABLED
invoke EnableMenuItem,hSubMenu,IDM_CUT,MF_ENABLED
invoke EnableMenuItem,hSubMenu,IDM_DELETE,MF_ENABLED
.endif
ret
PrepareEditMenu endp
ParseBuffer proc uses edi esi hHeap:DWORD,pBuffer:DWORD, nSize:DWORD, ArrayOffset:DWORD,pArray:DWORD
LOCAL buffer[128]:BYTE
LOCAL InProgress:DWORD
mov InProgress,FALSE
lea esi,buffer
mov edi,pBuffer
invoke CharLower,edi
mov ecx,nSize
SearchLoop:
or ecx,ecx
jz Finished
cmp byte ptr [edi]," "
je EndOfWord
cmp byte ptr [edi],9 ; tab
je EndOfWord
mov InProgress,TRUE
mov al,byte ptr [edi]
mov byte ptr [esi],al
inc esi
SkipIt:
inc edi
dec ecx
jmp SearchLoop
EndOfWord:
cmp InProgress,TRUE
je WordFound
jmp SkipIt
WordFound:
mov byte ptr [esi],0
push ecx
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,sizeof WORDINFO
push esi
mov esi,eax
assume esi:ptr WORDINFO
invoke lstrlen,addr buffer
mov [esi].WordLen,eax
push ArrayOffset
pop [esi].pColor
inc eax
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax
mov [esi].pszWord,eax
mov edx,eax
invoke lstrcpy,edx,addr buffer
mov eax,pArray
movzx edx,byte ptr [buffer]
shl edx,2 ; multiply by 4
add eax,edx
.if dword ptr [eax]==0
mov dword ptr [eax],esi
.else
push dword ptr [eax]
pop [esi].NextLink
mov dword ptr [eax],esi
.endif
pop esi
pop ecx
lea esi,buffer
mov InProgress,FALSE
jmp SkipIt
Finished:
.if InProgress==TRUE
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,sizeof WORDINFO
push esi
mov esi,eax
assume esi:ptr WORDINFO
invoke lstrlen,addr buffer
mov [esi].WordLen,eax
push ArrayOffset
pop [esi].pColor
inc eax
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax
mov [esi].pszWord,eax
mov edx,eax
invoke lstrcpy,edx,addr buffer
mov eax,pArray
movzx edx,byte ptr [buffer]
shl edx,2 ; multiply by 4
add eax,edx
.if dword ptr [eax]==0
mov dword ptr [eax],esi
.else
push dword ptr [eax]
pop [esi].NextLink
mov dword ptr [eax],esi
.endif
pop esi
.endif
ret
ParseBuffer endp
FillHiliteInfo proc uses edi
LOCAL buffer[1024]:BYTE
LOCAL pTemp:DWORD
LOCAL BlockSize:DWORD
invoke RtlZeroMemory,addr ASMSyntaxArray,sizeof ASMSyntaxArray
invoke GetModuleFileName,hInstance,addr buffer,sizeof buffer
invoke lstrlen,addr buffer
mov ecx,eax
dec ecx
lea edi,buffer
add edi,ecx
std
mov al,"\"
repne scasb
cld
inc edi
mov byte ptr [edi],0
invoke lstrcat,addr buffer,addr WordFileName
invoke GetFileAttributes,addr buffer
.if eax!=-1
mov BlockSize,1024*10
invoke HeapAlloc,hMainHeap,0,BlockSize
mov pTemp,eax
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C1Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C2Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,4
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C3Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,8
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C4Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,12
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C5Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,16
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C6Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,20
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C7Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,24
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C8Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,28
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C9Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,32
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C10Key,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
mov edx,offset ASMColorArray
add edx,36
invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
.endif
invoke HeapFree,hMainHeap,0,pTemp
.endif
ret
FillHiliteInfo endp
NewRichEditProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL hdc:DWORD
LOCAL hOldFont:DWORD
LOCAL FirstChar:DWORD
LOCAL rect:RECT
LOCAL txtrange:TEXTRANGE
LOCAL buffer[1024*10]:BYTE
LOCAL hRgn:DWORD
LOCAL hOldRgn:DWORD
LOCAL RealRect:RECT
LOCAL pString:DWORD
LOCAL BufferSize:DWORD
LOCAL pt:POINT
.if uMsg==WM_PAINT
push edi
push esi
invoke HideCaret,hWnd
invoke CallWindowProc,OldWndProc,hWnd,uMsg,wParam,lParam
push eax
mov edi,offset ASMSyntaxArray
invoke GetDC,hWnd
mov hdc,eax
invoke SetBkMode,hdc,TRANSPARENT
invoke SendMessage,hWnd,EM_GETRECT,0,addr rect
invoke SendMessage,hWnd,EM_CHARFROMPOS,0,addr rect
invoke SendMessage,hWnd,EM_LINEFROMCHAR,eax,0
invoke SendMessage,hWnd,EM_LINEINDEX,eax,0
mov txtrange.chrg.cpMin,eax
mov FirstChar,eax
invoke SendMessage,hWnd,EM_CHARFROMPOS,0,addr rect.right
mov txtrange.chrg.cpMax,eax
push rect.left
pop RealRect.left
push rect.top
pop RealRect.top
push rect.right
pop RealRect.right
push rect.bottom
pop RealRect.bottom
invoke CreateRectRgn,RealRect.left,RealRect.top,RealRect.right,RealRect.bottom
mov hRgn,eax
invoke SelectObject,hdc,hRgn
mov hOldRgn,eax
invoke SetTextColor,hdc,CommentColor
lea eax,buffer
mov txtrange.lpstrText,eax
invoke SendMessage,hWnd,EM_GETTEXTRANGE,0,addr txtrange
.if eax>0
mov esi,eax ; esi == size of the text
mov BufferSize,eax
push edi
push ebx
lea edi,buffer
mov edx,edi ; used as the reference point
mov ecx,esi
mov al,";"
ScanMore:
repne scasb
je NextSkip
jmp NoMoreHit
NextSkip:
dec edi
inc ecx
mov pString,edi
mov ebx,edi
sub ebx,edx
add ebx,FirstChar
mov txtrange.chrg.cpMin,ebx
push eax
mov al,0Dh
repne scasb
pop eax
HiliteTheComment:
.if ecx>0
mov byte ptr [edi-1],0
.endif
mov ebx,edi
sub ebx,edx
add ebx,FirstChar
mov txtrange.chrg.cpMax,ebx
pushad
mov edi,pString
mov esi,txtrange.chrg.cpMax
sub esi,txtrange.chrg.cpMin ; esi contains the length of the buffer
mov eax,esi
push edi
.while eax>0
.if byte ptr [edi]==9
mov byte ptr [edi],0
.endif
inc edi
dec eax
.endw
pop edi
.while esi>0
.if byte ptr [edi]!=0
invoke lstrlen,edi
push eax
mov ecx,edi
lea edx,buffer
sub ecx,edx
add ecx,FirstChar
.if RichEditVersion==3
invoke SendMessage,hWnd,EM_POSFROMCHAR,addr rect,ecx
.else
invoke SendMessage,hWnd,EM_POSFROMCHAR,ecx,0
mov ecx,eax
and ecx,0FFFFh
mov rect.left,ecx
shr eax,16
mov rect.top,eax
.endif
invoke DrawText,hdc,edi,-1,addr rect,0
pop eax
add edi,eax
sub esi,eax
.else
inc edi
dec esi
.endif
.endw
mov ecx,txtrange.chrg.cpMax
sub ecx,txtrange.chrg.cpMin
invoke RtlZeroMemory,pString,ecx
popad
.if ecx>0
jmp ScanMore
.endif
NoMoreHit:
pop ebx
pop edi
mov ecx,BufferSize
lea esi,buffer
.while ecx>0
mov al,byte ptr [esi]
.if al==" " || al==0Dh || al=="/" || al=="," || al=="|" || al=="+" || al=="-" || al=="*" || al=="&" || al=="<" || al==">" || al=="=" || al=="(" || al==")" || al=="{" || al=="}" || al=="[" || al=="]" || al=="^" || al==":" || al==9
mov byte ptr [esi],0
.endif
dec ecx
inc esi
.endw
lea esi,buffer
mov ecx,BufferSize
.while ecx>0
mov al,byte ptr [esi]
.if al!=0
push ecx
invoke lstrlen,esi
push eax
mov edx,eax ; edx contains the length of the string
movzx eax,byte ptr [esi]
.if al>="A" && al<="Z"
sub al,"A"
add al,"a"
.endif
shl eax,2
add eax,edi ; edi contains the pointer to the WORDINFO pointer array
.if dword ptr [eax]!=0
mov eax,dword ptr [eax]
assume eax:ptr WORDINFO
.while eax!=0
.if edx==[eax].WordLen
pushad
invoke lstrcmpi,[eax].pszWord,esi
.if eax==0
popad
mov ecx,esi
lea edx,buffer
sub ecx,edx
add ecx,FirstChar
pushad
.if RichEditVersion==3
invoke SendMessage,hWnd,EM_POSFROMCHAR,addr rect,ecx
.else
invoke SendMessage,hWnd,EM_POSFROMCHAR,ecx,0
mov ecx,eax
and ecx,0FFFFh
mov rect.left,ecx
shr eax,16
mov rect.top,eax
.endif
popad
mov edx,[eax].pColor
invoke SetTextColor,hdc,dword ptr [edx]
invoke DrawText,hdc,esi,-1,addr rect,0
.break
.endif
popad
.endif
push [eax].NextLink
pop eax
.endw
.endif
pop eax
pop ecx
add esi,eax
sub ecx,eax
.else
inc esi
dec ecx
.endif
.endw
.endif
invoke SelectObject,hdc,hOldRgn
invoke DeleteObject,hRgn
invoke SelectObject,hdc,hOldFont
invoke ReleaseDC,hWnd,hdc
invoke ShowCaret,hWnd
pop eax
pop esi
pop edi
ret
.elseif uMsg==WM_CLOSE
invoke SetWindowLong,hWnd,GWL_WNDPROC,OldWndProc
.else
invoke CallWindowProc,OldWndProc,hWnd,uMsg,wParam,lParam
ret
.endif
NewRichEditProc endp
WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL ofn:OPENFILENAME
LOCAL buffer[256]:BYTE
LOCAL editstream:EDITSTREAM
LOCAL hFile:DWORD
LOCAL hPopup:DWORD
LOCAL pt:POINT
LOCAL chrg:CHARRANGE
.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
invoke SendMessage,hwndRichEdit,EM_SETTYPOGRAPHYOPTIONS,TO_SIMPLELINEBREAK,TO_SIMPLELINEBREAK
invoke SendMessage,hwndRichEdit,EM_GETTYPOGRAPHYOPTIONS,1,1
.if eax==0 ; means this message is not processed
mov RichEditVersion,2
.else
mov RichEditVersion,3
invoke SendMessage,hwndRichEdit,EM_SETEDITSTYLE,SES_EMULATESYSEDIT,SES_EMULATESYSEDIT
.endif
invoke SetWindowLong,hwndRichEdit,GWL_WNDPROC, addr NewRichEditProc
mov OldWndProc,eax
invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
invoke SetColor
invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
invoke SendMessage,hwndRichEdit,EM_SETEVENTMASK,0,ENM_MOUSEEVENTS
invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0
.elseif uMsg==WM_NOTIFY
push esi
mov esi,lParam
assume esi:ptr NMHDR
.if [esi].code==EN_MSGFILTER
assume esi:ptr MSGFILTER
.if [esi].msg==WM_RBUTTONDOWN
invoke GetMenu,hWnd
invoke GetSubMenu,eax,1
mov hPopup,eax
invoke PrepareEditMenu,hPopup
mov edx,[esi].lParam
mov ecx,edx
and edx,0FFFFh
shr ecx,16
mov pt.x,edx
mov pt.y,ecx
invoke ClientToScreen,hWnd,addr pt
invoke TrackPopupMenu,hPopup,TPM_LEFTALIGN or TPM_BOTTOMALIGN,pt.x,pt.y,NULL,hWnd,NULL
.endif
.endif
pop esi
.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
invoke PrepareEditMenu,wParam
.elseif ax==2 ; search menu bar
.if FileOpened==TRUE
invoke EnableMenuItem,wParam,IDM_FIND,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_FINDNEXT,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_FINDPREV,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_REPLACE,MF_ENABLED
invoke EnableMenuItem,wParam,IDM_GOTOLINE,MF_ENABLED
.else
invoke EnableMenuItem,wParam,IDM_FIND,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_FINDNEXT,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_FINDPREV,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_REPLACE,MF_GRAYED
invoke EnableMenuItem,wParam,IDM_GOTOLINE,MF_GRAYED
.endif
.endif
.elseif uMsg==WM_COMMAND
.if lParam==0 ; menu commands
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
mov editstream.dwCookie,eax
mov editstream.pfnCallback,offset StreamInProc
invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream
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
mov editstream.dwCookie,eax
mov editstream.pfnCallback,offset StreamOutProc
invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT,addr editstream
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_FIND
.if hSearch==0
invoke CreateDialogParam,hInstance,IDD_FINDDLG,hWnd,addr SearchProc,0
.endif
.elseif ax==IDM_REPLACE
.if hSearch==0
invoke CreateDialogParam,hInstance,IDD_REPLACEDLG,hWnd,addr ReplaceProc,0
.endif
.elseif ax==IDM_GOTOLINE
.if hSearch==0
invoke CreateDialogParam,hInstance,IDD_GOTODLG,hWnd,addr GoToProc,0
.endif
.elseif ax==IDM_FINDNEXT
invoke lstrlen,addr FindBuffer
.if eax!=0
invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr findtext.chrg
mov eax,findtext.chrg.cpMin
.if eax!=findtext.chrg.cpMax
push findtext.chrg.cpMax
pop findtext.chrg.cpMin
.endif
mov findtext.chrg.cpMax,-1
mov findtext.lpstrText,offset FindBuffer
invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,FR_DOWN,addr findtext
.if eax!=-1
invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText
.endif
.endif
.elseif ax==IDM_FINDPREV
invoke lstrlen,addr FindBuffer
.if eax!=0
invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr findtext.chrg
mov findtext.chrg.cpMax,0
mov findtext.lpstrText,offset FindBuffer
invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,0,addr findtext
.if eax!=-1
invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText
.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
La primera acción antes de llamar a WinMain es llamar a FillHiliteInfo. Esta función lee el contenido de wordfile.txt y analiza el contenido.
FillHiliteInfo proc uses edi LOCAL buffer[1024]:BYTE LOCAL pTemp:DWORD LOCAL BlockSize:DWORD invoke RtlZeroMemory,addr ASMSyntaxArray,sizeof ASMSyntaxArray
Se inicializa ASMSyntaxArray en cero.
invoke GetModuleFileName,hInstance,addr buffer,sizeof buffer invoke lstrlen,addr buffer mov ecx,eax dec ecx lea edi,buffer add edi,ecx std mov al,"\" repne scasb cld inc edi mov byte ptr [edi],0 invoke lstrcat,addr buffer,addr WordFileName
Construímos todo el nombre del camino hacia wordfile.txt: asumimos que siempre es el mismo
del programa.
invoke GetFileAttributes,addr buffer .if eax!=-1
Uso este método como una manera de chequear rápido si el fichero existe.
mov BlockSize,1024*10 invoke HeapAlloc,hMainHeap,0,BlockSize mov pTemp,eax
SE localiza el bloque de memoria para almacenar las words. Por defecto será 10K. La memoria
es localizada a partir del montículo por defecto.
@@:
invoke GetPrivateProfileString,addr ASMSection,addr C1Key,\
addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
Uso GetPrivateProfileString para recuperar el contenido
de cada clave en wordfile.txt. La clave va desde C1 hasta C10.
inc eax
.if eax==BlockSize ; the buffer is too small
add BlockSize,1024*10
invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize
mov pTemp,eax
jmp @B
.endif
Se chequea si el bloque de memoria es lo suficientemente grande. Si no lo es, incrementamos
el tamaño del bloque por 10K hasta que el bloque sea lo suficientemente grande.
mov edx,offset ASMColorArray invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray
Se pasan las palabras, el handle del bloque de memoria, el tamaño de los datos leídos de wordfile.txt, la dirección de la dword de color que será usada para resaltar las palabras y la dirección de ASMSyntaxArray.
Ahora vamos a examinar qué hace ParseBuffer. Básicamente,
esta función acepta el buffer que contiene las palabras a ser resaltadas, las pasa a palabras individuales y las almacena en un array de estructuras WORDINFO que puede ser accedida rápidamente desde ASMSyntaxArray.
ParseBuffer proc uses edi esi hHeap:DWORD,pBuffer:DWORD, nSize:DWORD, ArrayOffset:DWORD,pArray:DWORD LOCAL buffer[128]:BYTE LOCAL InProgress:DWORD mov InProgress,FALSE
InProgress es la bandera [flag] que uso para introducir si el proceso de revisión ha comenzado.
Si el valor es FALSE, no hemos encontrado un caracter de espacio en blanco.
lea esi,buffer mov edi,pBuffer invoke CharLower,edi
esi apunta a nuestro buffer local que contendrá la palabra que hemos parsed de la lista de
palabras. edi apunta a la cadena de lista de palabras. Para simplificar la búsqueda posterior, convertimos todos los caracteres en minúsculas.
mov ecx,nSize SearchLoop: or ecx,ecx jz Finished cmp byte ptr [edi]," " je EndOfWord cmp byte ptr [edi],9 ; tab je EndOfWord
Revisamos toda la lista de palabras en el buffer, fijándonos en los espacios en blanco. Si se ha encontrado un espacio en blanco, tenemos que determinar si marca el final del comienzo de una palabra.
mov InProgress,TRUE mov al,byte ptr [edi] mov byte ptr [esi],al inc esi SkipIt: inc edi dec ecx jmp SearchLoop
Si el byte que se está revisando no es un espacio en blanco, lo copiamos en el buffer para construir una palabra y luego seguimos con la revisión .
EndOfWord: cmp InProgress,TRUE je WordFound jmp SkipIt
Si se encuentra un espacio en blanco, chequeamos el valor en InProgress. Si el valor es TRUE, podemos asumir que el espacio en blanco marca el final de una palabra y podemos proceder a poner la palabra actual en el buffer local (apuntado por esi) dentro de una estructura WORDINFO. Si el valor es FALSE, seguimos la revisión hasta que encontramos un espacio en blanco.
WordFound: mov byte ptr [esi],0 push ecx invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,sizeof WORDINFO
Cuando encontramos el final de la palabra, anexamos 0 al buffer para hacer de la palabra una cadena ASCIIZ. Luego localizamos un bloque de memoria a partir del montículo con el tamaño de WORDINFO para esta palabra.
push esi mov esi,eax assume esi:ptr WORDINFO invoke lstrlen,addr buffer mov [esi].WordLen,eax
Obtenemos el tamaño de la palabre en el buffer local y lo almacenamos en el miembro WordLen de la estructura WORDINFO, a ser usada como una comparación rápida.
push ArrayOffset pop [esi].pColor
Almacenamos la dirección de la dword que contiene el color a ser usado para remarcar la palabra en el miembro pColor.
inc eax invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax mov [esi].pszWord,eax mov edx,eax invoke lstrcpy,edx,addr buffer
Localizamos memoria desde el montículo para almacenar la misma palabra. Bien, ahora la estructura WORDINFO está preparada para ser insertada en la lista enlazada apropiada.
mov eax,pArray movzx edx,byte ptr [buffer] shl edx,2 ; multiply by 4 add eax,edx
pArray contiene la dirección de ASMSyntaxArray. Queremos mover la dword que tiene el mismo índice que el valor del primer caracter de la palabra. Así que ponemos el valor del primer caracter de la palabra en edx y luego multiplicamos edx por 4 (porque cada elemento en ASMSyntaxArray tiene un tamaño de 4 bytes) y luego agregamos el desplazamiento de la dirección de ASMSyntaxArray. Tenemos en eax la dirección de la dword corespondiente.
.if dword ptr [eax]==0 mov dword ptr [eax],esi .else push dword ptr [eax] pop [esi].NextLink mov dword ptr [eax],esi .endif
Chequeamos el valor de la dword. Si es 0, significa que actualmente no hay una palabra que comience con este caracter en la lista. Así que ponemos la dirección de la estructura WORDINFO actual en esa dword.
Si el valor en la dword no es 0, significa que al menos hay una palabra que comienza con este caracter en el array. Así queinsertamos esta estructura WORDINFO en la cabeza de la lista enlazada y actualizamos su miembro NextLink para que apunte a la siguiente estructura WORDINFO.
pop esi pop ecx lea esi,buffer mov InProgress,FALSE jmp SkipIt
Después que la operación se haya completado, comezamos el siguiente ciclo de revisión hasta que alacanzamos el final del buffer.
invoke SendMessage,hwndRichEdit,EM_SETTYPOGRAPHYOPTIONS,TO_SIMPLELINEBREAK,TO_SIMPLELINEBREAK invoke SendMessage,hwndRichEdit,EM_GETTYPOGRAPHYOPTIONS,1,1 .if eax==0 ; significa que este mensaje no procede mov RichEditVersion,2 .else mov RichEditVersion,3 invoke SendMessage,hwndRichEdit,EM_SETEDITSTYLE,SES_EMULATESYSEDIT,SES_EMULATESYSEDIT .endif
Después de que es creado el control richedit necesitamos determinar su versión. Este paso es necesario porque EM_POSFROMCHAR se comporta de modo diferente para la versión 2.0 y la 3.0, y EM_POSFROMCHAR es crucial para nuestra rutina remarcadora de sintaxis. Nunca he visto documentada una manera de chequear la versión del control richedit, así que uso una estratagema [workaround]. En este caso, pongo una opción que es específica a la versión 3.0 e inmediatamente recupero su valor. Si puedo recuperar el valor, asumo que la versión del control es 3.0.
Si usas el control RichEdit versión 3.0, notarás que la actualización del color de la fuente para un archivo más largo, toma un poco más de tiempo. Este problema parece específico a la versión 3.0. Encuentro un estratagema [workaround]: haciendo que el control emule el comportamiento del control edit del sistema enviando un mensaje EM_SETEDITSTYLE.
Después de que podemos obtener información de la versión, procedemos a subclasificar el control richedit. Examinaremos ahora el procedimiento de ventana para el control RichEdit.
NewRichEditProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD ........ ....... .if uMsg==WM_PAINT push edi push esi invoke HideCaret,hWnd invoke CallWindowProc,OldWndProc,hWnd,uMsg,wParam,lParam push eax
Manejamos el mensaje WM_PAINT. Primero, escondemos la careta para evitar algún horrible efecto después de hacer el remarcado. Después de eso, pasamos elmensaje al procedimiento richedit original para que actualice la ventana. Cuando CallWindowProc regresa, el texto es actualizado con su usual color/fondo. Ahora es la oportunidad de hacer el remarcado de la sintaxis.
mov edi,offset ASMSyntaxArray invoke GetDC,hWnd mov hdc,eax invoke SetBkMode,hdc,TRANSPARENT
Almacenamos la dirección de ASMSyntaxArray en edi. Luego obtenemos el handle al contexto de dispositivo y ponemos el texto en modo de fondo para transparentar el texto que escribiremos
usando el color del fondo por defecto.
invoke SendMessage,hWnd,EM_GETRECT,0,addr rect invoke SendMessage,hWnd,EM_CHARFROMPOS,0,addr rect invoke SendMessage,hWnd,EM_LINEFROMCHAR,eax,0 invoke SendMessage,hWnd,EM_LINEINDEX,eax,0
Queremos obtener el texto visible, así que primero tenemos que obtener el rectángulo formateado enviando el mensaje EM_GETRECT al control richedit. Ahora que tenemos el rectángulo delineado, obtenemos el índice del caracter más cercano a la esquina izquierda superior del rectángulo con EM_CHARFROMPOS. Una vez que tenemos el índice del caracter (el primer caracter visible en el control), podemos comenzar a hacer remarcado de sintaxis comenzando desde esa posición. Pero el efecto no podría ser tan bueno como cuando comenzamos desde el primer caracter de la linea en la que está el caracter. Por esto necesito obtener el número de la línea en donde está ese primer caracter visible enviando el mensaje EM_LINEFROMCHAR. Para obtener el primer caracter de esa línea, envío el mensaje EM_LINEINDEX.
mov txtrange.chrg.cpMin,eax mov FirstChar,eax invoke SendMessage,hWnd,EM_CHARFROMPOS,0,addr rect.right mov txtrange.chrg.cpMax,eax
Una vez que tenemos el primer índice del caracter, lo almacenamos para una referencia futura para la variable FirstChar. Luego obtenemos el índice al último caracter visible enviando EM_CHARFROMPOS, pasando la esquina inferior derecha del rectángulo formateado en lParam.
push rect.left pop RealRect.left push rect.top pop RealRect.top push rect.right pop RealRect.right push rect.bottom pop RealRect.bottom invoke CreateRectRgn,RealRect.left,RealRect.top,RealRect.right,RealRect.bottom mov hRgn,eax invoke SelectObject,hdc,hRgn mov hOldRgn,eax
Mientras hacía el remarcado de sintaxis, noté un efecto antiestético colateral de este método: si el control richedit tiene un margen (puedes especificar un margen enviando el mensaje EM_SETMARGINS al control richedit), DrawText escribe sobre él. Así que necesitamos crear una región de recorte, el tamaño del rectángulo formateado, llamando a CreateRectRgn. La salida de las funciones GDI será recortada y limitada al área que puede ser escrita.
Luego, necesitamos remarcar primero los comentarios y sacarlos de nuestro camino. Mi método es buscar ";" y remarcar el texto con el color de comentario hasta encontrar un retorno de carro. No analizaré aquí la rutina: es demasiado larga y complicada. Es suficiente decir aquí que, cuando todos los comentarios están remarcados, los reemplazamos con 0s en el buffer de manera que las palabras en los comentarios no sean luego procesados/remarcados.
mov ecx,BufferSize
lea esi,buffer
.while ecx>0
mov al,byte ptr [esi]
.if al==" " || al==0Dh || al=="/" || al=="," || al=="|" || al=="+" || al=="-" || \
al=="*" || al=="&" || al=="<" || al==">" || al=="=" || al=="(" || al==")" || \
al=="{" || al=="}" || al=="[" || al=="]" || al=="^" || al==":" || al==9
mov byte ptr [esi],0
.endif
dec ecx
inc esi
.endwUna vez que los comentarios estén fuera de nuestro camino, separamos las palabras en el buffer poniendo en 0s en los separadores de caracteres. Con este método ya no tenemos que ver más con caracteres marcadores mientras procesamos las palabras en el buffer: hay sólo un caracter separador: NULL.
lea esi,buffer mov ecx,BufferSize .while ecx>0 mov al,byte ptr [esi] .if al!=0
Buscamos el primer caracter que no sea nulo, es decir, el primer caracter de una palabra.
push ecx invoke lstrlen,esi push eax mov edx,eax
Obtenemos el tamaño de la palabra y lo ponemos en edx
movzx eax,byte ptr [esi] .if al>="A" && al<="Z" sub al,"A" add al,"a" .endif
Convertimos el caracter en minúscula (si está en mayúscula)
shl eax,2 add eax,edi ; edi contiene un puntero al array de punteros WORDINFO .if dword ptr [eax]!=0
Después de eso, nos saltamos la correspondiente dword en ASMSyntaxArray y chequeamos si el valor en esa dword es 0. Si lo es, podemos saltar a la siguiente palabra.
mov eax,dword ptr [eax] assume eax:ptr WORDINFO .while eax!=0 .if edx==[eax].WordLen
Si el valor en la dword es diferente de cero, apunta a la lista enlazada de estructuras WORDINFO. Procedemos a revisar la lista enlazada, comparando el tamaño de la palabara en nuestro buffer local con el largo de la palabra en la estructura WORDINFO. Esta es una prueba rápida antes de comparar las palabras. Deberáa salvar unos ciclos del CPU.
pushad invoke lstrcmpi,[eax].pszWord,esi .if eax==0
Si el tamaño de ambas palabras es igual, procedemos a compararla con lstrcmpi.
popad mov ecx,esi lea edx,buffer sub ecx,edx add ecx,FirstChar
Construimos el índice de caracteres a partir de la dirección del primer caracter de la palabra que coincide en el buffer. Primero obtenemos su desplazamiento relativo a partir de la dirección inicial del buffer, luego agregamos el índice del caracter del primer caracter visible.
pushad .if RichEditVersion==3 invoke SendMessage,hWnd,EM_POSFROMCHAR,addr rect,ecx .else invoke SendMessage,hWnd,EM_POSFROMCHAR,ecx,0 mov ecx,eax and ecx,0FFFFh mov rect.left,ecx shr eax,16 mov rect.top,eax .endif popad
Una vez que conocemos el índice de caracter del primer caracter de la palabra a ser resaltada, procedemos a obtener su coordenada enviendo el mensaje EM_POSFROMCHAR. Sin embargo, este mensaje es interpretado de manera muy diferente por richedit 2.0 y 3.0. Para richedit 2.0, wParam contiene el índice del caracter y lParam no es usado. Regresa la coordenada en eax. Para richedit 3.0, wParam es el puntero a una estructura POINT que será llenada con la coordenada y lParam contiene el índice del caracter.
Como puedes ver, pasar los argumentos equivocados a EM_POSFROMCHAR puede hacer estragos [wreak havoc] en tu sistema. Por eso tengo que diferenciar entre las versiones de los controles RichEdit.
mov edx,[eax].pColor invoke SetTextColor,hdc,dword ptr [edx] invoke DrawText,hdc,esi,-1,addr rect,0
Una vez que he obtenido la coordenada para comenzar, ponemos el color del texto con el especificado en la estructura WORDINFO. Y luego procedemos a sobreescribir la palabra con el nuevo color.
En las palabras finales, este método puede ser mejorado de varias maneras. Por ejemplo, obtengo todos los textos que comienzan desde la primera hasta la última línea visible. Si las líneas son muy largas, la ejecución puede perjudicarse por procesar palabras que no son visibles. Puedes optimizar esto obteniendo el texto realmente visible línea por línea. También el algorritmo de búsqueda puede ser mejorado usando un método más eficiente. No creas que me he equivocado: el método de realce usado en este ejemplo es RÁPIDO pero puede ser más RÁPIDO:)
[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