📄 tut35.html
字号:
<html>
<head>
<title>Iczelion's Win32 Assembly Tutorial 35: RichEdit Control: Syntax Hilighting</title>
<meta http-equiv="Content-Type" content="text/html; charset=">
</head>
<body bgcolor="#FFFFFF">
<h1 align="center"><font face="Tahoma" color="#0000FF">Tutorial 35: RichEdit Control:
Syntax Hilighting</font></h1>
<p align="left"><font face="Tahoma" size="-1">Before reading this tutorial, let
me warn you that it's a complicated subject: not suited for a beginner. This
is the last in the richedit control tutorials.</font></p>
<p align="left"><font face="Tahoma" size="-1">Download <a href="files/tut35.zip">the
example</a>.</font></p>
<h3 align="left"><font face="Times New Roman, Times, serif" color="#0000FF">Theory</font></h3>
<p align="left"><font face="Tahoma" size="-1">Syntax hilighting is a subject of
hot debate for those writing text editors. The best method (in my opinion) is
to code a custom edit control and this is the approach taken by lots of commercial
softwares. However, for those of us who don't have time for coding such control,
the next best thing is to adapt the existing control to make it suit our need.</font></p>
<p align="left"><font face="Tahoma" size="-1">Let us take a look at what RichEdit
control provides to help us in implementing syntax hilighting. I should state
at this moment that the following method is not the "correct" path:
I just want to show you the pitfall that many fall for. RichEdit control provides
<font color="#006666"> <b>EM_SETCHARFORMAT</b></font> message that you can use
to change the color of the text. At first glance, this message seems to be the
perfect solution (I know because I was one of the victim). However, a closer
examination will show you several things that are undesirable:</font></p>
<ul>
<li><font face="Tahoma" size="-1"><b><font color="#006666">EM_SETCHARFORMAT</font></b>
only works for a text currently in selection or all text in the control. If
you want to change the text color (hilight) a certain word, you must first
select it.</font></li>
<li><font face="Tahoma" size="-1"><b><font color="#006666">EM_SETCHARFORMAT</font></b>
is very slow</font></li>
<li><font face="Tahoma" size="-1">It has a problem with the caret position in
the richedit control</font></li>
</ul>
<p><font face="Tahoma" size="-1">With the above discussion, you can see that using
<font color="#006666"> <b>EM_SETCHARFORMAT</b></font> is a wrong choice. I'll
show you the "relatively correct" choice.</font></p>
<p><font face="Tahoma" size="-1">The method I currently use is "syntax hilighting
just-in-time". I'll hilight only the visible portion of text. Thus the
speed of the hilighting will not be related to the size of the file at all.
No matter how large the file, only a small portion of it is visible at one time.</font></p>
<p><font face="Tahoma" size="-1">How to do that? The answer is simple: </font></p>
<ol>
<li><font face="Tahoma" size="-1">subclass the richedit control and handle <font color="#006666"><b>WM_PAINT</b></font>
message within your own window procedure</font></li>
<li><font face="Tahoma" size="-1">When it receives <font color="#006666"><b>WM_PAINT</b></font>
message, it calls the original window procedure of the richedit control to
let it update the screen as usual.</font></li>
<li><font face="Tahoma" size="-1">After that, we overwrite the words to be hilighted
with different color</font></li>
</ol>
<p><font face="Tahoma" size="-1">Of course, the road is not that easy: there are
still a couple of minor things to fix but the above method works quite nicely.
The display speed is very satisfactory.</font></p>
<p><font face="Tahoma" size="-1">Now let's concentrate on the detail. The subclassing
process is simple and doesn't require much attention. The really complicated
part is when we have to find a fast way of searching for the words to be hilighted.
This is further complicated by the need <font color="#006666"><b>not</b></font>
to hilight any word within a comment block. </font></p>
<p><font face="Tahoma" size="-1">The method I use may not be the best but it works
ok. I'm sure you can find a faster way. Anyway, here it is:</font></p>
<ul>
<li><font face="Tahoma" size="-1">I create a 256 dword array, initialized to
0. Each dword corresponds to a possible ASCII character,named <font color="#0000CC"><b>ASMSyntaxArray</b></font>.
For example, the 21th dword represents the ascii 20h (space). I use them as
a fast lookup table: For example, if I have the word "include",
I'll extract the first character (i) from the word and look up the dword at
the corresponding index. If that dword is 0, I know immediately that there
is no words to be hilighted starting with "i". If the dword is non-zero,
it contains the pointer to the linked list of the <font color="#006666"><b>WORDINFO</b></font>
structure which contains the information about the word to be hilighted.</font></li>
<li><font face="Tahoma" size="-1">I read the words to be hilighted and create
a <font color="#006666"><b>WORDINFO</b></font> structure for each of them.</font></li>
</ul>
<pre><font face="Tahoma"><b> WORDINFO struct
WordLen dd ? ; the length of the word: used as a quick comparison
pszWord dd ? ; pointer to the word
pColor dd ? ; point to the dword that contains the color used to hilite the word
NextLink dd ? ; point to the next WORDINFO structure
WORDINFO ends </b></font>
</pre>
<blockquote>
<p><font face="Tahoma" size="-1">As you can see, I use the length of the word
as the second quick comparison. If the first character of the word matches,
we next compare its length to the available words. Each dword in <font color="#0000CC"><b>ASMSyntaxArray</b></font>
contains a pointer to the head of the associated <font color="#006666"><b>WORDINFO</b></font>
array. For example, the dword that represents the character "i"
will contain the pointer to the linked list of the words that begin with "i".
<font color="#990099"> <b>pColor</b></font> member points to the dword that
contains the color value used to hilight the word. <font color="#990099"><b>pszWord</b></font>
points to the word to be hilighted, in lowercase.</font></p>
</blockquote>
<ul>
<li><font face="Tahoma" size="-1">The memory for the linked list is allocated
from the default heap so it's fast and easy to clean up, ie, no cleaning up
required at all.</font></li>
</ul>
<p><font face="Tahoma" size="-1">The word list is stored in a file named "wordfile.txt"
and I access it with <font color="#000099"><b>GetPrivateProfileString</b></font>
APIs. I provide as many as 10 different syntax coloring, starting from C1 to
C10. The color array is named <font color="#006666"><b>ASMColorArray</b></font>.
pColor member of each <font color="#006666"><b>WORDINFO</b></font> structure
points to one of the dwords in <font color="#006666"><b>ASMColorArray</b></font>.
Thus it is easy to change the syntax coloring on the fly: you just change the
dword in <font color="#006666"><b>ASMColorArray</b></font> and all words using
that color will use the new color immediately. </font></p>
<h3><font face="Times New Roman, Times, serif" color="#0000CC">Example</font></h3>
<pre><font face="Tahoma"><b>.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 ? ; the length of the word: used as a quick comparison
pszWord dd ? ; pointer to the word
pColor dd ? ; point to the dword that contains the color used to hilite the word
NextLink dd ? ; point to the next WORDINFO structure
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 ; default to white
TextColor dd 0 ; default to black
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 to the search/replace dialog box
hAccel dd ?
hMainHeap dd ? ; heap handle
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -