Iczelion - 33 - RichEdit Steuerelement: Grundlagen

Tutorial 33: RichEdit Steuerelement: Grundlagen



Es gab viele Anfragen wegen Tutorials über das RichEdit Steuerelement. Letztendlich habe ich genügend damit rumgespielt, dass ich denke, ich kann ein Tutorial darüber schreiben. So hier ist es: das erste RichEdit Tutorial. Die Tutorials werden fast alles beschreiben, was man übers RichtEdit Steuerelement wissen kann oder wenigstens soviel, wie ich weiß. Die Menge der Informationen ist so groß, dass ich sie in mehrere Teile teile, dieses Tutorial ist der erste Teil. In diesem Tutorial werden Sie lernen, was ein RichEdit Steuerelement ist, wie man es erzeugt und wie man Daten hinein/daraus lädt/speichert.

Laden Sie das Beispiel herunter.

Theorie

Ein RichEdit Steuerelement kann als aufgepepptes Edit Steuerelement angesehen werden. Es stellt eine Menge wünschenswerter Features zu Verfügung, die dem einfachen Edit Steuerelement fehlen, zum Beispiel die Möglichkeit multipler Fonts, bzw. Größen, multiple wiederholen/rückgangängig-mach-Funktionen, nach Text suchen, OLE-embedded Objekte, drag-and-drop Suppor, etc. Da das RichEdit Steuerelement so viele Features hat, ist es in einer seperaten DLL gespeichert. Das bedeutet auch, dass Sie nicht einfach InitCommonControls wie bei den Standard Steuerelementen aufrufen können. Sie müssen LoadLibrary aufrufen, um die RichEdit DLL zu laden.

Das Problem dabei ist, dass es drei Versionen bis jetzt gibt. Version 1, 2 und 3 Die folgende Tabelle zeigt Ihnen die Namen der DLL für jede Version.

DLL Name RichEdit version Richedit Klassen Name
Riched32.dll 1.0 RICHEDIT
RichEd20.dll 2.0 RICHEDIT20A
RichEd20.dll 3.0 RICHEDIT20A
Sie werden bemerken, dass die RichEdit Version 2 und 3 den selben DLL Namen benutzen. Sie benutzen auch den selben Klassen Namen! Das kann Probleme aufwerfen, wenn Sie spezielle Features von RichEdit 3.0 benutzen wollen. Bis jetzt habe ich keine offizielle Methode gefunden, um zwischen Version 2.0 und 3.0 zu differenzieren. Wie dem auch sei, es gibt einen Trick der ganz gut funktioniert, den ich Ihnen später zeigen werde.

.data
   RichEditDLL db "RichEd20.dll",0
.....
.data?
hRichEditDLL dd ?
.code
invoke LoadLibrary,addr RichEditDLL
mov hRichEditDLL,eax
......
invoke FreeLibrary,hRichEditDLL
Wenn die RichEdit DLL geladen ist, registriert sie die RichEdit Fenster-Klasse. Demnach ist es notwendig, dass Sie die DLL vor der Erzeugung des Steuerelementes laden. Die Namen der RichEdit Steuerelement Klassen sid auch verschieden. Nun werden Sie vielleicht die Frage haben: wie weiß ich, welche Version des RichEdit Steuerelementes Sie benutzen sollen? Die letzte Version zu benutzen macht nicht immer Sinn, wenn Sie die Extra-Features nicht benötigen. Deshalb folgt eine Tabelle, die die unterstützten Features jeder Version des RichEdit Steuerelementes auflistet.

Feature Version 1.0 Version 2.0 Version 3.0
selection bar
x
x
x
unicode editing
x
x
Zeichen/Paragraph Formatierung
x
x
x
nach Text suchen
abwärts
abwärts/aufwärts
abwärts/aufwärts
OLE embedding
x
x
x
Drag and drop editing
x
x
x
Undo/Redo
single-level
multi-level
multi-level
automatische URL Erkennung
x
x
Unterstützung von Shortcuts
x
x
Fensterlose Operation
x
x
Zeilenumbruch
CRLF
nur CR
nur CR (kann Version 1.0 emulieren)
Zoom
x
Paragraph Nummerierung
x
einfache Tabelle
x
normale und heading Stile
x
farbiges unterstreichen
x
versteckter Text
x
font binding
x
Die Tabelle ist auf keinen Fall vollständig: Ich habe nur die wichtigsten Features aufgelistet.

Erzeugung des RichEdit Steuerelementes

