老文章档案 · 2003 年

RichEdit 控件:语法高亮显示

天下资讯 · 破解文章 · 2003-10-17 · 作者:人气: · 出处:赢政天下整理收藏)

在读这篇教程之前先提醒你,这是一个复杂的主题:不适合初学者。这是最后一篇RichEdit 控件教程。

下载 例子程序.

Theory

语法高亮显示对那些编写文本编辑器的人来说是一个热点主题。最好的解决方法(我自己认为的)是编写一个定制的Edit控件,这也是很多商业软件所使用的方法。然而,对于那些没什么时间来编写这么一个控件的人来说,次策就是改写现有的控件使之符合我们的需要。

让我们看一看,到底 RichEdit 控件提供了什么功能来帮助我们实现语法高亮。现在我应该声明,下面的方法不是一个“正确”方法:我只是给你们指出那些缺陷。 RichEdit 控件提供了EM_SETCHARFORMAT 消息,是你可以用来改变正文颜色。乍一看,这个消息好象是一个完美的解决方法(我之所以知道是因为我也是其中的一个受骗者)。然而靠近看看,将会发现有几个不合意的地方:

EM_SETCHARFORMAT 仅仅对选定的正文或者控件中所有的正文有影响。如果你想改变正文颜色(高亮显示某一个特定的词),你必须先选定它。

EM_SETCHARFORMAT 执行速度很慢。

RichEdit 控件中的插入点位置处理也有一点问题。

通过上面的讨论,你可以看到使用 EM_SETCHARFORMAT 是一个错误的选择,我会给你演示 "相当正确" 的选择。

我现在使用的方法是“即时语法高亮”,我只高亮显示可见部分的正文。因此高亮显示的速度跟文件的大小根本是无关的。无论是多大的文件,在某一时刻只有一小部分是可见的。

怎么样实现?答案很简单:

子类化RichEdit控件并在你自己的窗口处理函数中处理 WM_PAINT 消息。

当收到 WM_PAINT 消息时,它调用RichEdit控件原来的窗口过程,让它正常地更新屏幕。

之后,我们将要高亮显示的词用不同的颜色来覆盖掉。

当然了,路也不是这么容易走的:仍然有两个次要的问题需要矫正,不过上面的方法工作起来很好。显示速度令人很满意。

现在让我们集中在细节上。子类化处理是很简单的,不需要很多注意力。真正复杂的部分是:我们必须找到一个快速的方法来搜索那些需要高亮的词。更复杂的那些在某个注释块里的 不 需要高亮显示的词。

我使用的方法可能不是最好的,但是它工作的很好。我敢肯定你可以找到更快的方法。不管怎么说,先看看我下面的方法:

我创建一个有256元素的双字(DWORD)数组,全部初始化为0。每一个元素对应一个可能的 ASCII 字符,数组名叫 ASMSyntaxArray。例如,第21个元素代表ASCII 20h (空格字符)。我将他们作为快速查询表使用:譬如,假定我有一个词 "include",我从词中分离出第一个字符 (i) ,并以响应索引查找数组。如果该元素为0,我就立刻知道需要高亮的词是没有以 "i" 开头的。如果该元素非0,它就包含一个指针,指向一个 WORDINFO 结构的链表。里面包含了需要高亮词的信息。

我读取需要高亮显示的词,并为每个词创建一个 WORDINFO 结构。 WORDINFO struct

WordLen dd ? ; 词的长度,用来快速比较

pszWord dd ? ; 词的指针

pColor dd ? ; 用来高亮显示的颜色所在的DWORD的指针

NextLink dd ? ; 下一个 WORDINFO 结构

WORDINFO ends

正如你所看到的,我使用词的长度来作为第二个快速比较方法。如果词中的第一个字符匹配后,我们下一个比较的是词的才长度。ASMSyntaxArray 中的每一个元素包含了一个指针,指向一个相关的WORDINFO 数组.例如,代表字符 "i" 的元素将会包含一个指向以"i"开头的词的链表。 pColor 成员指向一个DWORD,包含用来做高亮显示该词的颜色值。pszWord 指向要高亮显示的词。是小写形式的。

