在读这篇教程之前先提醒你,这是一个复杂的主题:不适合初学者。这是最后一篇RichEdit 控件教程。
下载 例子程序.
语法高亮显示对那些编写文本编辑器的人来说是一个热点主题。最好的解决方法(我自己认为的)是编写一个定制的Edit控件,这也是很多商业软件所使用的方法。然而,对于那些没什么时间来编写这么一个控件的人来说,次策就是改写现有的控件使之符合我们的需要。
让我们看一看,到底 RichEdit 控件提供了什么功能来帮助我们实现语法高亮。现在我应该声明,下面的方法不是一个“正确”方法:我只是给你们指出那些缺陷。 RichEdit 控件提供了EM_SETCHARFORMAT 消息,是你可以用来改变正文颜色。乍一看,这个消息好象是一个完美的解决方法(我之所以知道是因为我也是其中的一个受骗者)。然而靠近看看,将会发现有几个不合意的地方:
通过上面的讨论,你可以看到使用 EM_SETCHARFORMAT 是一个错误的选择,我会给你演示 "相当正确" 的选择。
我现在使用的方法是“即时语法高亮”,我只高亮显示可见部分的正文。因此高亮显示的速度跟文件的大小根本是无关的。无论是多大的文件,在某一时刻只有一小部分是可见的。
怎么样实现?答案很简单:
当然了,路也不是这么容易走的:仍然有两个次要的问题需要矫正,不过上面的方法工作起来很好。显示速度令人很满意。
现在让我们集中在细节上。子类化处理是很简单的,不需要很多注意力。真正复杂的部分是:我们必须找到一个快速的方法来搜索那些需要高亮的词。更复杂的那些在某个注释块里的 不 需要高亮显示的词。
我使用的方法可能不是最好的,但是它工作的很好。我敢肯定你可以找到更快的方法。不管怎么说,先看看我下面的方法:
WORDINFO struct WordLen dd ? ; 词的长度,用来快速比较 pszWord dd ? ; 词的指针 pColor dd ? ; 用来高亮显示的颜色所在的DWORD的指针 NextLink dd ? ; 下一个 WORDINFO 结构 WORDINFO ends
正如你所看到的,我使用词的长度来作为第二个快速比较方法。如果词中的第一个字符匹配后,我们下一个比较的是词的才长度。ASMSyntaxArray 中的每一个元素包含了一个指针,指向一个相关的WORDINFO 数组.例如,代表字符 "i" 的元素将会包含一个指向以"i"开头的词的链表。 pColor 成员指向一个DWORD,包含用来做高亮显示该词的颜色值。pszWord 指向要高亮显示的词。是小写形式的。
高亮词列表保存在文件 "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):这个例子用来进行语法高亮显示的方法是快,但是它可以更快!:)
翻译:farsky 来源:[Iczelion's Win32 Assembly
Homepage]
LuoYunBin's Win32 ASM Page, http://asm.yeah.net/