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:
- Den gesamten Text im Steuerelement beeinflussen (SCF_ALL)
- Den Text in der aktuellen Markierung beeinflussen (SCF_SELECTION)
- 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