链表的内存是从堆(heap)中分配的,速度快,容易清除,也就是说根本不用清楚。

高亮词列表保存在文件 "wordfile.txt"中,我通过 GetPrivateProfileString API 函数来访问。我提供了多达10种不同的语法颜色,从 C1 到 C10。颜色数组名叫 ASMColorArray。每一个 WORDINFO 结构的 pColor 成员都指向 ASMColorArray 中的某一个元素。因此闲时很容易改变语法颜色:你只需要改变 ASMColorArray 中的元素的值,这样所有使用那种颜色高亮的词就立刻使用新颜色显示。

例子

.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 ? ; 词的长度:用来进行快速比较

pszWord dd ? ; 词的指针

pColor dd ? ; 指向某个DWORD,里面包含用来高亮显示词的颜色值。

NextLink dd ? ; 指向下一个 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 ; 缺省为白色

TextColor dd 0 ; 缺省为黑色

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 ? ; 搜索/替换对话框的句柄

hAccel dd ?

hMainHeap dd ? ; 堆的句柄

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 ; 剪贴板里没有正文

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 ; 当前没有选定正文

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 ; 乘以 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 ; 乘以 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 ; 缓冲区太小

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 ; 缓冲区太小

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 ;缓冲区太小

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 ; 缓冲区太小

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 ; 缓冲区太小

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 ; 缓冲区太小

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 ; 缓冲区太小

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 ; 缓冲区太小

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 ; 缓冲区太小

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 ; 缓冲区太小

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 == 正文大小

mov BufferSize,eax

push edi

push ebx

lea edi,buffer

mov edx,edi ; 用做参照点

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 是缓冲区长度

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 为字符串长度

movzx eax,byte ptr [esi]

.if al>="A" && al<="Z"

sub al,"A"

add al,"a"

.endif

shl eax,2

add eax,edi ; edi 为 WORDINFO 指针数组的指针

.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 ; 说明该消息没被处理

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 菜单

.if FileOpened==TRUE ; 已经打开了一个文件

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 菜单

invoke PrepareEditMenu,wParam

.elseif ax==2 ; search 菜单条

.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 ; 菜单命令

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

分析:

第一步,在调用 WinMain 之前先调用 FillHiliteInfo。这个函数读出 wordfile.txt 的内容然后解析该内容。

FillHiliteInfo proc uses edi

LOCAL buffer[1024]:BYTE

LOCAL pTemp:DWORD

LOCAL BlockSize:DWORD

invoke RtlZeroMemory,addr ASMSyntaxArray,sizeof ASMSyntaxArray

初始化 ASMSyntaxArray 为 0 。

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

构建 of wordfile.txt 的完全路径: 我假定该文件总是跟程序文件在同一个文件夹。

invoke GetFileAttributes,addr buffer

.if eax!=-1

我使用这个方法来快速检验一个文件是否存在。

mov BlockSize,1024*10

invoke HeapAlloc,hMainHeap,0,BlockSize

mov pTemp,eax

分配内存块来保存高亮词。缺省是10K。是从缺省堆中分配的。

@@:

invoke GetPrivateProfileString,addr ASMSection,addr C1Key,addr ZeroString,pTemp,BlockSize,addr buffer

.if eax!=0

我使用 GetPrivateProfileString 来检索 wordfile.txt 中每一个关键字的内容。关键字从 C1 到 C10。

inc eax

.if eax==BlockSize ; 缓冲区太小

add BlockSize,1024*10

invoke HeapReAlloc,hMainHeap,0,pTemp,BlockSize

mov pTemp,eax

jmp @B

.endif

检查内存块是否足够大。如果不够的话,我们就增大10K,直到足够大为止。

mov edx,offset ASMColorArray

invoke ParseBuffer,hMainHeap,pTemp,eax,edx,addr ASMSyntaxArray

