Iczelion - 34 - RichEdit Steuerelement: Mehr Text Operationen

Tutorial 34: RichEdit Steuerelement: Mehr Text Operationen



Sie werden mehr über Text Operationen mit dem RichEdit Steuerelement lernen. Speziell werden Sie lernen wie man nach Text sucht und ersetzt und zu einer bestimmten Zeilennummer springt.

Laden Sie das Beispiel herunter.

Theorie

Nach Text suchen

Es gibt verschiedene Text Operationen unter dem RichEdit Steuerelement. Nach Text suchen ist eine dieser Operationen. Nach Text suchen wird durch das Senden der Nachrichten EM_FINDTEXT oder EM_FINDTEXTEX erreicht. Diese Nachrichte haben einen kleinen Unterschied.

EM_FINDTEXT
wParam
== Such Optionen. Kann irgend eine Kombination aus den Werten aus der folgenden Tabelle sein. Diese Optionen sind für beide EM_FINDTEXT und EM_FINDTEXTEX gleich.

FR_DOWN Wenn dieses Flag angegeben ist, startet die Suche vom end der aktuellen Markierung bis zum end des Textes im Steuerelement (abwärts). Dieses Flag ist nur für RichEdit 2.0 oder höher von Bedeutung: Dieses Verhalten ist Standard für RichEdit 1.0. Das Standard-Verhalten von RichEdit 2.0 oder höher ist, vom Ende der aktuellen Markierung bis zum Anfang des Textes zu suchen (aufwärts).
Kurz gesagt, wenn Sie RichEdit 1.0 benutzen, können Sie die Suchrichtung nicht beeinflussen: es wird immer abwärts gesucht. Wenn Sie RichEdit 2.0 benutzen und Sie möchten abwärts suchen, müssen Sie diese Flag angeben, ansonsten ist die Suche aufwärts.
FR_MATCHCASE Wenn dieses Flag angegeben ist, ist die Suche Case-Sensitive. (Groß-/Kleinschreibung wird beachtet).
FR_WHOLEWORD Wenn dieses Flag gesetzt ist, findet die Suche nur ganze Wörter, die mit dem Suchstring übereinstimmen.


In Wirklichkeit gibt es ein paar mehr Flags, die aber nur zur nicht Englischen Sprachen relevant sind. (Anm. d. Übersetzers: nicht europäisch wäre wohl passender)
lParam == Zeiger auf die FINDTEXT Struktur.

            FINDTEXT STRUCT
              chrg          CHARRANGE  
              lpstrText     DWORD      ?
            FINDTEXT ENDS

chrg ist eine CHARRANGE Struktur die wie folgt definiert ist:

            CHARRANGE STRUCT
              cpMin  DWORD      ?
              cpMax  DWORD      ?
            CHARRANGE ENDS

cpMin enthält den Zeichen-Index des ersten Zeichens im Zeichen-Array (Range (=Reichweite)).
cpMax enthält den Zeichen-Index des ersten Zeichens, das unmittelbar nach dem letzten Zeichen im Zeichen-Array steht.

Um nach einen Text-String zu suchen, müssen Sie im wesentlichen das Zeichen-Range spezifizieren, indem gesucht werden soll. Die Bedeutung von cpMin
und cpMax unterscheiden sich demnach, ob Sie aufwärts oder abwärts suchen. Wenn die Suche abwärts gerichtet ist, spezifiziert cpMin den Anfangs-Zeichen-Index um zu suchen und cpMax das End-Zeichen-Index. Wenn die Suche aufwärts gerichtet ist, gilt das Gegenteil, z.B. cpMin enthält das End-Zeichen-Index, während cpMax den Start-Zeichen-Index enthält.

lpstrText ist der Zeiger auf den Text-String, nach dem gesucht werden soll.

EM_FINDTEXT liefert den Zeichen-Index des ersten Zeichens des übereinstimmenden Text-String im RichEdit Steuerelement. Es wird -1 zurückgeliefert, wenn nichts passendes gefunden wird.

EM_FINDTEXTEX
wParam == die Such-Optionen. Die selben wie bei EM_FINDTEXT.
lParam == Zeiger auf die FINDTEXTEX Struktur.

            FINDTEXTEX STRUCT
              chrg          CHARRANGE  
              lpstrText     DWORD      ?
              chrgText    CHARRANGE 
            FINDTEXTEX ENDS

Die ersten beiden Elemente von FINDTEXTEX sind identisch mit denen der FINDTEXT Struktur. chrgText ist eine CHARRANGE Struktur, die mit den Start-/End-Indizes des Zeichens gefüllt wird, wenn keine Übereinstimmung gefunden wird.
Der Rückgabewert von EM_FINDTEXTEX ist der selbe wie von EM_FINDTEXT.