Nachdem die RichEdit DLL geladen ist, können Sie CreateWindowEx aufrufen, um das Steuerelement zu erzeugen. Sie können die Edit Steuerlement Stile und Standard Fenster Stile bei CreateWindowEx benutzen, außer ES_LOWERCASE, ES_UPPERCASE und ES_OEMCONVERT.

.const
    RichEditID equ 300
.data
    RichEditDLL db "RichEd20.dll",0
RichEditClass db "RichEdit20A",0 ... .data?
hRichEditDLL dd ? hwndRichEdit dd ? .code ..... invoke LoadLibrary,addr RichEditDLL mov 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,0 mov hwndRichEdit,eax

Die Standard Text und Hintergrund-Farbe setzen

Sie werden vielleicht Probleme haben die Text und Hintegrund-Farbe des Edit Steuerelementes zu setzen. Aber dieses Problem wurde im RichEdit Steuerelement beseitigt. Um die Hintergrundfarbe des RichEdit Steuerelementes zu setzen, senden Sie EM_SETBKGNDCOLOR an das RichEdit Steuerelement. Die Nachricht hat folgende Syntax:

wParam == Farb Option. Der Wert 0 in diesem Parameter gibt an, dass Windows den Farb-Wert in lParam als Hintergrund Farbe benutzt. Wenn der Wert ungleich null ist, benutzt Windows die Windows-System Hintergrund-Farbe. Da wir diese Nachricht senden, um die Hintergrundfarbe zu ändern, müssen wir 0 in wParam übergeben.
lParam == spezifiziert die COLORREF Struktur der Farbe, die Sie setzen wollen, wenn wParam 0 ist.

Wenn Sie zum Beispiel die Hintergrundfarbe auf pures Blau setzen wollen, würde ich folgende Zeile benutzen:

        invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,0FF0000h
Um die Textfarbe zu setzen, unterstützt das RichEdit Steuerelement eine weitere neue Nachricht, EM_SETCHARFORMAT, für diese Arbeit. Diese Nachricht kontrolliert die Text-Formatierungen narkierten der Zeichen, bzw. des gesamten Textes. Diese Nachricht hat folgende Syntax:

wParam == Formatierungs Optionen:

SCF_ALL Die Operation betrifft den gesamten Text im Steuerelement.
SCF_SELECTION Die Operation betrifft nur den markierten Text
SCF_WORD or SCF_SELECTION Betrifft nur die markierten Wörter. Wenn keine Selektierung vorliegt, dh. der Cursor in einem Wort steht, betrifft es nur das Wort. Das SCF_WORD Flag muss mit SCF_SELECTION benutzt werden.
lParam == Zeiger auf eine CHARFORMAT oder CHARFORMAT2 Struktur die die vorzunehmenden Text-Formatierungen spezifiziert. CHARFORMAT2 ist nur für RichEdit 2.0 oder höher verfügbar. Das bedeutet nicht, dass Sie CHARFORMAT2 mit RichEdit 2.0 oder höher benutzen müssen. Sie können immer noch CHARFORMAT benutzen, wenn die hinzugefügten Features in CHARFORMAT2 nicht von Ihnen benötigt werden.

CHARFORMATA STRUCT
    cbSize DWORD ?
    dwMask DWORD ?
    dwEffects DWORD    ?
    yHeight DWORD ?
    yOffset DWORD ?
    crTextColor COLORREF ?
    bCharSet BYTE ?
    bPitchAndFamily    BYTE ?
    szFaceName BYTE LF_FACESIZE dup(?)
    _wPad2 WORD ?
CHARFORMATA ENDS 
Feld Name Beschreibung
cbSize Die Größe der Struktur. Das RichEdit Steuerelement benutzt dieses Feld, um die Version der Struktur zu bestimmen, ob es entweder CHARFORMAT oder CHARFORMAT2 ist
dwMask Bit Flags die bestimmen, welche der folgenden Elemente gültig sind.

CFM_BOLD Der CFE_BOLD Wert des dwEffects Elementes ist gültig
CFM_CHARSET Das bCharSet Element ist gültig.
CFM_COLOR Das crTextColor Element und der CFE_AUTOCOLOR Wert des dwEffects Elementes sind gültig
CFM_FACE Das szFaceName Element ist gültig.
CFM_ITALIC Der CFE_ITALIC Wert des dwEffects Elementes ist gültig
CFM_OFFSET Das yOffset Element ist gültig
CFM_PROTECTED Der CFE_PROTECTED Wert des dwEffects Elementes ist gültig
CFM_SIZE Das yHeight Element ist gültig
CFM_STRIKEOUT Der CFE_STRIKEOUT Wert des dwEffects Elementes ist gültig.
CFM_UNDERLINE Der CFE_UNDERLINE Wert des dwEffects Elementes ist gültig
dwEffects Die Zeichen Eigenschaften. Kann eine Kombinatoin aus folgenden Werten sein