传递需要高亮显示的词的缓冲区, 内存块句柄,从WordFile.txt中读出的数据的大小,用来高亮显示词的颜色数组的地址,和ASMSyntaxArray的地址。

现在让我们来看看 ParseBuffer 做了什么。基本上,这个函数接收包含需要高亮显示词的缓冲区,将它们解析为单个的词并将它们每个都保存在一个 WORDINFO 结构数组中,这样可以从 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 是一个标志,我用来指示扫描处理是否已经开始。如果它的值是FALSE,表示我们还没遇到一个非空格字符。

lea esi,buffer

mov edi,pBuffer

invoke CharLower,edi

ESI 指向一个局部缓冲区,用来保存从词表中分离出来的单个词。EDI 指向词表字符串。为了方便以后的搜索,我们将所有的词都转换为小写形式。

mov ecx,nSize

SearchLoop:

or ecx,ecx

jz Finished

cmp byte ptr [edi]," "

je EndOfWord

cmp byte ptr [edi],9 ; tab

je EndOfWord

扫描缓冲区里的整个词表,搜索空格(White Space)字符。如果找到的话,我们必须判断它表示一个词的结束还是开始。

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

如果找到了一个空格字符,我们就检查 InProgress 的值。如果值为 TRUE ,我们可以假定空格标识一个词的结束,我们可以继续将解析出来的在局部缓冲区(由ESI指向)中的词保存到一个 WORDINFO 结构中。如果值为 FALSE ,我们继续扫描直到发现一个非空白(non-white space)的空格字符。

WordFound:

mov byte ptr [esi],0

push ecx

invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,sizeof WORDINFO

到达词的结尾后,我们就在词缓冲区尾加一个0,标识该词是一个 ASCIIZ 字符串。然后我们从堆中为该词分配一块 WORDINFO 内存。

push esi

mov esi,eax

assume esi:ptr WORDINFO

invoke lstrlen,addr buffer

mov [esi].WordLen,eax

我们取得局部变量中该词的长度,保存在 WORDINFO 结构的 WordLen 成员中,以后用来快速比较。

push ArrayOffset

pop [esi].pColor

将包含用来高亮显示该词的颜色的双字的地址保存到 pColor 成员中。

inc eax

invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax

mov [esi].pszWord,eax

mov edx,eax

invoke lstrcpy,edx,addr buffer

从堆中分配一块内存用来保存词本身。现在 WORDINFO 结构已经都填充好了,可以插入到适当的链表中去。

mov eax,pArray

movzx edx,byte ptr [buffer]

shl edx,2 ; 乘以 4

add eax,edx

pArray包含的是 ASMSyntaxArray 的地址。我们要移动到跟词中第一个字符具有相同索引的DWORD上。因此我们将词中的第一个字符放入EDX,然后将EDX乘以4(因为 ASMSyntaxArray 中的每个元素都是 4 字节大小),再将偏移量加到 ASMSyntaxArray 的地址上,我们就在EAX中得到相应DWORD的地址。

.if dword ptr [eax]==0

mov dword ptr [eax],esi

.else

push dword ptr [eax]

pop [esi].NextLink

mov dword ptr [eax],esi

.endif

检查那个DWORD的值,如果为 0 , 意味着当前链表中没有词是以这个字符开头的。因此我们将当前 WORDINFO 结构的地址保存到那个DWORD中。

如果值为非0, 意味着数组中至少有一个词是以这个字符开头的,因此我们将这个WORDINFO 结构插入到该链表头并更新其 NextLink 成员来指向下一个WORDINFO 结构。

pop esi

pop ecx

lea esi,buffer

mov InProgress,FALSE

jmp SkipIt

完成这个操作后,我们就开始下一轮扫描循环直到到达缓冲区的尾。

invoke SendMessage,hwndRichEdit,EM_SETTYPOGRAPHYOPTIONS,TO_SIMPLELINEBREAK,TO_SIMPLELINEBREAK

invoke SendMessage,hwndRichEdit,EM_GETTYPOGRAPHYOPTIONS,1,1

.if eax==0 ; 说明该消息没被处理