Der Unterschied zwischen EM_FINDTEXT und EM_FINDTEXTEX ist, dass die FINDTEXTEX Struktur zusätzliche Elemente hat,
chrgText, welche mit dem Start-/End-Zeichen Indizes gefüllt wird, wenn eine Übereinstimmung gefunden wurde. Das ist recht bequem, wenn wir mehrere Text Operationen auf den String anwenden wollen.

Text ersetzen/einfügen

Das RichEdit Steuerelement bietet EM_SETTEXTEX an, um Text zu ersetzen/einzufügen. Diese Nachricht kombiniert die Funktionalität von WM_SETTEXT und EM_REPLACESEL. Sie hat folgende Syntax:

    EM_SETTEXTEX
    wParam == Zeiger auf die SETTEXTEX Struktur.

            SETTEXTEX STRUCT
              flags          DWORD      ?
              codepage       DWORD      ?
            SETTEXTEX ENDS

    flags kann die Kombination folgender Werte sein:

ST_DEFAULT Löscht den Undo- (=Einfügen-) Stack, verwirft Richt-Text-Formatierungen, ersetzt den ganzen Text.
ST_KEEPUNDO Behält den Undo Stack
ST_SELECTION Ersetzt die Markierung und behält Rich-Rext Formatierungen bei


    codepage ist die Konstante, die die Codepage spezifiziert, auf der der Text sein soll. Normalerweise benutzen wir einfach CP_ACP.

Text Markierung

Wir können den Text automatisch mitEM_SETSEL oder EM_EXSETSEL selektieren. Beide funktionieren wunderbar. Welche Nachricht verwendet wird, hängt vom verfügbaren Format der Zeichen-Indize ab. Wenn sie schon in einer CHARRANGE Struktur bespeichert sind, ist es einfacher, EM_EXSETSEL zu benutzen.

    EM_EXSETSEL
    wParam == nicht benutzt. Muss 0 sein
    lParam == Zeiger auf eine CHARRANGE Struktur die den zu selektierenden Zeichen-Range enthält.

Ereigniss Benachrichtigung

Für den Fall eines mehrzeiligen Edit-Steuerelementes, müssen Sie es ableiten (subclassing), um die Eingabe-Nachrichten wie Maus/Tastatur-Ereignisse zu erhalten. Das RichEdit Steuerelement unterstützt ein besseres Schema, dass das Parent-Fenster bei einem solchen Ereigniss benachrichtigt. Um die Benachrichtigung zu registrieren, sendet das Parent-Fenster eine EM_SETEVENTMASK Nachricht an das RichEdit Steuerelement und spezifizert darin, in welchen Ereignissen es interessiert ist.EM_SETEVENTMASK hat folgende Syntax:

    EM_SETEVENTMASK
    wParam == nicht benutzt. Muss 0 sein
    lParam == Ereigniss-Masken-Wert. Kann eine Kombination aus folgenden Werten sein:

ENM_CHANGE Sendet EN_CHANGE Benachrichtigungen
ENM_CORRECTTEXT Sendet EN_CORRECTTEXT Benachrichtigungen
ENM_DRAGDROPDONE Sendet EN_DRAGDROPDONE Benachrichtigungen
ENM_DROPFILES Sendet EN_DROPFILES Benachrichtigungen.
ENM_KEYEVENTS Sendet EN_MSGFILTER Benachrichtigungen für Tastatur-Ereignisse
ENM_LINK Rich Edit 2.0 und höher: Sendet EN_LINK Benachrichtigungen, wenn der Mauszeiger über dem Text ist, welcher den CFE_LINK hat und eine von verschiedenen Maus-Aktionen ausgeführt wird.
ENM_MOUSEEVENTS Sendet EN_MSGFILTER Benachrichtigungen für Maus-Ereignisse
ENM_OBJECTPOSITIONS Sendet EN_OBJECTPOSITIONS Benachrichtigungen
ENM_PROTECTED Sendet EN_PROTECTED Benachrichtigungen
ENM_REQUESTRESIZE Sendet EN_REQUESTRESIZE Benachrichtigungen
ENM_SCROLL Sendet EN_HSCROLL und EN_VSCROLL Benachrichtigungen
ENM_SCROLLEVENTS Sendet EN_MSGFILTER Benachrichtigungen für Maus-Rad-Ereignisse
ENM_SELCHANGE Sendet EN_SELCHANGE Benachrichtigungen
ENM_UPDATE Sendet EN_UPDATE Benachrichtigungen.
Rich Edit 2.0 und häher: dieses Flag wird ignoriert und EN_UPDATE Benachrichtigungen werden immer gesendet. Wenn Rich Edit 3.0 Rich Edit 1.0 emuliert, müssen Sie dieses Flag benutzen um EN_UPDATE Benachrichtigungen zu senden