CFE_AUTOCOLOR Benutzt die System-Text-Farbe
CFE_BOLD Zeichen sind dick
CFE_ITALIC Zeichen sind kursiv
CFE_STRIKEOUT Zeichen sind durchgestrichen.
CFE_UNDERLINE Zeichen sind unterstrichen
CFE_PROTECTED Zeichen sind geschützt; der Versuch sie zu ändern erzeugt eine EN_PROTECTED Benachrichtigungs-Nachricht.
yHeight Zeichen Höhe in Twips (1/1440 eines Inch's oder 1/20 eines Drucker Punktes).
yOffset Zeichen Offset in Twips, von der Grundlinie aus. Wenn der Wert dieses Elementes Positiv ist, ist dieses Zeichen ein Superscript (hochgestellt); wenn es negativ ist, ist das Zeichen ein Subscript (tiefgestellt).
crTextColor Text-Farbe. Dieses Element wird ignoriert, wenn die CFE_AUTOCOLOR Zeichen-Eigenschaft angegeben ist.
bCharSet Zeichensatz
bPitchAndFamily Font Familie und Pitch.
szFaceName Null-terminiertes Zeichen Array spezifiziert den Font Namen
_wPad2 Padding (=Block)
Sie werden beim durchsehen der Struktur sehen, dass wir die Text-Eigenschaften (bold,italic, strikeout,underline), Text-Farbe (crTextColor) Und Font Aussehen/Größe/Zeichensatz ändern können. Ein beachtenswertes Flag ist CFE_RPOTECTED. Der Text ist mit diesem Flag als geschützt markiert, was bedeutet, dass wenn der Benutzer versucht ihn zu modifizieren, eine EN_PROTECTED Benachrichtungs-Nachricht an das Parent-Fenster gesendet wird. Und Sie können erlauben, ob die Änderung vorgenommen werden darf, oder nicht.

CHARFORMAT2 fügt mehr Text Stile hinzu, so wie Font Breit, Abstand, Text-Hintergrund-Farbe, etc. Wenn Sie die Extra-Features nicht benötigen, verwenden Sie einfach CHARFORMAT.

Um Text-Formatierungen zu setzen, müssen Sie über den Range des Textes nachdenken, der beeinflusst werden soll. Das RichEdit Steuerelement den Begriff des Zeichen-Text-Ranges ein. (Anm. d. Übersetzers: Range=Spannweite, (Beeinflussungs-)Bereich). Das RichEdit Steuerelement gibt jedem Zeichen eine Zeichen Nummer mit 0 beginnend: das erste Zeichen in dem Steuerelement hat die ID 0, das zweite Zeichen 1 und so weiter. Um den Text Range zu spezifizieren, müssen Sie dem RichEdit Steuerelement zwei Zahlen übermitteln: die IDs des ersten und des letzten Buchstabens des Range. Um Text mit EM_SETCHARFORMAT zu beeinflussen, haben Sie drei Möglichkeiten:

  1. Den gesamten Text im Steuerelement beeinflussen (SCF_ALL)
  2. Den Text in der aktuellen Markierung beeinflussen (SCF_SELECTION)
  3. Das ganze Wort in der aktuellen Markierung beeinflussen (SCF_WORD oder SCF_SELECTION)
Die erste und zweite Wahl sind eindeutig. Die letzte Wahl benötigt etwas Erklärung. Wenn die aktuelle Markierung nur ein oder mehr Zeichen in einem Wort, aber nicht das ganze Wort markiert sind, spezifiziert das Flag SCF_WORD+SCF_SELECTION, dass das ganze Wort von der Text-Formatierung beeinflusst werden soll. Selbst wenn keine aktuelle Markierung vorliegt, dh. nur der Cursor innerhalb eines Wortes positioniert ist, wird bei der dritten Wahl das gesamte Wort formatiert.

Um EM_SETCHARFORMAT zu nutzen, müssen Sie verschiedene Elemente der CHARFORMAT (oder CHARFORMAT2) Struktur füllen. Wenn Sie zum Beispiel die Text-Farbe setzen wollen, füllen wir die CHARFORMAT Struktur wie folgt:

.data?
    cf CHARFORMAT 
....
.code
    mov cf.cbSize,sizeof cf
    mov cf.dwMask,CFM_COLOR
    mov cf.crTextColor,0FF0000h
    invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cf
Der obige Code-Ausschnitt setzt die Text-Farbe des RichtEdit Steuerelementes auf pures blau. Bachten Sie, dass, wenn kein Text sich im RichEdit Steuerelement befindet, wenn EM_SETCHARFORMAT angefordert wird, wird der Text, der nachfolgend der Nachricht eingegeben wird, wird die Text-Formatierungen, die durch die EM_SETCHARFORMAT Nachricht spezifiziert sind, benutzen.


Text laden/Text speichern

Für die unter Ihnen, die mit dem Edit-Steuerelement gearbeitet haben, sind sicherlich mitWM_GETTEXT/WM_SETTEXT vertraut, was bedeutet, dass Text aus/in das Steuerelement gelesen/geladen wird. Diese Method funktioniert immer noch, mag bei großen Dateien aber nicht effizient sein. Das Edit-Steuerelement beschränkt den Text, der eingegeben werden kann, auf 64K, aber das RichEdit Steuerelement akzeptiert wesentlich mehr als das. Es wäre ziemlich nervenaufreibend einen sehr großen Speicherblock zu alloziieren (so wie 10 MB oder so) um den Text mittels WM_GETTEXT zu erhalten. Das RichEdit Steuerelement bietet eine neue Möglichkeit dieser Methode gegenüber, d.h. Text Streaming.

Einfach gehalten, übergeben Sie die Adresse einer Callback-Funktion an das RichEdit Steuerelement. Und das RichEdit Steuerelement ruft diese Callback-Funktion auf, die Adresse des Buffers übergebend, wenn sie fertig ist. Die Callback-Funktion füllt den Buffer mit den Daten, die es an das Steuerelement senden will oder ließt Daten aus dem Buffer und wartet dann auf den nächsten Aufruf, bis die Operation beendet ist. Dieses Paradigma wir für beides, Streaming In (Text setzen) und Streaming Out (Text aus dem Steuerelement auslesen) benutzt. Sie werden sehen, dass diese Methode effizienter ist: der Buffer wird vom RichEdit Steuerelement selbst zu Verfügung gestellt, so dass die Daten in Chunks geteilt werden. In die Operation sind zwei Nachrichten involviert: EM_STREAMIN und EM_STREAMOUT

Beide, EM_STREAMIN und EM_STREAMOUT benutzen die selbe Syntax:

wParam == Formatierungs-Optionen.

SF_RTF Die Daten sind im Rich-Text Format (RTF)
SF_TEXT Die Daten sind im reinen Text-Format
SFF_PLAINRTF Nur die Schlüsselwörter der gebräuchlichen Spraceh werden ein-gestreamt.
SFF_SELECTION Wenn spezifiziert, ist das Ziel der Operation der aktuell markierte Text. Wenn Sie den Text ein-streamen, ersetzt der Text die aktuelle Markierung. Wenn Sie den Text aus-streamen, wird nur der aktuell markierte Text aus-gestreamt. Wenn dieses Flag nicht angegeben wird, wird der gesamte Text im Steuerelement von der Operation beeinflusst.
SF_UNICODE (Verfügbar ab RichEdit 2.0 oder höher) Spezifiziert den Unicode Text.
lParam == zeigt auf eine EDITSTREAM Struktur, die die folgende Definition hat:

 EDITSTREAM STRUCT
    dwCookie DWORD    ?
    dwError DWORD ?
    pfnCallback DWORD ?
EDITSTREAM ENDS
dwCookie Applikations-definierter Wert, der der Callback-Funktion übergeben wird, die im pfnCallback Element unten spezifziert ist. Wir übergeben normalerweise einige wichtige Werte der Callback-Funktion, so wie Datei-Handle, das in der Stream-in/out Prozedur benutzt wird.
dwError Indiziert die Ergebnisse der Stream-in (lese) oder Stream-out (schreib) Operation. Ein Wert gleich 0 indiziert keinen Fehler. Ein Wert ungleich null, kann der Rückgabewert der EditStreamCallback Funktion oder ein Code, der einen Fehler indiziert, bedeuten.
pfnCallback Zeiger auf eine EditStreamCallback Funktion, welche eine Applikations-definierte Funktion ist, die das Steuerelement aufruft, um Daten zu transferieren. Das Steuerelement ruft die die Callback-Funktion immer wieder auf, und transferiert dabei einen Teil der Daten bei jedem Aufruf
Die Editstream Callback Funktion hat folgende Definition:

    EditStreamCallback proto dwCookie:DWORD,
pBuffer:DWORD, NumBytes:DWORD, pBytesTransferred:DWORD
Sie müssen eine Funktion mit obigen Prototypen in Ihrem Programm erzeugen. Und dann dessen Adresse EM_STREAMIN oder EM_STREAMOUT via der EDITSTREAM Struktur übergeben.

Für Stream-in Operationen (den Text im RichEdit Steuerelement setzen):

    dwCookie: der Applikations-definierte Wert, den Sie EM_STREAMIN via der EDITSTREAM Struktur übergeben. Wie übergeben hier fast immer das Datei-Handle der Datei, dessen Inhalt wir im Steuerelement setzen wollen.
    pBuffer: zeigt auf den Buffer, der vom RichEdit Steuerelement zur Verfügung gestellt wird, der den Text von Ihrer Callback-Funktion empfängt.
    NumBytes: die maximale Anzahl an Bytes, die Sie in diesen Buffer (pBuffer) während des Calls (Aufruf) schreiben können. Sie MÜSSEN immer diese Limit beachten, dh. Sie können weniger Daten senden, als der Wert in NumBytes, aber dürfen nicht mehr Daten als diesen Wert senden. Sie können sich diesen Wert als Größe des Buffers in pBuffer vorstellen.
   pBytesTransferred: zeigt auf ein DWord, dass Sie setzen müssen, der Wert inidiziert die Anzahl der Bytes, die Sie tatsächlich in den Buffer transferiert haben. Dieser Wert ist normalerweise identisch mit dem Wert in NumBytes. Die Ausnahme ist, wenn die weniger Daten gesendet werden, als der Buffer unterstützt, so wie wenn das Dateiende erreicht ist.
Für Stream-out Operationen (den Text aus dem RichEdit Steuerelement lesen):

    dwCookie: Dasselbe wie bei der Stream-in Operation. Wir übergeben normalerweise das Datei-Handle, wo wir die Daten hinein schreiben wollen, in diesem Parameter.
    pBuffer: zeigt auf den Buffer, der vom RichEdit Steuerelement zur Verfügung gestellt wird, der mit den Daten aus dem RichEdit Steuerelement gefüllt wird. Um seine Größe zu erhalten, müssen Sie den Wert aus NumBytes untersuchen. 
    NumBytes: die Größe der Daten in dem Buffer, auf den von pBuffer gezeigt wird.
    pBytesTransferred: zeigt auf ein DWord, dessen Wert Sie auf die Anzahl der tatsächlich aus dem Buffer gelesenen Bytes setzen müssen. 
Die Callback Funktion liefert bei Erfolg 0 zurück und das RichEdit Steuerelement wird weiterhin die Callback-Funktion aufrufen, soweit noch Daten zum lesen/schreiben übrig sind. Wenn ein Fehler während der Ausf+hrung auftritt und Sie die Operation stoppen wollen, wird ein Wert ungleich Null zurückgeliefert und das RichEdit Steuerelement wird die Daten, auf die von pBuffer gezeigt wird, verwerfen. Der Fehler/Erfolgs- Wert wird im dwError Feld von EDITSTREAM gespeichert, so dass Sie den Feler-/Erfolgs-Status der Stream-Operation nach der Rückkehr von SendMessage untersuchen können.

Beispiel:

Das folgende Beispiel ist ein simpler Editor, womit Sie ASM Source Code Dateien öffnen, editieren und speichern können. Es benutzt das RichEdit Steuerelement Version 2.0 oder höher.

.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

RichEditID             equ 300

.data
ClassName db "IczEditClass",0
AppName  db "IczEdit version 1.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 in dem Steuerelement wurden verändert. Wollen Sie speichern?",0
FileOpened dd FALSE
BackgroundColor dd 0FFFFFFh        ; Standard auf weiß
TextColor dd 0        ; Standard auf schwarz

.data?
hInstance dd ?
hRichEdit dd ?
hwndRichEdit dd ?
FileName db 256 dup(?)
AlternateFileName db 256 dup(?)
CustomColors dd 16 dup(?)

.code
start:
    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
    .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
    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
                ;==================================================================================
                ; speicher den veränderungs-Status des RichtEdit Steuerelementes, da das ändern der Textfarbe den
                ; Veränderungs-Status des RichtEdit Steuerelementes verändert.
                ;==================================================================================
                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

WndProc 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
        ;=============================================================
        ; Setze das Text-Limit. Standard sind 64K
        ;=============================================================
        invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
        ;=============================================================
        ; Setze die Standard Text/Hintergrund-Farbe
        ;=============================================================
        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        ; Datei Menü
            .if FileOpened==TRUE    ; eine Datei ist bereits 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    ; edit menu
            ;=============================================================================
            ; Überprüfe ob etwas Text in der Zwischenablage ist. Wenn ja, aktivieren wir das Menüelement Paste (Einfügen)
            ;=============================================================================
            invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
            .if eax==0        ; kein Text in der Zwischenablage
                invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
            .else
                invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
            .endif
            ;==========================================================
            ; Überprüfe ob die Undo (=Einfügen)-Queue leer ist
            ;==========================================================
            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
            ;=========================================================
            ; überprüfe ob die Redo (=wiederholen)-Queue leer ist
            ;=========================================================
            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
            ;=========================================================
            ; Überprüfe, ob zur Zeit eine Selektierung im RichEdit Steuerelement vorliegt.
            ; Wenn ja, aktivieren wir die Menüelemente cut/copy/delete (=ausschneiden/kopieren/löschen)
            ;=========================================================
            invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
            mov eax,chrg.cpMin
            .if eax==chrg.cpMax        ; nein, zur Zeit keine Selektierung
                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        ; Menü Befehle
            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 die 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_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

;===================================================================
; Die Ressource Datei
;===================================================================
#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                        40013

IDR_MAINMENU MENU DISCARDABLE
BEGIN
    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_OPTION
END


IDD_OPTIONDLG DIALOG DISCARDABLE  0, 0, 183, 54
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_CENTER
CAPTION "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_BORDER
END

Analyse:

Das Programm lädt als erstes die RichEdit DLL, welche in diesem Fall riched20.dll ist. Wenn die DLL nicht geladen werden kann, kehrt das Programm zu Windows zurück.


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
Nachdem die DLL erfolgreich geladen ist, fahren wir mit der Erzeugung eines normalen Fensters fort, welches das Parent des RichEdit Steuerelementes sein wird. Im WM_CREATE Handler, erzeugen wir das RichEdit Steuerelement:

        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
Beachten Sie, dass wir den ES_MULTILINE Stil angeben, ansonsten wird es ein einzeiligese Steuerelement.


        invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0
Nachdem das RichEdit Steuerelement erzeugt wurde, müssen wir das Text Limit setzen. Standardmäßig, hat das RichEdit Steuerelement ein 64K Text Limit, das selbe wie ein einfaches mehrzeiliges Edit Steuerelement. Wir müssen dieses Limit erweitern um das Arbeiten mit größeren Dateien zu erlauben. In der obigen Zeile, gebe ich -1 an, was den Wert 0FFFFFFFFh hat, ein sehr großer Wert.

       invoke SetColor

Als nächstes setzen wir die Text-/Hintergrundfarbe. Da diese Operation auch in anderen Teilen des Programmes ausgeführt werden kann, packe ich den Code in eine Funktion names SetColor.


SetColor proc
    LOCAL cfm:CHARFORMAT
    invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor
Die Hintergrundfarbe eines RichEdit Steuerelementes zu setzen ist ziemlich einfach: einfach eine EM_SETBKGNDCOLOR Nachricht an das RichEdit Steuerelement sende. (Wenn Sie ein mehrzeiliges Edit Steuerelement benutzen, müssen Sie WM_CTLCOLOREDIT benutzen). Die Standard-Hintergrund-Farbe ist weiß.


    invoke RtlZeroMemory,addr cfm,sizeof cfm
    mov cfm.cbSize,sizeof cfm
    mov cfm.dwMask,CFM_COLOR
    push TextColor
    pop cfm.crTextColor
Nachdem die Hintergrundfarbe gesetzt wurde, füllen wir die Elemente von CHARFORMAT um die Textfarbe zu setzen. Beachten Sie, dass wir cbSize mit der Größe der Struktur füllen, so dass das RichEdit Steuerelement weiß, das wir ihm CHARFORMAT, nicht CHARFORMAT2 senden. dwMask hat nur ein Flag, CFM_COLOR, da wir nur die Textfarbe setzen wollen und crTextColor ist mit dem Wert der gewünschten Textfarbe gefüllt.

    invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cfm
    ret
SetColor endp
Nachdem die Farbe gesetzt wurde, müssen Sie den Rückgängig-Buffer leeren, einfach weil das ändern der Text-/Hintergrund-Farbe rückängig gemacht werden kann. Wir senden eine EM_EMPTYUNDOBUFFER Nachricht, um das zu erreichen.

    invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0 
Nachdem die CHARFORMAT Struktur gefüllt ist, senden wir EM_SETCHARFORMAT an das RichEdit Steuerelement, spezifizieren das SCF_ALL Flag in wParam um zu indizieren, dass wir die Text-Formatierungen auf den gesamten Text im Steuerelement anwenden wollen.

Beachten Sie, dass wir die Größe/Position des RichtEdit Steuerelementes nicht angeben, wenn wir es das erste Mal erzeugen. Das kommt daher, dass wir es auf der gesamten Client Area des Parent-Fensters haben wollen. Wir ändern sein Größe immer dann, wenn sich die Größe des Parent Fensters ändert.

    .elseif uMsg==WM_SIZE
        mov eax,lParam
        mov edx,eax
        and eax,0FFFFh
        shr edx,16
        invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE
In dem obigen Code Ausschnitt benutzen wir die neue Dimensionen der Client Area, die in lParam übergeben wurden um das RichEdit Steuerelement mit MoveWindow in der Größe zu ändern.

Wenn der Benutzer auf File/Edit klickt, bearbeiten wir WM_INITPOPUPMENU, so dass wir die Stati der Menüitems in den Submenüs vorbereiten können, bevor sie dem Benutzer gezeigt werden. Wenn eine Datei zum Beispiel schon geöffnet ist, wollen wir das Open Menüelement deaktivieren und die übrigen Menüelemente aktivieren.

Für den Fall des File Menüs benutzen wir die Variable FileOpened als Flag, um zu bestimmen, ob eine Datei bereits geöffnet ist. Wenn der Wert der Variable TRUE ist, wissen wir, dass eine Datei bereits geöffnet ist.

    .elseif uMsg==WM_INITMENUPOPUP
        mov eax,lParam
        .if ax==0        ; Datei Menü
            .if FileOpened==TRUE    ; eine Datei ist bereits 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
Wie Sie sehen können, grauen wir das Open Menüelement aus, wenn eine Datei bereits geöffnet ist und aktivieren die übrigen Menüelemente. Das Gegenteil ist wahr, wenn FileOpened falsch ist.

In diesem Fall müssen wir als erstes für das Edit Menü den Status der Zwischenablage des RichEdit Steuerelementes überprüfen.

            invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0
            .if eax==0        ; kein Text in der Zwischenablage
                invoke EnableMenuItem,wParam,IDM_PASTE,MF_GRAYED
            .else
                invoke EnableMenuItem,wParam,IDM_PASTE,MF_ENABLED
            .endif
Als erstes überprüfen wir, ob sich Text in der Zwischenablage befindet, indem wir eine EM_CANPASTE Nachricht senden. Wenn Text verfügbar ist, liefert SendMessage TRUE zurück und wir aktivieren das Paste Menüelement. Wenn nicht grauen wir das Menüelement aus.

            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
Als nächstes überprüfen wir, ob der Rückgäging (Undo)-Buffer leer ist, indem wir eine EM_CANUNDO Nachricht senden. Wenn er nicht leer ist, liefert SendMessage TRUE zurück und wir aktivieren das Undo Menüelement.

            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
Wir überprüfen den Wiederholen (Redo-) Buffer in dem wir eine EM_CANREDO Nachricht an das RichEdit Steuerelement senden. Wenn er nicht leer ist, liefert SendMessage TRUE zurück und wir aktivieren das Redo Menüelement.

            invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg
            mov eax,chrg.cpMin
            .if eax==chrg.cpMax        ; zur Zeit keine Selektierung
                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
Als letztes überprüfen wir, ob zur Zeit eine Markierung existiert, indem wir eine EM_EXGETSEL Nachricht senden. Diese Nachricht benutzt eine CHARRANGE Struktur, die wie folgt definiert ist:

CHARRANGE STRUCT
  cpMin  DWORD      ?
  cpMax  DWORD      ?
CHARRANGE ENDS
cpMin enthält den Zeichen-Positions-Index der unmittelbar dem ersten Zeichen im Range folgt.
cpMax enthält den Zeichen-Positions-Index der unmittelbar dem letzten Zeichen im Range folgt.

Nachdem EM_EXGETSEL zurückkehrt, ist die CHARRANGE Struktur mit dem starten-endenden Zeichen-Position-Indizes des Markierungs-Range gefüllt. Wenn es zur Zeit keine Markierung gibt, sind cpMin und cpMax identisch und wir grauen die Menüelemente cut/copy/delete aus.

Wenn der Benutzer das Open Menüelement klickt, zeigen wir einen Datei-Öffnen-Dialog an und wenn der Benutzer eine Datei auswählt, öffnen wir die Datei und streamen den Inhalt in das RichEdit Steuerelement.

                    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
Nachdem die Datei erfolgreich mit CreateFile geöffnet wurde, füllen wir die EDITSTREAM Struktur als Vorbereitung für die EM_STREAMIN Nachricht. Wir senden das Handle der geöffneten Datei via dem dwCookie Element und übergeben die Adresse der Stream Callback Funktion in pfnCallback.

Die Stream Callback Prozedur selbst ist ziemlich einfach.

StreamInProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesRead:DWORD
    invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0
    xor eax,1
    ret
StreamInProc endp
Wie Sie sehen können, passen alle Parameter der Stream Callback Prozedur perfekt zu ReadFile. Und der Rückgabewert von ReadFile wird mit 1 ge-xor-ed, so dass 1 zurückgeliefert wird (Erfolg), der aktuelle Wert der in EAX zurückgeliefert wird ist 0 und vice versa.

    invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0
    invoke CloseHandle,hFile
    mov FileOpened,TRUE
Nachdem EM_STREAMIN zurückkehrt, bedeutet das, dass die Stream Operation beenden ist. In Wirklichkeit müssen wir den Wert des dwError Elementes der EDITSTREAM Struktur überprüfen.

Das RichEdit (und Edit) Steuerelement unterstützen ein Flag, das indiziert, ob der Inhalt modifiziert wurde. Wir können den Inhalt des Flags erhalten, in dem wir eine EM_GETMODIFY Nachricht an das Steuerelement senden. SendMessage liefert TRUE zurück, wenn der Inhalt des Steuerelementes modifiziert wurde. Da wir den Text des Steuerelementes in das Steuerelement streamen, ist es eine Art Modifikation. Wir müssen das Modify-Flag auf FALSE setzen, indem wir EM_SETMODIFY mit wParam==FALSE an das Steuerelement senden, um neu zu beginnen, nachdem die Stream-Operation beendet ist. Wie schließen unverzüglich die Datei und setzen FileOpened auf TRUE, um zu indizieren, dass eine Datei geöffnet ist.

Wenn der Benutzer das Menüelement Save/Save as anklickt, benutzen wir die EM_STREAMOUT Nachricht, um den Inhalt des RichEdit Steuerelementes in einer Datei zu speichern. Wie die StreamIn Callback-Funktion, erklärt sich die StreamOut-Callback-Funktion von selbst. Sie passt perfekt mit WriteFile zusammen.

Die Text Operationen so wie cut/copy/paste/redo/undo sind einfach implementiert, indem eine einzelne Nachricht an das RichEdit Steuerelement gesendet wird, respektive WM_CUT/WM_COPY/WM_PASTE/WM_REDO/WM_UNDO.

Die delete/select all Operationen werden wie folgt realisiert:

            .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
Die delete Operation beeinflusst die aktuelle Selektion. Ich sende eine EM_REPLACESEL Nachricht mit einem NULL String, so dass das RichEdit Steuerelement den aktuell selektierten Text mit dem null String ersetzt.

Die select-all Operation wird durch ein senden, einer EM_EXSETSEL Nachricht realisiert, spezifizierend cpMin==0 und cpMax==-1 was den ganzen Text selektiert.

Wenn der Benutzer das Menü Option wählt, zeigen wir einen Dialog an, der die aktuelle Hintergrund-/Text-Farbe präsentiert.



Wenn der Benutzer auf einer der Farb-Boxen klickt, zeigt es den Farbe-wählen-Dialog an. Die "Farb Box" ist in Wirklichkeit ein statisches Steuerelement mit SS_NOTIFY und WS_BORDER Flag. Ein statisches Steuerelement mit SS_NOTIFY Flag benachrichtigt sein Parent-Fenster, bei Maus-Aktionen, so wie BN_CLICKED (STN_CLICKED). Das ist der Trick.


            .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
Wenn der Benutzer auf eine der Farb-Boxen klickt, füllen wir die Elemente der CHOOSECOLOR Struktur und rufen ChooseColor auf, um den Farbe-wählen-Dialog anzuzeigen. Wenn der Benutzer eine Farbe auswählt, wird der colorref Wert im rgbResult Element zurückgeliefert und wir speichern diese Wert in der BackgroundColor Variable. Danach zwingen wir die Farb-Box erneut zu zeichnen, indem wir InvalidateRect mit dem Handle der Farbe-Box aufrufen. Die Farb-Box sendet eine WM_CTLCOLORSTATIC Nachricht an sein Parent-Fenster.

        invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX
        .if eax==lParam
            invoke CreateSolidBrush,BackgroundColor
            ret
Im WM_CTLCOLORSTATIC Handler, vergleichen wir das Handle des statischen Steuerelementes, dass in lParam übergeben wurde, mit dem der beiden Farb-Boxen.Wenn die Werte übereinstimmen, erzeugen wir einen enuen Brush und benutzen dabei die Farbe aus der Variable und kehren unverzüglich zurück. Das statische Steuerelement wird den neu erzeugten Brush erzeugen, um den Hintergrund zu zeichnen.


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