mov RichEditVersion,2

.else

mov RichEditVersion,3

invoke SendMessage,hwndRichEdit,EM_SETEDITSTYLE,SES_EMULATESYSEDIT,SES_EMULATESYSEDIT

.endif

创建RichEdit控件之后,我们需要确定其版本。这一步很必要,因为EM_POSFROMCHAR 在 RichEdit 2.0 和 3.0 中是不同的,而 EM_POSFROMCHAR 对于我们的语法高亮显示程序是至关重要的。我从来没见到有文档介绍到检查RichEdit控件版本的方法,因此我必须使用一个解决办法(workaround)。在这种情况下,我设置一个选项指定 3.0 版本并立即检索它的值。如果我能检索到该值的话我就假设该控件是3.0版本的。

如果你使用 RichEdit 3.0 ,你会注意到更改一个大文件的正文颜色会花费很长的时间。这个问题好象是3.0版本特有的。我找到一个解决办法:通过发送EM_SETEDITSTYLE 消息,使控件模拟系统Edit控件行为。

取得版本信息后,我们继续下一步,子类化RichEdit控件。现在我们检查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

我们处理 WM_PAINT 消息。首先,我们隐藏光标以便避免一些高亮后的难看的gfx (ugly gfx)。我们发送WM_PAINT消息给 RichEdit的原来的处理过程,让它更新窗口。CallWindowProc 返回后,正文已经被使用原来的颜色/背景色更新过了。现在是我们做语法高亮显示的机会了!

mov edi,offset ASMSyntaxArray

invoke GetDC,hWnd

mov hdc,eax

invoke SetBkMode,hdc,TRANSPARENT

保存 ASMSyntaxArray 的地址到 EDI。然后我们取得DC的句柄,设置正文背景模式为 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

我们要取得可见的正文,因此我们首先必须发送 EM_GETRECT 消息给RichEdit控件来取得格式化矩形(formatting rectangle)。现在我们有了范围矩形,我们通过EM_CHARFROMPOS消息,取得矩形左上角的最接近的字符索引 。一旦我们有了字符索引(控件中第一个可见字符), 我们就可以从该位置开始做语法高亮显示了。但是效果可能没有从该字符所在行的第一个字符开始高亮显示的效果好。这就是为什么我需要通过 EM_LINEFROMCHAR 消息去取得第一个可见字符所在行的行号的原因。为了取得该行的第一个字符,我需要发送 EM_LINEINDEX 消息。

mov txtrange.chrg.cpMin,eax

mov FirstChar,eax

invoke SendMessage,hWnd,EM_CHARFROMPOS,0,addr rect.right

mov txtrange.chrg.cpMax,eax

取得第一个字符的索引后,将之保存到变量 FirstChar 中以便以后引用。跟着我们来取得最后一个可见字符的索引,通过发送 EM_CHARFROMPOS 消息,在 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

在进行语法高亮显示时,我注意到这个方法的一个不好看的边缘效果:如果RichEdit控件有页边空白的话(你可以通过发送 EM_SETMARGINS 消息给RichEdit控件来指定页边空白), DrawText 写出了页边空白。 因而我需要创建一个裁剪区域,大小为矩形框的大小,这可以通过调用 CreateRectRgn 来实现。这样 GDI 函数的输出就会被裁剪在“可写”区域内。

跟着,我们需要先高亮显示注释,并将它们排除在目标之外。我使用的方法是搜索 ";" 并用注释颜色来高亮显示那些正文,直到遇到回车符。我在这里不分析这个过程了:它相当的长和复杂。现在可以说,当所有的注释高亮显示后,我们就在缓冲区中用 0 来替换掉它们,以便注释中的词在以后不会再被重复高亮处理。

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

将注释内容排除掉后,我们通过使用 0 替换掉分隔符,将缓冲区中的词表分开为一个个单个的词。用这个方法,我们在处理缓冲区中的词时就再不需要考虑分隔符了:只有一个分隔符,就是 NULL 。

lea esi,buffer

mov ecx,BufferSize

.while ecx>0