Alle oben genannten Benachrichtigungen werden als WM_NOTIFY Nachricht gesendet: Sie müssen das Code Element der NMHDR Struktur für die Benachrichtigungs-Nachricht überprüfen. Wenn Sie zum Beispiel ein Maus-Ereigniss registrieren wollen (wenn Sie z.B. ein Kontext-Sensitives Popup-Menü haben wollen), müssen Sie etwa folgendes machen:

    invoke SendMessage,hwndRichEdit,EM_SETEVENTMASK,0,ENM_MOUSEEVENTS
    .....
    .....
    WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
    .....
    ....
        .elseif uMsg==WM_NOTIFY
            push esi
            mov esi,lParam
            assume esi:ptr NMHDR
            .if [esi].code==EN_MSGFILTER
                ....
                [ mach hier etwas ]
                ....
            .endif
            pop esi

Beispiel:

Das folgende Beispiel ist ein Update von IczEdit aus Tutorial Nr. 33. Es sind die Funktionalitäten Suchen/Ersetzen und Shortcuts hinzugefügt worden. Auch werden jetzt die Mausnachrichten bearbeitet und Popup-Menüs, auf rechten Mausklick hin, unterstützt.

.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

.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 2.0",0
RichEditDLL db "riched20.dll",0
RichEditClass db "RichEdit20A",0
NoRichEdit db "Konnte riched20.dll nicht finden",0
ASMFilterString         db "ASM Source code (*.asm)",0,"*.asm",0
                db "Alle Dateien (*.*)",0,"*.*",0,0
OpenFileFail db "Konnte Datei nicht öffnen",0
WannaSave db "Die Daten im Steuerelement wurden verändert. Wollen Sie speichern?",0
FileOpened dd FALSE
BackgroundColor dd 0FFFFFFh        ; Standard auf weiß
TextColor dd 0        ; Standard auf Schwarz
hSearch dd ?        ; Handle des Suchen/Ersetzten Dialoges
hAccel dd ?

.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 

.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 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 eaxLineNo
                    invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0
                    mov chrg.cpMin,eax
                    mov chrg.cpMax,eax
                    invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg
                    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        ; kein Text in der Zwischenablage
        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        ; zur Zeit keine Markierung
        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

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_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        ; Datei Menü            
            .if FileOpened==TRUE    ; es ist schon eine Datei geöffnet
                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    ; Bearbeiten Menü
            invoke PrepareEditMenu,wParam
        .elseif ax==2        ; Suchen- Menü
            .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        ; Menü Kommandos
            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
                        ;================================================================
                        ; stream den Text in das Richedit Steuerelement
                        ;================================================================                        
                        mov editstream.dwCookie,eax
                        mov editstream.pfnCallback,offset StreamInProc
                        invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream
                        ;==========================================================
                        ; initialisiere den veränderungs-Status auf 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
                    ;================================================================
                    ; stream den Text in eine Datei
                    ;================================================================                        
                    mov editstream.dwCookie,eax
                    mov editstream.pfnCallback,offset StreamOutProc
                    invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT,addr editstream
                    ;==========================================================
                    ; initialisiere den veränderungs-Status auf 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_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

Analyse

Die Suche-nach-Text Fähigkeit ist mit EM_FINDTEXTEX implementiert. Wenn der Benutzer auf das Menü-Element Find (=Suchen) klickt, wird eine IDM_FIND Nachricht gesendet und der Suchen-Dialog wird angezeigt



    invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer
    .if eax!=0
Wenn der Benutzer den Text nachdem gesucht werden soll eingibt und auf den OK Button klickt, erhalten wir den zu suchenden Text in FindBuffer.

        mov uFlags,0
        invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr findtext.chrg
Wenn der Text String nicht null ist, fahren wir fort und initialisieren die uFlags Variable mit 0. Diese Variable wird benutzt, um die Such-Flags die mit EM_FINDTEXTEX benutzt werden, zu speichern. Danach erhalten wir die aktuelle Markierung mit EM_EXGETSEL, da wir den Anfangspunkt für unsere Suche wissen müssen.

    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
Der nächste Teil ist ein wenig trickreich. Wir überprüfen den Richtungs-Radio-Button um zu ermitteln in welche Richtung wir suchen sollen. Wenn die abwärtige Suche indiziert wird, setze wir das FR_DOWN Flag auf uFlags. Danach überprüfen wir ob gerade was markiert ist, indem wir die Werte cpMin und cpMax vergleichen. Wenn beide Werte nicht gleich sind, bedeutet das, dass es zur Zeit eine Markierung gibt und wir die Suche am Ende der Markierung bis zum Ende des Textes fortsetzen müssen. Deshalb müssen wir den Wert cpMax mit dem von cpMin ersetzen und den Wert cpMax ändern wir auf -1 (0FFFFFFFFh). Wenn es zur Zeit keine Markierung gibt, ist die Range der Suche von der aktuellen Cursor-Position bis zum Ende des Textes.

