| 关键词: parts 服务器 newparts 部分 username 密码 如果 password split 代码 |
有很多人要求我提供关于RichEdit控件的指南,经过这方面的大量编程实践,我想我终于可以开写关于RichEdit控件的指南了。诺,这个就是第一篇RichEdit指南。本指南将会描述涉及到RichEdit的几乎所有方面,起码是我所知道的关于它的所有方面。因为指南的信息量相当相当的大,所以我将它分成了几个部分,本篇是第一部分。在这篇指南里,你将会了解到什么是RichEdit控件,怎样创建它,怎样往里面载入数据和保存里面的数据。理论A richedit control can be thought of as a souped-up edit control. 它提供了普通简单Edit控件所缺少的很多令人合意的功能,譬如可以使用多种字体和字体大小,多级Undo/Redo,文本搜索,内嵌OLE对象,编辑的拖放支持等等。由于RichEdit控件有这么多功能,因此它独存于一个单独的DLL文件。这个意味着如果你要使用RichEdit控件,就不能象使用其他普通控件那样只调用InitCommonControls 函数。你必须先使用 LoadLibrary 来载入RichEdit DLL文件。这里有个问题,就是至今为止RichEdit控件共有3个版本,版本1,2和3。下面的表格显示了每个版本的相应的DLL文件名。DLL NameRichEdit versionRichedit Class NameRiched32.dll1.0RICHEDITRichEd20.dll2.0RICHEDIT20ARichEd20.dll3.0RICHEDIT20A你会注意到版本2和3都使用了相同的DLL文件名,而且它们也使用了相同的类名!这样在你想要明确的使用RichEdit 3.0的功能的时候就会产生问题. 到目前为止,我还找到一个正式的方法来区分版本2.0和3.0.不过,有个解决方法工作良好,我会在后面演示给你看..data RichEditDLL db "RichEd20.dll",0 ......data? hRichEditDLL dd ?.code invoke LoadLibrary,addr RichEditDLL mov hRichEditDLL,eax ...... invoke FreeLibrary,hRichEditDLL当Richedit DLL文件被载入时,它会注册 RichEdit 窗口类,因此需要在你创建Richedit控件之前必须先要载入DLL.Richedit控件的类名是不同的。现在你可能会有一个问题要提:我怎么样才能够知道我要使用哪个版本的Richedit控件?如果你不需要使用那些特别的功能,使用最新的版本也不定是最适宜的.下面的表格列出了每个RichEdit版本提供的功能.功能版本 1.0版本 2.0版本 3.0选择条xxxUnicode 编辑xx字符段落格式xxx文本搜索向前搜索向前/向后搜索向前/向后搜索内嵌OLExxx拖放编辑xxxUndo/Redo单级多级多级自动 URL 识别xx加速键支持xx非窗口操作xx分行符CRLFCRCR (可以模拟1.0版本)ZoomxParagraph numberingxsimple tablexnormal and heading stylesxunderline coloringxhidden textxfont bindingx上面的表格不是全面的:我只是列出了那些重要的功能.创建RchEdit 控件载入DLL文件后,你可以调用CreateWindowEx 来创建RichEdit控件,创建控件时你可以使用Edit控件风格和普通窗口风格,但是ES_LOWERCASE, ES_UPPERCASE 和 ES_OEMCONVERT 风格除外..constRichEditID equ 300.dataRichEditDLL db "RichEd20.dll",0 RichEditClass db "RichEdit20A",0....data? hRichEditDLL dd ?hwndRichEdit dd ?.code.....invoke LoadLibrary,addr RichEditDLLmov hRichEditDLL,eax invoke CreateWindowEx,0,addr RichEditClass,WS_VISIBLE or ES_MULTILINE or WS_CHILD or WS_VSCROLL or WS_HSCROLL, \CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,RichEditID,hInstance,0mov hwndRichEdit,eax设置缺省的文本和背景颜色在使用Edit控件时设置文本和背景颜色可能有点问题,但是在RichEdit控件中已经修正了这个问题.要设置RichEdit的背景色,你要发送 EM_SETBKGNDCOLOR 消息给RichEdit控件.这个消息具有以下语法.wParam == 颜色选项. 如果是0说明Windows使用 lParam 中的颜色值作为背景色.如果该参数不为0,Windows使用系统背景色.因为我们要发送该消息来改变背景色,所以我们必须令wParam为0.lParam == 指定要设置的颜色的 COLORREF 结构,仅当 wParam 为 0 时才有效.譬如, 如果我要设置背景为纯蓝色,我需要写入以下代码:invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,0FF0000h要设置文本颜色,RichEdit控件提供了另外的新消息,EM_SETCHARFORMAT来做这个工作. 这个消息控制控件中一段选定的文本或者全部正文的格式. 这个消息的语法如下:wParam == 格式选项:SCF_ALL 本次操作影响控件中全部文本.SCF_SELECTION 本次操作仅影响选定的文本SCF_WORD or SCF_SELECTION 本次操作仅影响所选定的那个单词. 如果选定是空的话,仅将插入点(光标)设置到指定字的位置. SCF_WORD 标志必须跟 SCF_SELECTION 一起使用.lParam == CHARFORMAT 或者 CHARFORMAT2 结构的指针,说明要使用的正文格式. CHARFORMAT2 只在 Richedit 2.0 和更高版本才提供.这个不是说在RichEdit 2.0以后你一定要使用 CHARFORMAT2 . 如果你不需要使用到 CHARFORMAT2 中新加的功能, 你仍然可以使用CHARFORMAT .CHARFORMATA STRUCTcbSize DWORD ?dwMask DWORD ?dwEffects DWORD ?yHeight DWORD ?yOffset DWORD ?crTextColor COLORREF ?bCharSet BYTE ?bPitchAndFamily BYTE ?szFaceName BYTE LF_FACESIZE dup(?)_wPad2 WORD ?CHARFORMATA ENDSField NameDescriptioncbSize该结构的大小. RichEdit控件使用该域来判断该结构的版本是 CHARFORMAT 还是 CHARFORMAT2dwMask位标记, 用来指定下面的那些成员是有效的.CFM_BOLDdwEffects 成语中的 CFE_BOLD 值是有效的.CFM_CHARSETbCharSet 成员是有效的。CFM_COLORcrTextColor 成员跟 dwEffects中的 CFE_AUTOCOLOR 值是有效的.CFM_FACEszFaceName 成员是有效的.CFM_ITALICdwEffects 成员中的 CFE_ITALIC 值是有效的.CFM_OFFSETyOffset 成员是有效的.CFM_PROTECTEDdwEffects 成员中的 CFE_PROTECTED 值是有效的.CFM_SIZEyHeight 成员是有效的.CFM_STRIKEOUTdwEffects 成员中的 CFE_STRIKEOUT 值是有效的.CFM_UNDERLINEdwEffects 成员中的 CFE_UNDERLINE 值是有效的.dwEffects字符效果. 可以是以下值的组合CFE_AUTOCOLOR使用系统正文颜色CFE_BOLD粗体字符CFE_ITALIC斜体字符CFE_STRIKEOUT字符带删除线CFE_UNDERLINE字符带下划线CFE_PROTECTED字符是受保护的; 企图改变字体会产生一个EN_PROTECTED 通知消息. yHeight字符高度, 单位是 twips ( 1/1440 英寸 或者 1/20 的打印机的打印点). yOffset字符偏移,单位是 twips, 从基线算起.如果该成员是正值,字符是下标,如果负值,字符是下标. crTextColor 正文颜色. 如果指定了 CFE_AUTOCOLOR 字符效果,这个值会被忽略.bCharSet字符集bPitchAndFamilyFont family and pitch. szFaceName 字体名字,是一个空字符结尾的字符数组._wPad2填充通过检查这个结构,你会发现我们可以改变正文效果(粗体,斜体,删除线,下划线),正文颜色 (crTextColor) 和字体外观/大小/字符集. 值得注意的是 CFE_RPOTECTED 标志. 具有该标志的正文会被标志上受保护标志,意味着当用户试图改变它时, EN_PROTECTED 这个通知消息会被发送到父窗口.同时你可以允许该更改是是否可以进行.CHARFORMAT2 增加更多的正文风格,象字体权值,间距,正文背景色,字距调整等等.如果你不需要这些额外的功能,只要使用 CHARFORMAT即可.要设置正文格式,你必须考虑你想要该格式应用到的正文的范围.RichEdit控件引入了字符正文范围的概念.RichEdit 分派给每个字符一个从0开始数字值:控件中的第一个字符具有数字ID值为0,第二个为1...等等。要说明一个正文范围,你必须提供给RichEdit控件两个数值:范围的第一个和最后一个字符的ID值。要跟 EM_SETCHARFORMAT 一起应用字符格式,你有最多3种选择:1.应用于控件中的所有正文。 (SCF_ALL)2.应用于当前选定的正文。 (SCF_SELECTION)3.应用于当前选定的一个单词。 (SCF_WORD 或者 SCF_SELECTION)第一、二种情况是直观的,最后一个选择需要一点解释。如果当前选择只覆盖了词中的一个或多个字符但是不是整个词,指定 SCF_WORD+SCF_SELECTION 标志将正文格式应用于整个单词。即使没有当前选定,只有插入点(光标)位于单词中,第三种情况下仍然会将正文格式应用到整个单词中。要使用 EM_SETCHARFORMAT, 你需要填充 CHARFORMAT (或者 CHARFORMAT2) 结构的结果成员。譬如,如果我们要设置正文颜色,需要如下一样填充 CHARFORMAT 结构:.data?cf CHARFORMAT .....codemov cf.cbSize,sizeof cfmov cf.dwMask,CFM_COLORmov cf.crTextColor,0FF0000hinvoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cf以上的代码片段设置RichEdit控件的正文颜色为纯蓝色。注意当 EM_SETCHARFORMAT 发生时,如果控件中没有正文,在这个消息之后键入的正文将会使用消息 EM_SETCHARFORMAT 中指定的正文格式。设置正文/保存正文对于那些经常使用Edit控件的人来说,你们肯定对使用 WM_GETTEXT/WM_SETTEXT 来设置/保存 控制中的正文的方法很熟悉。这个方法对RichEdit仍然适用,但是如果文件很大的话这个方法不再是最有效的。Edit控件限制了正文长度,可以输入最多64K的正文。但是RichEdit控件可以接受比这个限制大的多的正文数据。要分配一个很大的内存块(譬如大约 10MB ) 来接受来自来自WM_GETTEXT 消息的正文是一件很麻烦的事. Richedit 控件为此提供了一个新的方法,就是 正文流(Text Streaming)。要设置RichEdit文本,你只需给RichEdit简单的提供一个回调函数的地址,当一切准备好时,RichEdit 会调用回调函数,并将正文缓冲区的地址传递给它。回调函数会将要发送给RichEdit的数据填入缓冲区或者将缓冲区的数据读出,然后等待下一次调用自到操作完成。 范例程序是流入(设置正文)和流出(取出正文)两者的例子。你将会发现这个方法更加有效:这个缓冲区是RichEdit控件自己提供的,因此数据被分成了几个大块。这个操作包括两条消息: EM_STREAMIN 和 EM_STREAMOUT。EM_STREAMIN 和 EM_STREAMOUT 两者使用同一个语法:wParam == 格式选项.SF_RTF 数据是RTF格式。SF_TEXT 数据是简单正文格式。SFF_PLAINRTF 只有那些对所有语系都共有的关键词才流入。SFF_SELECTION 如果指定这个标志,流操作的目标就是当前选定的正文。如果你将正文流入,当前正文就会被替换,如果是流出,则只有那些当前选定的正文才流出。如果没有指定这个标志,操作就会影响到控件中的所有正文。SF_UNICODE (RichEdit 2.0 或更高版本才提供)指定的是 Unicode 正文。lParam == 指向一个 EDITSTREAM 结构,该结构定义如下:EDITSTREAM STRUCTdwCookie DWORD ?dwError DWORD ?pfnCallback DWORD ?EDITSTREAM ENDSdwCookie 应用程序定义的数值,将会被传递给由 pfnCallback 成员说明的回调函数。通常地我们传递一些重要的参数值给回调函数,譬如流入/流出 处理中使用到的文件句柄。dwError 指示流入(读)或流出(写)操作的结果。0说明没有错误。非0值可以是 EditStreamCallback 函数的返回值或者说明控件遇到了错误。pfnCallback 指向 EditStreamCallback 函数的指针,该函数是由用户定义,由RichEdit调用来传输数据的。RichEdit 将数据分开多个部分,重复地调用该函数,一次一部分地进行数据传输。EditStream 回调函数具有以下定义:EditStreamCallback proto dwCookie:DWORD,pBuffer:DWORD,NumBytes:DWORD,pBytesTransferred:DWORD你必须在程序中按照以上原型来创建回调函数。然后将函数地址通过 EDITSTREAM 结构传递给EM_STREAMIN 或者 EM_STREAMOUT 。对流入操作(设置RichEdit 控件中的正文):dwCookie: 应用程序定义的数值,通过 EDITSTREAM 结构传递给 EM_STREAMIN 。 在这里我们几乎全部都是将用其内容来设置RichEdit正文的文件的句柄传给这个参数。pBuffer: 指向RichEdit提供的缓冲区。用来接受回调函数中提供的正文数据。NumBytes: 本次调用中你可以写入缓冲区(pBuffer)的最大正文字节数。你 必须 遵守这个限制, 也就是说你发送的数据的大小可以比NumBytes指定要小,但是不能大于这个值。 你可以将这个数值当作pBuffer缓冲区的大小。 pBytesTransferred: 指向一个双字(DWORD),你必须设置这个值来指示你实际上传输了多少字节数据到缓冲区。 这个值通常跟 NumBytes中的值是相等的. 唯一的例外是当要传送的数据少于缓冲区提供的大小时, 譬如到达文件尾时就是了。对流出操作(读出RichEdit控件的正文):dwCookie: 跟流入操作相同. 我们一般传递想要将数据写入的文件的句柄给这个参数。pBuffer: 指向由RichEdit提供的缓冲区,里面的是RichEdit的正文。要得到其大小,你必须检查 NumBytes 的知.NumBytes: pBuffer 指向的缓冲区的的数据的大小。pBytesTransferred: 指向一个双字(DWORD),你必须设置这个值来指示你实际上从缓冲区里读出了多少字节数据。回调函数返回0说明操作成功,而且如果还有数据需要读/写的话,RichEdit控件会继续调用它。如果操作中发生了错误,而且你想停止操作的话,你可以返回一个非0值,这样RichEdit就会丢弃pBuffer指向的数据。错误/成功返回值会在 EDITSTREAM 的 dwError 成员中返回,你可以 在SendMessage 返回后检查流操作的错误/成功状态。.例子:下面的例子是一个简单的编辑器,你可以用来打开一个asm源文件,编辑它,然后保存。它使用了 RichEdit 控件 2.0 或者更高的版本..386.model flat,stdcalloption casemap:noneinclude \masm32\include\windows.incinclude \masm32\include\user32.incinclude \masm32\include\comdlg32.incinclude \masm32\include\gdi32.incinclude \masm32\include\kernel32.incincludelib \masm32\lib\gdi32.libincludelib \masm32\lib\comdlg32.libincludelib \masm32\lib\user32.libincludelib \masm32\lib\kernel32.libWinMain proto :DWORD,:DWORD,:DWORD,:DWORD.constIDR_MAINMENU equ 101IDM_OPEN equ 40001IDM_SAVE equ 40002IDM_CLOSE equ 40003IDM_SAVEAS equ 40004IDM_EXIT equ 40005IDM_COPY equ 40006IDM_CUT equ 40007IDM_PASTE equ 40008IDM_DELETE equ 40009IDM_SELECTALL equ 40010IDM_OPTION equ 40011IDM_UNDO equ 40012IDM_REDO equ 40013IDD_OPTIONDLG equ 101IDC_BACKCOLORBOX equ 1000IDC_TEXTCOLORBOX equ 1001RichEditID equ 300.dataClassName db "IczEditClass",0AppName db "IczEdit version 1.0",0RichEditDLL db "riched20.dll",0RichEditClass db "RichEdit20A",0NoRichEdit db "Cannot find riched20.dll",0ASMFilterString db "ASM Source code (*.asm)",0,"*.asm",0 db "All Files (*.*)",0,"*.*",0,0OpenFileFail db "Cannot open the file",0WannaSave db "The data in the control is modified. Want to save it?",0FileOpened dd FALSEBackgroundColor dd 0FFFFFFh ; 缺省为白色TextColor dd 0 ; 缺省为黑色.data?hInstance dd ?hRichEdit dd ?hwndRichEdit dd ?FileName db 256 dup(?)AlternateFileName db 256 dup(?)CustomColors dd 16 dup(?).codestart: invoke GetModuleHandle, NULL mov hInstance,eax invoke LoadLibrary,addr RichEditDLL .if eax!=0 mov hRichEdit,eax invoke WinMain, hInstance,0,0, SW_SHOWDEFAULT invoke FreeLibrary,hRichEdit .else invoke MessageBox,0,addr NoRichEdit,addr AppName,MB_OK or MB_ICONERROR .endif invoke ExitProcess,eaxWinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:DWORD mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,IDR_MAINMENU mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd .while TRUE invoke GetMessage, ADDR msg,0,0,0 .break .if (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .endw mov eax,msg.wParam retWinMain endpStreamInProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesRead:DWORD invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0 xor eax,1 retStreamInProc endpStreamOutProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesWritten:DWORD invoke WriteFile,hFile,pBuffer,NumBytes,pBytesWritten,0 xor eax,1 retStreamOutProc endpCheckModifyState 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 retCheckModifyState endpSetColor 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 retSetColor endpOptionProc 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 ;================================================================================== ;保存RichEdit控件的修改状态,因为更改正文颜色时改变了RichEdit控件的修改状态。 ; ;================================================================================== invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0 push eax invoke SetColor pop eax invoke SendMessage,hwndRichEdit,EM_SETMODIFY,eax,0 invoke EndDialog,hWnd,0 .endif .endif .elseif uMsg==WM_CTLCOLORSTATIC invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX .if eax==lParam invoke CreateSolidBrush,BackgroundColor ret .else invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX .if eax==lParam invoke CreateSolidBrush,TextColor ret .endif .endif mov eax,FALSE ret .elseif uMsg==WM_CLOSE invoke EndDialog,hWnd,0 .else mov eax,FALSE ret .endif mov eax,TRUE retOptionProc endpWndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL chrg:CHARRANGE LOCAL ofn:OPENFILENAME LOCAL buffer[256]:BYTE LOCAL editstream:EDITSTREAM LOCAL hFile:DWORD .if uMsg==WM_CREATE invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr RichEditClass,0,WS_CHILD or\ WS_VISIBLE or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_NOHIDESEL,\ CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,RichEditID,hInstance,0 mov hwndRichEdit,eax ;============================================================= ; 设置正文长度限制,缺省是 64K ;============================================================= invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0 ;============================================================= ; Set the default text/background color ;============================================================= invoke SetColor invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0 invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0 .elseif uMsg==WM_INITMENUPOPUP mov eax,lParam;==================================================== .if ax==0 ; file menu .if FileOpened==TRUE ; a file is already opened invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED .else invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED .endif .elseif ax==1 ; edit 菜单 ;============================================================================= ; 检查剪贴板里是否有正文,如果有的话我们就使能 Paste 菜单项。 ;============================================================================= invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0 .if eax==0 ; no text in the clipboard invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED .else invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED .endif ;========================================================== ; 检查Undo队列时是否为空 ;========================================================== invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0 .if eax==0 invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED .else invoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED .endif ;========================================================= ; 检查Redo队列时是否为空 ;========================================================= invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0 .if eax==0 invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED .else invoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED .endif ;========================================================= ; 检查RichEdit控件中是否有当前选定正文,有的话就使能 ; cut/copy/delete 菜单项。 ;========================================================= invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg mov eax,chrg.cpMin .if eax==chrg.cpMax ; 没有当前选定 invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYED invoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYED invoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED .else invoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLED invoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLED invoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED .endif .endif;======================================================= .elseif uMsg==WM_COMMAND .if lParam==0 ; 命令菜单 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 ;================================================================ ; 将正文流入RichEdit控件。 ;================================================================ mov editstream.dwCookie,eax mov editstream.pfnCallback,offset StreamInProc invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream ;========================================================== ; 初始化修改状态为FALSE ;========================================================== 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 ;================================================================ ; 将RichEdit中的正文流出到文件中。 ;================================================================ mov editstream.dwCookie,eax mov editstream.pfnCallback,offset StreamOutProc invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT,addr editstream ;========================================================== ; 初始化修改状态为FALSE ;========================================================== invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0 invoke CloseHandle,hFile .else invoke MessageBox,hWnd,addr OpenFileFail,addr AppName,MB_OK or MB_ICONERROR .endif .elseif ax==IDM_COPY invoke SendMessage,hwndRichEdit,WM_COPY,0,0 .elseif ax==IDM_CUT invoke SendMessage,hwndRichEdit,WM_CUT,0,0 .elseif ax==IDM_PASTE invoke SendMessage,hwndRichEdit,WM_PASTE,0,0 .elseif ax==IDM_DELETE invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0 .elseif ax==IDM_SELECTALL mov chrg.cpMin,0 mov chrg.cpMax,-1 invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg .elseif ax==IDM_UNDO invoke SendMessage,hwndRichEdit,EM_UNDO,0,0 .elseif ax==IDM_REDO invoke SendMessage,hwndRichEdit,EM_REDO,0,0 .elseif ax==IDM_OPTION invoke DialogBoxParam,hInstance,IDD_OPTIONDLG,hWnd,addr OptionProc,0 .elseif ax==IDM_SAVEAS invoke RtlZeroMemory,addr ofn,sizeof ofn mov ofn.lStructSize,sizeof ofn push hWnd pop ofn.hwndOwner push hInstance pop ofn.hInstance mov ofn.lpstrFilter,offset ASMFilterString mov ofn.lpstrFile,offset AlternateFileName mov byte ptr [AlternateFileName],0 mov ofn.nMaxFile,sizeof AlternateFileName mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST invoke GetSaveFileName,addr ofn .if eax!=0 invoke CreateFile,addr AlternateFileName,GENERIC_WRITE,\ FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0 .if eax!=INVALID_HANDLE_VALUE jmp @B .endif .endif .elseif ax==IDM_EXIT invoke SendMessage,hWnd,WM_CLOSE,0,0 .endif .endif .elseif uMsg==WM_CLOSE invoke CheckModifyState,hWnd .if eax==TRUE invoke DestroyWindow,hWnd .endif .elseif uMsg==WM_SIZE mov eax,lParam mov edx,eax and eax,0FFFFh shr edx,16 invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE .elseif uMsg==WM_DESTROY invoke PostQuitMessage,NULL .else invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .endif xor eax,eax retWndProc endpend start;===================================================================; 资源文件;===================================================================#include "resource.h"#define IDR_MAINMENU 101#define IDD_OPTIONDLG 101#define IDC_BACKCOLORBOX 1000#define IDC_TEXTCOLORBOX 1001#define IDM_OPEN 40001#define IDM_SAVE 40002#define IDM_CLOSE 40003#define IDM_SAVEAS 40004#define IDM_EXIT 40005#define IDM_COPY 40006#define IDM_CUT 40007#define IDM_PASTE 40008#define IDM_DELETE 40009#define IDM_SELECTALL 40010#define IDM_OPTION 40011#define IDM_UNDO 40012#define IDM_REDO 40013IDR_MAINMENU MENU DISCARDABLEBEGIN POPUP "&File" BEGIN MENUITEM "&Open", IDM_OPEN MENUITEM "&Close", IDM_CLOSE MENUITEM "&Save", IDM_SAVE MENUITEM "Save &As", IDM_SAVEAS MENUITEM SEPARATOR MENUITEM "E&xit", IDM_EXIT END POPUP "&Edit" BEGIN MENUITEM "&Undo", IDM_UNDO MENUITEM "&Redo", IDM_REDO MENUITEM "&Copy", IDM_COPY MENUITEM "C&ut", IDM_CUT MENUITEM "&Paste", IDM_PASTE MENUITEM SEPARATOR MENUITEM "&Delete", IDM_DELETE MENUITEM SEPARATOR MENUITEM "Select &All", IDM_SELECTALL END MENUITEM "Options", IDM_OPTIONENDIDD_OPTIONDLG DIALOG DISCARDABLE 0, 0, 183, 54STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_CENTERCAPTION "Options"FONT 8, "MS Sans Serif"BEGIN DEFPUSHBUTTON "OK",IDOK,137,7,39,14 PUSHBUTTON "Cancel",IDCANCEL,137,25,39,14 GROUPBOX "",IDC_STATIC,5,0,124,49 LTEXT "Background Color:",IDC_STATIC,20,14,60,8 LTEXT "",IDC_BACKCOLORBOX,85,11,28,14,SS_NOTIFY | WS_BORDER LTEXT "Text Color:",IDC_STATIC,20,33,35,8 LTEXT "",IDC_TEXTCOLORBOX,85,29,28,14,SS_NOTIFY | WS_BORDEREND--------------------------------------------------------------------------------分析:例子程序首先载入RichEdit DLL, 在这里是 riched20.dll. 如果DLL载入失败,就返回 Windows.invoke LoadLibrary,addr RichEditDLL.if eax!=0mov hRichEdit,eaxinvoke WinMain,hInstance,0,0, SW_SHOWDEFAULTinvoke FreeLibrary,hRichEdit.elseinvoke MessageBox,0,addr NoRichEdit,addr AppName,MB_OK or MB_ICONERROR.endifinvoke ExitProcess,eax成功载入DLL后,我们继续创建一个常规窗口,作为RichEdit的父窗口。在 WM_CREATE 处理函数里,我们创建一个RichEdit控件:invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr RichEditClass,0,WS_CHILD or WS_VISIBLE or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_NOHIDESEL,\CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,RichEditID,hInstance,0mov hwndRichEdit,eax注意在这里我们指定了 ES_MULTILINE 风格,否则创建的会是一个单行的控件。invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0创建了RichEdit控件之后,我们必须设置新的正文大小。缺省时,RichEdit控件具有64K的正文大小限制,跟简单的多行Edit控件相同。我们需要扩展这个限制,允许用来操作更大的文件。在上一个代码行里,我们指定了大小为 -1, 大小总计为 0FFFFFFFFh 字节, 是一个很大的数值了。 invoke SetColor下一步,我们设置正文/背景色 。 因为这个操作可以在程序中的其他部分执行,我把这些代码放到一个叫SetColor的函数里。SetColor procLOCAL cfm:CHARFORMATinvoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor设置RichEdit控件的背景色是一个很简单的操作:只需发送 EM_SETBKGNDCOLOR 信息给RIchEdit控件就行了。(如果你使用多行Edit控件,你必须处理 WM_CTLCOLOREDIT 消息)。缺省的背景色是白色的。invoke RtlZeroMemory,addr cfm,sizeof cfmmov cfm.cbSize,sizeof cfmmov cfm.dwMask,CFM_COLORpush TextColorpop cfm.crTextColor设置好背景色之后,我们填充 CHARFORMAT 的成员,以便用来设置设置正文颜色。应该注意的是我们使用该结构的大小来填充cbSize 成员,这样RichEdit 控件就知道我们发送的是CHARFORMAT, 而不是 CHARFORMAT2。 dwMask 只使用了一个 CFM_COLOR 标志, 因为我们只想设置正文颜色,同时往 crTextColor 里填入我们想要的正文颜色值.invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cfmretSetColor endp设置好颜色后,你必须要清空 Undo 缓冲区,因为更改正文/背景颜色的操作是可撤消的(Undo-able),更改颜色时在缓冲区里留下了Undo信息。我们可以发送 EM_EMPTYUNDOBUFFER 消息来实现这个操作。invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0填充好 CHARFORMAT 结构, 我们发送 EM_SETCHARFORMAT 消息给RichEdit控件,在wParam中指定 SCF_ALL 标志,说明我们想把正文格式应用于控件中的所有正文。注意在我们第一次创建RichEdit控件时,我们没有指定它的大小/位置。这是因为我们想它覆盖父窗口的全部客户区。当父窗口大小改变时,我们就跟着改变RichEdit控件的大小。.elseif uMsg==WM_SIZEmov eax,lParammov edx,eaxand eax,0FFFFhshr edx,16invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE在上面的程序片段, 我们使用了在 lParam中的客户区的新尺寸,通过 MoveWindow来改变RichEdit的大小。当用户点击文件 File/Edit 菜单条时,我们处理 WM_INITPOPUPMENU 消息,因此我们可以在显示子菜单给用户之前准备好各个子菜单项的状态。譬如,如果已经有一个文件在RichEdit控件中打开了,我们想禁止Open菜单项同时使能其他的菜单项。对于这种情况下的File菜单条, 我们使用变量 FileOpened 来作为标志表示是否有一个文件已经打开了。如果这个变量是TRUE值,我们知道已经有一个文件被打开了。.elseif uMsg==WM_INITMENUPOPUPmov eax,lParam.if ax==0 ; file 菜单.if FileOpened==TRUE ; 已经打开了一个文件invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYEDinvoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLEDinvoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLEDinvoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED.elseinvoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLEDinvoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYEDinvoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYEDinvoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED.endif正如你所见的,如果有一个文件已经打开了,我们将Open菜单项变灰禁止并将其他菜单项都使能。跟TRUE值相反的是 FileOpened 值为FALSE.在这种情况下的EDIT菜单条我们需要先检查RichEdit控件/剪贴板的状态。invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0.if eax==0 ; 剪贴板里没有正文invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED.elseinvoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED.endif我们首先发送 EM_CANPASTE 消息,来检查剪贴板里是否存在可用的正文。如果有的话,SendMessage 返回 TRUE ,我们就将 Paste 菜单项使能。如果没有的话,我们将该菜单项变灰禁止。invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0.if eax==0invoke EnableMenuItem,wParam,IDM_UNDO,MF_GRAYED.elseinvoke EnableMenuItem,wParam,IDM_UNDO,MF_ENABLED.endif跟着,我们通过发送 EM_CANUNDO 消息来检查Undo 缓冲区是否为空,如果不空,SendMessage 返回 TRUE ,我们就使能 Undo 菜单项。invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0.if eax==0invoke EnableMenuItem,wParam,IDM_REDO,MF_GRAYED.elseinvoke EnableMenuItem,wParam,IDM_REDO,MF_ENABLED.endif我们通过发送 EM_CANREDO 消息给 RichEdit 控件来检查 Redo 缓冲区。如果不空的话,SendMessage 返回 TRUE,我们就使能 Redo 菜单项。invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrgmov eax,chrg.cpMin.if eax==chrg.cpMax ; 没有当前选定正文invoke EnableMenuItem,wParam,IDM_COPY,MF_GRAYEDinvoke EnableMenuItem,wParam,IDM_CUT,MF_GRAYEDinvoke EnableMenuItem,wParam,IDM_DELETE,MF_GRAYED.elseinvoke EnableMenuItem,wParam,IDM_COPY,MF_ENABLEDinvoke EnableMenuItem,wParam,IDM_CUT,MF_ENABLEDinvoke EnableMenuItem,wParam,IDM_DELETE,MF_ENABLED.endif最后,我们通过发送 EM_EXGETSEL 消息来检查是否存在当前选定的正文,该消息使用一个 CHARRANGE 结构,定义如下:CHARRANGE STRUCT cpMin DWORD ? cpMax DWORD ?CHARRANGE ENDScpMin 包含紧接在范围中的第一个字符之前的字符的位置索引。cpMax 包含紧跟在范围中的最后一个字符之后的的字符的位置索引。EM_EXGETSEL 返回后,CHARRANGE 结构就会被用选择范围的开始和结束位置索引所填充。如果没有当前选定,cpMin 和 cpMax 就会是同样的数值,我们就将 Cut/Copy/Delete 菜单项变灰禁止。当用户点击 Open 菜单项,我们就显示一个打开文件的对话框,如果用户选择了一个文件,我们就打开该文件并将其内容流入RichEdit 控件中。invoke CreateFile,addr FileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0.if eax!=INVALID_HANDLE_VALUEmov hFile,eaxmov editstream.dwCookie,eaxmov editstream.pfnCallback,offset StreamInProcinvoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream使用 CreateFile成功打开文件后,我们填充EDITSTREAM 结构,以便准备发送 EM_STREAMIN 消息我们选择通过 dwCookie 成员发送打开文件的句柄,并在 pfnCallback 成员中传递流回调函数的地址。流回调函数本身是简单基本的。StreamInProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesRead:DWORDinvoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0xor eax,1retStreamInProc endp你可以看到流回调函数的所有参数跟 ReadFile 函数 完美匹配。而且ReadFile 的返回值是是跟 1 经过 XOR异或运算的,因此如果它返回 1 (成功),在EAX中实际返回的值的是0,反之亦然。invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0invoke CloseHandle,hFilemov FileOpened,TRUE消息EM_STREAMIN 返回后, 意味着流操作已经完成。实际上,我们必需检查 EDITSTREAM 结构的 dwError 成员。RichEdit (和 Edit) 控件支持一个标志,用来指示其内容时候被改变了。我们可以通过发送 EM_GETMODIFY 消息给控件来得到这个标志的值。如果控件内容被改变了的话,SendMessage 返回 TRUE。因为我们将正文流入控件,也是一种改变。所以我们必需设置修改标志为FALSE,可以通过给控件发送EM_SETMODIFY 消息,令其 wParam==FALSE 使控件的修改标志在流入操作完成后重新开始。然后我们马上就关闭文件并设置变量 FileOpened 为 TRUE 来说明已经打开了一个文件。当用户点击 Save/SaveAs 菜单项,我们使用 EM_STREAMOUT 消息来将RichEdit控件的内容输出到一个文件中。跟流入回调函数一样,流出回调函数本身也是很简单的。它也是跟 WriteFile完美匹配.正文操作象Cut/Copy/Paste/Redo/Undo 等通过发送单个消息给控件是很容易实现的,这些消息分别是 WM_CUT/WM_COPY/WM_PASTE/WM_REDO/WM_UNDO 。删除/选择全部正文的操作如下:.elseif ax==IDM_DELETEinvoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0.elseif ax==IDM_SELECTALLmov chrg.cpMin,0mov chrg.cpMax,-1invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg删除操作影响到当前的选定。我发送 EM_REPLACESEL 消息,传递一个 NULL 字符串,RichEdit 控件将会用空串来替代当前选定的正文串。选择全部正文的操作通过发送 EM_EXSETSEL 消息,指定 cpMin==0 和 cpMax==-1 的数值来选择所有的正文。当用户选择 Option 菜单条,我们显示一个对话框,显示当前的背景/正文颜色。当用户点击任意一个颜色框,它会显示颜色选择对话框。"颜色框" 实际上是一个具有 SS_NOTIFY 和 WS_BORDER 风格标志的静态控件。具有 SS_NOTIFY 标志的静态控件会将在上面的鼠标动作通知给父窗口,譬如BN_CLICKED (STN_CLICKED). 这个就是窍门了!.elseif ax==IDC_BACKCOLORBOXinvoke RtlZeroMemory,addr clr,sizeof clrmov clr.lStructSize,sizeof clrpush hWndpop clr.hwndOwnerpush hInstancepop clr.hInstancepush BackgroundColorpop clr.rgbResultmov clr.lpCustColors,offset CustomColorsmov clr.Flags,CC_ANYCOLOR or CC_RGBINITinvoke ChooseColor,addr clr.if eax!=0push clr.rgbResultpop BackgroundColorinvoke GetDlgItem,hWnd,IDC_BACKCOLORBOXinvoke InvalidateRect,eax,0,TRUE.endif当用户点击任意一个颜色框时,我们会填充 CHOOSECOLOR 结构的成员并调用ChooseColor 来显示颜色选择对话框。如果用户选择了一种颜色,颜色值会在 rgbResult 成员中返回,我们把改值保存在变量 BackgroundColor 中。之后,我们通过用颜色框句柄调用InvalidateRect 函数来强迫颜色框进行重画。 颜色框会发送 WM_CTLCOLORSTATIC 消息给父窗口.invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX.if eax==lParaminvoke CreateSolidBrush,BackgroundColorret在 WM_CTLCOLORSTATIC 消息处理中,我们把在 lParam 传递进来的静态控件的句柄跟那两个颜色框的句柄作比较。如果值匹配的话,我们就使用变量中颜色值来创建一个新画刷并立刻返回。静态控件将会使用新创建的画刷来重画其背景。 |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|