mov al,byte ptr [esi]

.if al!=0

搜索缓冲区找到第一个非NULL字符,也就是某个词的第一个字符。

push ecx

invoke lstrlen,esi

push eax

mov edx,eax

取得词的长度并保存到 EDX

movzx eax,byte ptr [esi]

.if al>="A" && al<="Z"

sub al,"A"

add al,"a"

.endif

将字符转换为小写的 (如果是大写字符的话)

shl eax,2

add eax,edi ; edi 为 WORDINFO 指针数组的指针

.if dword ptr [eax]!=0

之后,我们跳到 ASMSyntaxArray 中的相应的DWORD,并检查该DWORD的值是否为 0 ,是的话我们就跳到下一个词。

mov eax,dword ptr [eax]

assume eax:ptr WORDINFO

.while eax!=0

.if edx==[eax].WordLen

如果DWORD的值不为 0 ,它就指向一个 WORDINFO 结构的链表。我们跟着遍历该链表,将局部缓冲区中词的长度跟 WORDINFO 结构中词的长度进行比较。这是在我们比较词之前进行的快速测试,可以节约一些时钟周期。

pushad

invoke lstrcmpi,[eax].pszWord,esi

.if eax==0

如果两个词的长度相等,我们就继续使用 lstrcmpi 比较两个词。

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

知道需要高亮显示的词的第一个字符的字符索引后,我们跟着通过发送 EM_POSFROMCHAR 消息取得它的位置。然而这个消息在RichEdit 2.0 和 3.0 中的解释是不同的。对RichEdit 2.0 来说,wParam 包含字符索引,lParam 没有使用。它在 EAX 返回 坐标。对 RichEdit 3.0 来说,wParam 是一个指向 POINT 结构的指针,该调用将会将结果坐标填入这里,而 lParam 则包含字符索引。

正如你看到的,如果传递错误的参数给 EM_POSFROMCHAR ,它可以对你的系统造成重大破坏。这个就是为什么我必须区分 RichEdit 控件的版本的缘故。

mov edx,[eax].pColor

invoke SetTextColor,hdc,dword ptr [edx]

invoke DrawText,hdc,esi,-1,addr rect,0

一旦我们取得了开始坐标,我们就使用 WORDINFO 结构中指定的颜色来设置正文颜色。跟着使用新颜色值重写该词。

As the final words, 这个方法可以从几个方面提高。例如,我取得从第一个可见行到最后一个可见行的所有正文。如果那些行很长的话,处理那些不可见的词会降低性能。你可以通过一行一行地取得真正的可见行来进行优化。同样,搜索算法也可以使用更多的不同方法来提高速度。不要受我的错误影响(Don't take me wrong):这个例子用来进行语法高亮显示的方法是快,但是它可以更快!:)

(出处:赢政天下整理收藏)

相关资讯:

相关软件:

→ 特别推荐

→ 热点TOP10

我为人人《《==》》人人为我

关于我们 |

帮助(?)

| 广告服务 |

版权声明 |

商业合作 |

发展历程 |

加盟我们 |

联系我们

Copyright © 1998-2003

winzheng.com All Rights Reserved.

繁体版:由华琦数码技术提供

Powered

by:MeSky.Net

2003-2004 年,宽带刚刚普及,下载站的文章区是许多人学电脑的第一课堂。
🕰 → 🤖 从那时到现在

当年一篇教程能让一台电脑焕然一新;今天的效率工具换成了 AI。 想知道当下最可靠的 AI 模型怎么选?看 赢政指数——我们用了 28 年的选软件严谨,现在用来评测大模型。

档案性质声明:本页为赢政天下(1998-2011)老文章的历史档案复原, 内容反映当年发表时的原貌——包括当年的技术表述、软件版本与观点,部分方法在今天已不适用或不合规,仅作互联网历史记录与研究用途, 不提供任何形式的下载。原始数据经由公共网络存档(Internet Archive)复原。发现问题请联系我们
← 返回老文章档案 · 软件史档案馆 · 赢政天下站史