Wenn der Benutzer auswählt aufwärts zu suchen, benutzen wir die Range vom Anfang der Selektierung bis zum Anfang des Textes. Das ist der Grund warum wir nur den Wert von cpMax auf 0 setzen. In diesem Fall der aufwärts Suche enthält cpMin den Zeichen-Index des letzten Zeichens in der Such Range und cpMax den Zeichen-Index des ersten Zeichens in der Such Range. Es ist das Inverse der abwärts Suche.

        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
Wir fahren mit der Überprüfung der Checkboxen in Hinsicht auf die Such-Flags fort, z.B. FR_MATCHCASE und FR_WHOLEWORD. Als letztes speichern wir den Offset des Suchtextes im lpstrText Element.

        invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,uFlags,addr findtext
        .if eax!=-1
            invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText
        .endif
    .endif
Nun sind wir bereit, um EM_FINDTEXTEX zu behandeln. Danach untersuchen wir die Such-Ergebnisse, die von SendMessage zurückgegeben wurden. Wenn der Rückgabewert -1 ist, wurde keine Übereinstimmung gefunden. Ansonsten, ist das chrgText Element der FINDTEXTEX Struktur mit den Zeichen-Indize des übereinstimmenden Textes gefüllt. Deshalb markieren wir diesen mit EM_EXSETSEL.

Die Ersetzungs-Operation wird in der selbe Weise ausgeführt.

    invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer
    invoke GetDlgItemText,hWnd,IDC_REPLACEEDIT,addr ReplaceBuffer,sizeof ReplaceBuffer
Wir erhalten den Suchtext und den Text der ersetzt werden soll.

    mov findtext.chrg.cpMin,0
    mov findtext.chrg.cpMax,-1
    mov findtext.lpstrText,offset FindBuffer
Um es einfach zu machen, wird der gesamte Text bearbeitet. Weshalb der Stard-Index 0 und der Ende-Index -1 ist.

    mov settext.flags,ST_SELECTION
    mov settext.codepage,CP_ACP
Wir initialisieren die SETTEXTEX Struktur um anzuzeigen, dass wir die aktuelle Markierung ersetzen wollen und die Standard-System-Codepage dafür benutzen wollen.

    .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
Wir betreten eine Endlos-Schleife, um nach übereinstimmenden Text zu suchen. Wenn einer gefunden wurde, selektieren wir ihn mit EM_EXSETSEL und ersetzen ihn mit EM_SETTEXTEX. Wenn keine weiteren Übereinstimmungen gefunden wurden, beenden wir die Schleife.

Die Find Next und Find Prev. Features benutzen die EM_FINDTEXTEX Nachricht in ähnlicher Weise wie die Suchen-Operation.

Wir untersuchen als nächstes das Go to Line (=springe zu Zeile) Feature. Wenn der Benutzer auf das Menüelement Go To Line klickt, zeigen wir folgenden Dialog an:



Wenn der Benutzer eine Zeilennummer eingibt und Ok drückt, beginnen wir mit der Operation

    invoke GetDlgItemInt,hWnd,IDC_LINENO,NULL,FALSE
    mov LineNo,eax
Ermittle die Zeilen-Nummer des Edit-Steuerelementes

    invoke SendMessage,hwndRichEdit,EM_GETLINECOUNT,0,0
    .if eaxLineNo
Ermittle die Anzahl der Zeilen im Steuerelement. Überprüfe, ob der Benutzer eine Zeilen-Nummer außerhalb des Range angegeben hat.

        invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0
Wenn die Zeilennummer gültig ist, wollen wir den Cursor zum ersten Zeichen der Zeile bewegen. Deshalb senden wir eine EM_LINEINDEX Nachricht an das RichEdit Steuerelement. Diese Nachricht liefert den Zeichen-Index des ersten Buchstaben in der spezifierten Zeile zurück. Wir senden die Zeilennummer in wParam und bei Rückkehr haben wir den Zeichen-Index.

        invoke SendMessage,hwndRichEdit,EM_SETSEL,eax,eax
Um die aktuelle Selektion zu setzen, benutzen wir diesmal EM_SETSEL, da die Zeichen-Indize noch nicht in einer CHARRANGE Struktur vorliegen, womit wir zwei Befehle sparen (um die Indize in eine CHARRANGE Struktur zu bringen).

        invoke SetFocus,hwndRichEdit
    .endif
Der Cursor wird solange nicht angezeigt, bis das RichEdit Steuerelement den Fokus hat. Deshalb rufen wir SetFocus auf.





Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage