| Vorheriges Kapitel (Fensterklassen) |
Dialoge stellen neben Popup-Fenstern eine weitere Möglichkeit dar, Informationen vom Anwender abzufragen. Sie werden in den meisten Fällen in einer Ressourcendatei definiert. Zwar ist auch die komplett dynamische Erzeugung von Dialogen möglich, von dieser Variante wird aber hier kein Gebrauch gemacht.
Es gibt zwei große Typen von Dialogen: modale und nichtmodale bzw. moduslose. Ein modaler Dialog schränkt während seiner Anzeige den Zugriff auf andere sichtbare Fenster der gleichen Anwendung ein. Der Dialog muss erst beendet werden, damit die anderen Fenster wieder erreichbar sind. Eine Erweiterung dieses Konzepts stellen die systemmodalen Dialoge dar, die auch den Zugriff auf Fenster einschränken, die nicht zur eigenen Applikation gehören. Ein Beispiel hierfür ist der Dialog "Windows herunterfahren". Moduslose Dialoge erlauben während ihrer Anzeige den Wechsel zu anderen Fenstern der gleichen Anwendung. Dialoge zum Suchen von Text sind in vielen Anwendungen als moduslose Dialoge definiert.
Modale und Systemmodale Dialoge werden mit der Funktion DialogBoxParam() gestartet, moduslose Dialoge mit der Funktion CreateDialog().
Beiden Typen gleich ist, dass die Steuerung über eine sogenannte Dialogprozedur erfolgt, deren Aufbau mit der Windowprozedur vergleichbar ist. Dabei wird uns ein großer Teil Arbeit durch vordefinierte Mechanismen von Windows abgenommen. Insbesondere die Steuerung des Tastaturfokus mittels der Tasten Tab, Shift+Tab und den Pfeiltasten sowie Reaktionen auf Alt+Buchstabe um bestimmte Dialogelemente direkt anzuspringen muss durch uns nicht programmiert werden.
Im Gegensatz zu Menüs kommt man bei Dialogen nicht so ohne weiteres um die Verwendung von Ressourcedateien herum. Es gibt zwar auch einen Weg, Dialoge erst zur Laufzeit im Speicher aufzubauen, diese werden aber nicht in diesem Tutorial beschrieben. Falls sie sich dafür interessieren, dann schauen sie sich die Funktionen CreateDialogIndirect() und DialogBoxIndirect() sowie die Strukturen DLGTEMPLATEEX und DLGITEMTEMPLATEEX an.
Obwohl es sich bei Ressourcedateien um Textdateien handelt, werden sie bei der Arbeit mit Hochsprachen-IDEs wie etwa Visual C++ nur selten von Hand angepasst. Stattdessen gibt es dafür Ressource-Editoren, mit denen Menüs, Dialoge und sonstige Ressourcen zusammengeklickt werden können.
In der MASM32-Auslieferung ist kein Ressourcen-Editor enthalten, und wenn sie keinen haben, dann bleibt ihnen nichts anderes, als die Ressourcedatei selbst zu schreiben. Da MASM und rc.exe ihre Konstanten auf unterschiedliche Art und Weise definieren, wurde dem MASM32-Paket die Datei resource.h beigelegt, die die für rc.exe benötigten Konstantendefinitionen enthält. Diese muss dann im jeweiligen Ressource-Skript eingebunden werden.
Die definierbaren Steuerelemente entsprechend weitgehend dem, was bereits im Kapitel Fensterklassen besprochen wurde.
Es gibt zwei gleichwertige Methoden, um ein Steuerelement als Ressource zu definieren: als explizite Anweisung oder mit der allgemeineren CONTROL- Anweisung, die das Steuerelement als Parameter enthält.
Steuerelement [Beschriftung,] [ID,] x ,y, dx, dy, [Stil], [erw. Stil], [Hilfe-ID]
[BEGIN
Datenelement1
Datenelement2
...
END]
CHECKBOX "Alles entfernen", IDC_REMOVEALL, 10, 5, 130, 10, WS_TABSTOP
|
CONTROL [Beschriftung,] ID, Fensterklasse, Stil, x ,y, dx, dy
CONTROL "Alles entfernen", IDC_REMOVEALL, "button", WS_TABSTOP|BS_CHECKBOX, 10, 5, 130, 10
|
Im obigen Beispiel wird ein Kontrollkästchen mit der Beschriftung "Alles entfernen" definiert. Die ID dieses Steuerelements lautet IDC_REMOVEALL, eine selbstdefinierte Konstante. Bei einigen Steuerelementen (z. B. LISTBOX) kann noch eine Initialisierung vorgenommen werden.
X, y, dx und dy stehen für die horizontale und vertikale Position der
linken oberen Ecke relativ zum Elternobjekt sowie die horizontale und vertikale
Ausdehnung. Positions- und Größenangaben in Ressourcedateien erfolgen
in sog. Dialogfeldeinheiten, nicht in Pixeln. Wie groß eine
Dialogfeldeinheit ist hängt von der zugrundeliegenden Schriftart ab. Bei X-
Koordinaten kommt die Formel
Pixel = Wert * Durchschnittsbreite eines Buchstaben / 4
zum Einsatz und bei Y-Koordinaten die Formel
Pixel = Wert * Schrifthöhe / 8
Dieser Weg wurde gewählt, um von der Bildschirmauflösung unabhängiger
zu sein. Die unterschiedlichen Teiler ergeben sich daraus, dass Schriften meist
halb so breit wie hoch sind.
Als groben Richtwert kann man davon ausgehen, dass eine Dialogfeldeinheit etwa 2
Pixeln entspricht.
MYDIALOG DIALOGEX 40, 40, 160, 150 STYLE WS_POPUP|DS_MODALFRAME|WS_VISIBLE|WS_CAPTION|WS_SYSMENU CAPTION "Attribute ändern" FONT 8, "MS Sans Serif" BEGIN Steuerelementdefinitionen END |
MYDIALOG ist der Name des Dialogs, über den er innerhalb des Programms
geladen wird.
Folgende Angaben können in Dialogfeldressourcedateien auftauchen.
| Ressourcebezeichner | Bedeutung | |
|---|---|---|
| Dialogboxbasisdefinition | ||
| DIALOGEX | Ersetzt den früheren Ressourcetyp DIALOG. Alle innerhalb von
DIALOGEX definierten Steuerelemente haben eine erweiterte SyntaxName DIALOGEX x, y, dx, dy [,Hilfe-ID] [Optionen]Die Angaben x und y liegen relativ zu dem Fenster, das den Dialog aufgerufen hat. Zu den Optionen zählen STYLE, EXSTYLE, CAPTION, CHARACTERISTICS, FONT, CLASS, MENU, VERSION und LANGUAGE. Einige Optionen werden in den nächsten Zeilen genauer beschrieben. | |
| STYLE | Es kommen alle Stile in Frage, die mit WS_ oder DS_ beginnen. Es sollte WS_VISIBLE verwendet werden, sonst muss der Dialog nach seinem Start noch mittels ShowWindow() sichtbar gemacht werden | |
| CAPTION | Wenn der Stil WS_CAPTION verwendet wird, dann wird durch diese Option
der Dialogfeldtitel festgelegt. Der Text sollte in Anführungszeichen
stehen.CAPTION Text
|
|
| FONT | Durch den Font wird sowohl die Standardschriftart für den Dialog
als auch indirekt die Größe der Steuerelemente.FONT Schriftröße, Schriftart [,Gewicht] [,Kursiv] [,Zeichensatz]FONT 10 "SYMBOL", FW_BOLD, FALSE, SYMBOL_CHARSET |
|
| Steuerelementdefinition | ||
| AUTO3STATE | Ein Kontrollkästchen mit den drei Zuständen markiert, nicht
markiert und unbestimmt. Das Steuerelement passt sein Aussehen selbst
seinem Zustand an.AUTO3STATE Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen von WS_TABSTOP, WS_DISABLED, WS_GROUP in Frage. |
|
| AUTOCHECKBOX | wie AUTO3STATE, aber ohne den Zustand unbestimmtAUTOCHECKBOX Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]
|
|
| AUTORADIOBUTTON | Radiobuttons werden meist in Gruppen sich gegenseitig ausschließender
Optionen eingesetzt. Das Aussehen wird automatisch dem Zustand angepasst.AUTORADIOBUTTON Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen von WS_TABSTOP, WS_DISABLED, WS_GROUP in Frage. |
|
| CHECKBOX | wie AUTOCHECKBOX, allerdings wird das Aussehen nicht automatisch
angepasst.CHECKBOX Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]
|
|
| COMBOBOX | Definiert ein Kombinationsfeld, das typischerweise aus einem Eingabefeld
sowie einem Listenfeld besteht.COMBOBOX ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen der mit CBS_ beginnenden Stile mit WS_TABSTOP, WS_DISABLED, WS_GROUP und WS_VSCROLL in Frage. |
|
| CTEXT | Definiert statischen Text, der innerhalb des durch die Positionsangaben
festgelegten Rechtecks zentriert dargestellt wird. Die ID ist i. d. R. -1,
da statischer Text nicht auf Ereignisse reagiert. Wird nicht -1 verwendet,
dann kann der Text zur Laufzeit verändert werden.CTEXT Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen der mit SS_ beginnenden Stile mit WS_TABSTOP und WS_GROUP in Frage. |
|
| DEFPUSHBUTTON | Definiert die Standardschaltfläche für den Dialog. Diese wird
automatisch aktiviert, wenn die ENTER-Taste gedrückt wird. Zur
Kenntlichmachung wird sie mit einem etwas dickeren Rahmen versehen. Es kann
nur einen DEFPUSHBUTTON je Dialog geben, alle anderen Schaltflächen
müssen normale PUSHBUTTONs sein.DEFPUSHBUTTON Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen der mit BS_ beginnenden Stile mit WS_TABSTOP, WS_DISABLED und WS_GROUP in Frage. |
|
| EDITTEXT | Definiert ein Eingabefeld. Dieses kommt von Haus aus mit den
üblichen Bearbeitungsfunktionen (Einfügen, Löchen, Markieren)
klar und bietet Befehle und ein Kontextmenü für die Arbeit mit der
Zwischenablage an. Die Möglichkeit für mehrzeiligen Text und
Scrolling in vertikaler und horizontaler Richtung ist eingebaut.EDITTEXT ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen der mit ES_ beginnenden Stile mit WS_TABSTOP, WS_DISABLED, WS_HSCROLL, WS_BORDER und WS_GROUP in Frage. |
|
| GROUPBOX | Definiert ein statisches Element, das zum Zeichnen eines Rahmens mit
einem Titel in der oberen Rahmenzeile verwendet wird. Durch diesen Rahmen
werden zusammengehörende Steuerelemente auch optisch zusammengefasst.
Es hat die ID -1, da das Steuerelement nicht auf Ereignisse reagiert.GROUPBOX Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen von WS_TABSTOP, WS_DISABLED und WS_GROUP in Frage. |
|
| ICON | Dient der Anzeige eines Symbols in einem Dialog. Dieses Symbol muss an
anderer Stelle in der Ressourcedatei mit der ICON-Anweisung definiert worden
sein.ICON Symbolname, ID, x, y, dx, dy [,Stil [, erw. Stil]]Der Symbolname ist der Verweis auf die eigentliche ICON-Definition. Als Stil kommt nur SS_ICON in Frage. |
|
| LISTBOX | Definiert eine Liste, aus der der Benutzer je nach Stil ein oder mehrere
Element/e auswählen kann.
LISTBOX ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen der mit LBS_ beginnenden Stile mit WS_TABSTOP, WS_DISABLED, WS_VSCROLL und WS_BORDER in Frage. |
|
| LTEXT | wie CTEXT, nur das der Text links ausgerichtet ist | |
| PUSHBOX | wie PUSHBUTTON allerdings ohne Rahmen. Bei mir erschien der Rahmen sobald das Steuerelement den Fokus hatte, verschwand aber nicht, wenn es den Fokus verloren hatte. | |
| PUSHBUTTON | wie DEFPUSHBUTTON, nur das die Schaltfläche explizit mit Maus oder Tastatur betätigt werden muss; der Rahmen des Steuerelements ist dünner. | |
| RADIOBUTTON | Wie AUTORADIOBUTTON, aber ohne die automatische Anpassung des Aussehens.RADIOBUTTON Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]]
|
|
| RTEXT | wie CTEXT, nur das der Text rechts ausgerichtet ist | |
| SCROLLBAR | Definiert Bildlaufleisten. Diese werden in Dialogen weniger zum Scrollen
des gesamten Dialogbereichs verwendet, sondern um eine Art Schieberegler
darzustellen, bspw. für eine Skalierung.SCROLLBAR ID, x, y, dx, dy [,Stil [, erw. Stil]]Als Stile kommen Kombinationen der mit SBS_ beginnenden Stile mit WS_TABSTOP, WS_DISABLED und WS_GROUP in Frage. Meist werden die Stile SBS_VERT und SBS_HORZ für vertikale und horizontale Bildlaufleisten verwendet. |
|
| STATE3 | wie AUTO3STATE, aber ohne die automatische Anpassung des Aussehens.
STATE3 Bezeichnung, ID, x, y, dx, dy [,Stil [, erw. Stil]] |
Im folgenden Beispiel wird der Dialog aus dem Hauptfenster heraus gestartet. Dazu wird ein Menüpunkt verwendet. Im Hauptfenster wird in einer Liste der Inhalt des aktuellen Verzeichnisses angezeigt (nur Dateinamen). Durch den Dialog werden zum einen die aktuellen Attribute des Verzeichniseintrags angezeigt als auch zur Änderung angeboten. Es soll ebenfalls möglich sein, den Zeitstempel des Verzeichniseintrags zu verändern. Der Dialog ist in weiten Teilen von einem sehr ähnlichen Dialog des Total Commanders inspiriert.
Zur Erzeugung der Listbox wird der bereits aus dem Beispiel Fensterklassen bekannte Code verwendet. Die Listbox wird mit dem Standard-ANSI-Font fü variable Zeichenbreite dargestellt. Die Listbox verfügt nicht über die Möglichkeit der Mehrfachauswahl.
...
...
WndProc PROC hWin:dword,uMsg:dword,wParam:dword,lParam:dword
LOCAL point:POINT, hFont:HFONT
.IF uMsg == WM_CREATE
;eine Listbox erzeugen
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR cListClass,NULL,\
WS_CHILD OR WS_VISIBLE OR LBS_STANDARD,\
40,30,200,270,hWin,10,hInstance,NULL
mov hList,eax
;die Listbox mit dem aktuellen Verzeichnisinhalt füllen
invoke SendMessage,eax,LB_DIR,DDL_READWRITE OR DDL_HIDDEN \
OR DDL_DIRECTORY OR 4000h,ADDR cFilter
invoke GetStockObject,ANSI_VAR_FONT ;Font aus dem Systemvorrat holen
invoke GetObject,eax,SIZEOF LOGFONT,ADDR logfont ;Font-Infos nach logfont schreiben
invoke CreateFontIndirect,ADDR logfont ;daraus eine log. Schrift erzeugen
invoke SendMessage,hList,WM_SETFONT,eax,0
...
...
|
Die Ermittlung des ausgewählten Listenelements wird als Reaktion auf die
Wahl des entsprechenden Menüpunktes eingebaut. Diese Wahl verursacht wie
immer ein Ereignis des Typs WM_COMMAND, wobei im unteren Word von wParam die
ID des entsprechenden Ereignisverursachers (des Menüpunkts) steht.
Indem mittels SendMessage() eine Nachricht LB_GETCURSEL an die Listbox geschickt
wird, kann der null-basierte Index des markierten Elements ermittelt werden, der
von SendMessage() in EAX geliefert wird. Falls in EAX LB_ERR stehen sollte, dann
wurde kein Element markiert oder ein sonstiger Fehler ist aufgetreten. In diesem
Fall kann die Bearbeitung des Ereignisses abgebrochen werden.
Nachdem der Index ermittelt wurde, muss die Zeichenkette des markierten Listeneintrags ermittelt werden. Dazu wird per SendMessage() die Nachricht LB_GETTEXT an die Listbox geschickt. Der Parameter wParam muss dabei den ermittelten null-basierten Index enthalten, während in lParam die Adresse eines Puffers steht, in den die Zeichenkette kopiert werden soll.
Im Anschluss werden mittels der Funktion GetFileAttributesEx() die Attribute sowie einige Zeitstempel ermittelt und in einer Variablen des Struktur-Typs WIN32_FILE_ATTRIBUTE_DATA abgelegt.
Anschließend erfolgt der Start des Dialogs. Dazu wird erst die Adresse der Dialogprozedur in EAX abgelegt, dann wird DialogBoxParam() mit dem Instanzhandle des dialogstartenden Programms, dem Fensterklassennamen, dem Handle des aufrufenden Fensters und der Adresse der Dialogprozedur als Parameter gestartet. Als 5. Parameter kann ein beliebiger 32-Bit-Wert übergeben werden. Dabei wird es sich in vielen Fällen um die Adresse einer Struktur zur Aufnahme weiterer Parameter handeln. Der Inhalt dieser Struktur wird dann durch den Dialog ausgewertet und ggf. verändert.
Interessanterweise ist in den mir vorliegenden Dokumentationen oft von der Funktion DialogBox() die Rede. Es handelt sich hierbei um ein C-Makro mit vier Parametern, das intern die Funktion DialogBoxParam() verwendet. Dabei stimmen die vier Parameter mit den ersten vier Parametern von DialogBoxParam() überein.
Die Listbox liefert auch eine Reaktion auf einen Doppelklick. Das Ereignis ist wieder WM_COMMAND. Im LoWord von wParam steckt die ID des Steuerelements, im HiWord steht die eigentliche Nachricht. Uns interessiert der Wert LBN_DBLCLK. Der Einfachheit halber prüfe ich nur auf diese Tatsache und springe bei Übereinstimmung in die Ereignisbehandlung von IDM_ATTRIBUTE.
...
...
.ELSEIF uMsg == WM_COMMAND
mov eax,wParam
.IF ax==IDM_EXIT
invoke DestroyWindow,hWnd
.ELSEIF 10 ;ID der Listbox
shr eax,16 ;Highword nach AX
cmp ax,LBN_DBLCLK ;Doppelklick?
je @@setattr
.ELSEIF ax==IDM_ATTRIBUTE
@@setattr:
invoke SendMessage,hList,LB_GETCURSEL,0,0 ;akt. sel. Element ermitteln
cmp eax,LB_ERR ;wenn keines markiert oder
je @@procend ;bei sonstigen Fehlern -> Ende
;ansonsten steht in EAX der null-basierte Index des markierten Elements
invoke SendMessage,hList,LB_GETTEXT,eax,ADDR cBuffer ;Element in Puffer schreiben
;Attribute und Zugriffszeiten in Struktur win32fileattr schreiben
invoke GetFileAttributesEx,ADDR cBuffer,0,ADDR win32fileattr
mov eax,OFFSET AttrDlgProc ;Adresse der Dialogproc nach EAX
invoke DialogBoxParam,hInstance,ADDR szClassName,hWnd,eax,NULL ;Dialogbox starten
.ENDIF
.ELSE
...
...
|
Die Dialogprozedur hat starke Ähnlichkeit mit der Windowprozedur, zumal sie praktisch die gleichen Parameter definiert. Allerdings ist hier der erste Parameter ein Handle auf den Dialog.
Die erste Nachricht, die ein Dialog erhält, lautet WM_INITDIALOG. In Reaktion auf diese Nachricht werden i. d. R. die Steuerelemente initialisiert. Kehrt die Dialogprozedur aus dieser Nachricht mit TRUE zurüch, dann setzt Windows den Eingabefokus auf das erste Element, das den Fensterstil WS_TABSTOP hat. Anderenfalls wird der Eingabefokus unverändert gelassen bzw. kann manuell per SetFocus() eingestellt werden.
Überhaupt wird der Return-Wert der Dialogprozedur als boolscher Wert interpretiert, bei der Windowprozedur als 32-Bit-Ganzzahl. Windowprozeduren überlassen nicht bearbeitete Nachrichten der Funktion DefWindowProc(), Dialogprozeduren liefern bei bearbeiteten Ereignissen TRUE, ansonsten FALSE zurück.
Der Rest der Dialoginitialisierung befasst sich mit der Ermittlung der Dateiattribute sowie des Zeitpunkts der letzten Änderung. Beides ist in der Struktur WIN32_FILE_ATTRIBUTE_DATA zu finden.
Während die Attribute noch relativ leicht zu ermitteln sind
(WIN32_FILE_ATTRIBUTE_DATA.dwFileAttributes auf bestimmte Flags testen und
mittels CheckDlgButton() das zugehörige Kontrollkästchen setzen), ist
die Emittlung des letzten Bearbeitungszeitpunktes schon beinahe lächerlich
umständlich.
Zuerst muss die Dateizeit in die Systemzeit umgewandelt werden. Diese Zeit ist
UTC-basiert. Verwendet wird dafür die Funktion FileTimeToSystemTime(), die
ihr Ergebnis in einer Struktur des Typs SYSTEMTIME ablegt. Diese UTC-Zeit muss
dann mittels SystemTimeToTzSpecificLocalTime() in eine "richtige" Zeit
umgewandelt werden, die auch die lokale Zeitzone berücksichtigt. Das
Ergebnis landet wieder in einer SYSTEMTIME-Struktur. Die einzelnen Werte
für Datum (Tag, Monat, Jahr) und Zeit (Stunde, Minute, Sekunde) sind als
16-Bit-Werte gespeichert. Diese werden auf 32 Bit erweitert und mittels der
Funktion wsprintf() in eine Zeichenkette konvertiert, die mit SetDlgItemText()
in das jeweilige Eingabefeld geschrieben wird.
Die eben beschriebene Konvertierung der UTC-Zeit in die lokale Zeit und ihre Darstellung im Dialog wurde in eine eigene Funktion (getDateTime()) ausgelagert. Der Dialog bietet die Möglichkeit, auch die aktuelle lokale Zeit als Zeitstempel für die Datei einzustellen. Die entsprechende Ereignisbehandlung für diesen Button ruft dafür die Funktion GetSystemTime() auf, die ihr Ergebnis wieder als UTC-Zeit in einer SYSTEMTIME- Struktur ablegt. Die Darstellung in der lokalen Zeit wird erneut durch die selbstgeschriebene Funktion getDateTime() realisiert.
...
...
.IF ax==IDC_BTNCURRENT
invoke GetSystemTime,ADDR stUTC
invoke getDateTime,ADDR stUTC,hDlg
mov eax,TRUE
ret
.ENDIF
...
...
|
Wird der Dialog mit OK beendet, dann müssen die Zustände der Kontrollkästchen ermittelt sowie die Zeichenketten für Datum und Zeit in ihre numerischen Werte konvertiert werden.
Die Kontrollkästchen können die Zustände Ja, Nein und Unbestimmt annehmen, wobei Unbestimmt bewirkt, dass das entsprechende Attribut nicht verändert wird. Aus diesem Grund fällt die Auswertung der Einstellungen etwas umfangreicher aus.
...
...
.IF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDC_BTNOK
;Test, ob Attribute angeklickt ist
invoke IsDlgButtonChecked,hDlg,IDC_ARCHIVE
cmp eax,BST_UNCHECKED
jne @@test_ARCH_CHECKED
and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_ARCHIVE
@@test_ARCH_CHECKED:
cmp eax,BST_CHECKED
jne @@test_RO_UNCHECKED
or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_ARCHIVE
@@test_RO_UNCHECKED:
...
...
|
Die Änderung der Attribute wird dann durch die Funktion SetFileAttributes()
erledigt.
Schwieriger ist da schon die Umsetzung der Datums/Zeit-Zeichenketten. Diese
müssen in eine FILETIME-Struktur konvertiert werden. Dazu muss allerdings
erst eine SYSTEMTIME-Struktur gefüllt werden, die mit SystemTimeToFileTime()
in eine lokale FILETIME und anschließend mit LocalFileTimeToFileTime() in eine
UTC-FILETIME konvertiert wird. Erst mit dieser Zeit kann mit SetFileTime() der
Zeitstempel der Datei manipuliert werden. SetFileTime() erwartet den Handle
einer Datei, die zumindest mit dem Zugriffs-Flag FILE_WRITE_ATTRIBUTES
geöffnet wurde. Dieses Flag ist im Wert GENERIC_WRITE enthalten.
Die Schwierigkeit hierbei ist der menschliche Faktor. Das Abfangen von
Fehleingaben ist schon in einer Hochsprache ein umfangreiches Unterfangen, in
Assembler wird es in der Regel noch schwieriger, wenn die Funktionen zur
Textauswertung und -manipulation selbst geschrieben werden müssen.
Die von mir geschriebene Routine convertDateTime() geht einfach davon aus, dass
die Eingaben korrekt in den Formaten TT.MM.JJJJ und HH:MM:SS vorliegen. Das
Trennzeichen ist dabei jeweils egal, wichtig ist die Stelligkeit.
convertDateTime() stützt sich zu einem großen Teil auf den Befehl
aad, der eigentlich vorgesehen ist, gepackte BCDs für die
Division vorzubereiten. Wird bspw. die Zeichenkette '29' ins Register AX
geladen, dann enthält AL den Wert 32h und AH den Wert 39h, wegen der
Intel-Byteanordnung steht in AX 3932h. Um aus diesen ASCII-Zahlen eine
'richtige' Zahl machen, ist von AL und AH jeweils 30h abzuziehen:
sub ax,3030h erledigt das - in AX steht jetzt 0902h.
aad wirkt nun so, das AH mit 10 multipliziert, dann zu AL addiert
und danach auf Null gesetzt wird. Zum jetzigen Zeitpunkt ergibt sich dabei der
Wert 92, deswegen muss vor aad die Bytereihenfolge in AX getauscht
werden.
Für das Jahr erfolgt die Konvertierung zweistufig: erst die ersten beiden
Ziffern, die nach der Multiplikation mit 100 das Jahrhundert ergeben, dann die
letzten beiden Ziffern, deren Wert zum Jahrhundert dazu addiert wird.
... ... convertDateTime PROC pDateString:dword, pTimeString:dword ;bedient sich des ASCII-Adjust-Befehls aad, um eine gepackte ;BCD in eine Binaerzahl zu konvertieren. Dazu muss aber der ;Inhalt von AX umgedreht werden (xchg) push ebx push ecx mov ebx,pDateString mov ax,[ebx] xchg al,ah sub ax,3030h aad mov stLocal.wDay,ax ... ... |
Hier das gesamte Programm
;dlgfattr.asm - Dateiattribute ändern .686 .MODEL flat,stdcall OPTION casemap:none include c:\masm32\include\windows.inc include c:\masm32\include\user32.inc include c:\masm32\include\kernel32.inc include c:\masm32\include\gdi32.inc includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib WndProc PROTO :dword, :dword, :dword, :dword getDateTime PROTO :dword, :dword convertDateTime PROTO :dword, :dword .CONST IDM_NEW EQU 40001 IDM_OPEN EQU 40002 IDM_SAVE EQU 40003 IDM_SAVEAS EQU 40004 IDM_EXIT EQU 40005 IDM_CUT EQU 40011 IDM_COPY EQU 40012 IDM_INSERT EQU 40013 IDM_UNDO EQU 40014 IDM_ATTRIBUTE EQU 40015 IDC_ARCHIVE EQU 10 IDC_READONLY EQU 11 IDC_HIDDEN EQU 12 IDC_SYSTEM EQU 13 IDC_DATETIME EQU 15 IDC_BTNCURRENT EQU 16 IDC_DATE EQU 18 IDC_TIME EQU 20 IDC_BTNOK EQU 25 .DATA szCaption db "Dialoge",0 szClassName db "AttrDialog",0 cMenuName db "WinMenu",0 cMenuFile db "&Datei",0 ;Hauptmenüpunkt cMenuNew db "&Neu" ,0 cMenuOpen db "Ö&ffnen...",0 cMenuSave db "&Speichern",0 cMenuSaveAs db "Speichern &unter...",0 cMenuAttribute db "&Attribute ändern",0 cMenuExit db "&Beenden",0 cFilter db "*.*",0 cListClass db "listbox",0 cBuffer db 80 dup (0) cDateString db 13 dup (0) cTimeString db 9 dup (0) cDateFormat db "%02d.%02d.%04d",0 cTimeFormat db "%02d:%02d:%02d",0 .DATA? ;uninitialisierte Daten hInstance HINSTANCE ? hIcon HANDLE ? hCursor HANDLE ? hWnd HWND ? cCmdline LPSTR ? hMenuBar HANDLE ? hMenu HANDLE ? hList HANDLE ? logfont LOGFONT <?> win32fileattr WIN32_FILE_ATTRIBUTE_DATA <?> ftUTC FILETIME <?> ftLocal FILETIME <?> stUTC SYSTEMTIME <?> stLocal SYSTEMTIME <?> .CODE start: call mymain invoke ExitProcess,0 mymain PROC LOCAL wwd:dword,wht:dword,wtx:dword,wty:dword,wc:WNDCLASSEX invoke GetModuleHandle,NULL mov hInstance,eax invoke GetCommandLine mov cCmdline,eax mov wtx,CW_USEDEFAULT ;oder konkrete Angaben: 0 mov wty,CW_USEDEFAULT ;0 mov wwd,CW_USEDEFAULT ;400 mov wht,CW_USEDEFAULT ;400 mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style,CS_BYTEALIGNCLIENT or CS_BYTEALIGNWINDOW mov wc.lpfnWndProc,OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInstance pop wc.hInstance mov wc.hbrBackground,COLOR_BTNFACE+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET szClassName invoke LoadIcon,NULL,IDI_APPLICATION mov hIcon,eax mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov hCursor,eax mov wc.hCursor,eax invoke RegisterClassEx,ADDR wc invoke CreateWindowEx,WS_EX_LEFT,ADDR szClassName,ADDR szCaption, WS_OVERLAPPEDWINDOW,wtx,wty,wwd,wht,NULL,NULL,hInstance,NULL mov hWnd,eax call erzeugeMenu mov hMenuBar,eax invoke SetMenu,hWnd,eax invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke UpdateWindow,hWnd call MsgLoop ret mymain ENDP MsgLoop PROC LOCAL msg:MSG Startloop: invoke GetMessage,ADDR msg,NULL,0,0 or eax,eax je Exitloop invoke TranslateMessage, ADDR msg invoke DispatchMessage,ADDR msg jmp Startloop Exitloop: mov eax,msg.wParam ret MsgLoop ENDP WndProc PROC hWin:dword,uMsg:dword,wParam:dword,lParam:dword LOCAL point:POINT, hFont:HFONT .IF uMsg == WM_CREATE ;eine Listbox erzeugen invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR cListClass,NULL,\ WS_CHILD OR WS_VISIBLE OR LBS_STANDARD,\ 40,30,200,270,hWin,10,hInstance,NULL mov hList,eax ;die Listbox mit dem aktuellen Verzeichnisinhalt füllen invoke SendMessage,eax,LB_DIR,DDL_READWRITE OR DDL_HIDDEN \ OR DDL_DIRECTORY OR 4000h,ADDR cFilter invoke GetStockObject,ANSI_VAR_FONT ;Font aus dem Systemvorrat holen invoke GetObject,eax,SIZEOF LOGFONT,ADDR logfont ;Font-Infos nach logfont schreiben invoke CreateFontIndirect,ADDR logfont ;daraus eine log. Schrift erzeugen invoke SendMessage,hList,WM_SETFONT,eax,0 .ELSEIF uMsg == WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg == WM_COMMAND mov eax,wParam .IF ax==IDM_EXIT invoke DestroyWindow,hWnd .ELSEIF ax==10 ;ID der Listbox shr eax,16 ;Highword nach AX cmp ax,LBN_DBLCLK ;Doppelklick? jne @@nodblclk invoke SendMessage,hWin,WM_COMMAND,IDM_ATTRIBUTE,NULL @@nodblclk: .ELSEIF ax==IDM_ATTRIBUTE @@setattr: invoke SendMessage,hList,LB_GETCURSEL,0,0 ;akt. sel. Element ermitteln cmp eax,LB_ERR ;wenn keines markiert oder je @@procend ;bei sonstigen Fehlern -> Ende ;ansonsten steht in EAX der null-basierte Index des markierten Elements invoke SendMessage,hList,LB_GETTEXT,eax,ADDR cBuffer ;Element in Puffer schreiben ;Attribute und Zugriffszeiten in Struktur win32fileattr schreiben invoke GetFileAttributesEx,ADDR cBuffer,0,ADDR win32fileattr mov eax,OFFSET AttrDlgProc ;Adresse der Dialogproc nach EAX invoke DialogBoxParam,hInstance,ADDR szClassName,hWnd,eax,NULL ;Dialogbox starten .ENDIF .ELSE invoke DefWindowProc,hWin,uMsg,wParam,lParam ret .ENDIF @@procend: xor eax,eax ret WndProc ENDP AttrDlgProc PROC hDlg:dword,uMsg:dword,wParam:dword,lParam:dword .IF uMsg==WM_INITDIALOG ;ab hier Test der Attribute test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_ARCHIVE jz @@test_RO invoke CheckDlgButton,hDlg,IDC_ARCHIVE,BST_CHECKED @@test_RO: test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_READONLY jz @@test_HID invoke CheckDlgButton,hDlg,IDC_READONLY,BST_CHECKED @@test_HID: test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_HIDDEN jz @@test_SYS invoke CheckDlgButton,hDlg,IDC_HIDDEN,BST_CHECKED @@test_SYS: test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_SYSTEM jz @@test_END invoke CheckDlgButton,hDlg,IDC_SYSTEM,BST_CHECKED @@test_END: ;Dateizeit in Systemzeit (UTC) umwandeln invoke FileTimeToSystemTime,ADDR win32fileattr.ftLastWriteTime,ADDR stUTC invoke getDateTime,ADDR stUTC,hDlg invoke GetDlgItem,hDlg,IDC_DATE invoke EnableWindow,eax,FALSE invoke GetDlgItem,hDlg,IDC_TIME invoke EnableWindow,eax,FALSE mov eax,TRUE ret .ENDIF .IF uMsg==WM_COMMAND mov eax,wParam .IF ax==IDC_DATETIME invoke IsDlgButtonChecked,hDlg,IDC_DATETIME cmp eax,BST_UNCHECKED jne @@dtchecked invoke GetDlgItem,hDlg,IDC_DATE invoke EnableWindow,eax,FALSE invoke GetDlgItem,hDlg,IDC_TIME invoke EnableWindow,eax,FALSE jmp short @@dtende @@dtchecked: invoke GetDlgItem,hDlg,IDC_DATE invoke EnableWindow,eax,TRUE invoke GetDlgItem,hDlg,IDC_TIME invoke EnableWindow,eax,TRUE @@dtende: .ELSEIF ax==IDC_BTNOK ;Test, ob Attribute angeklickt ist invoke IsDlgButtonChecked,hDlg,IDC_ARCHIVE cmp eax,BST_UNCHECKED jne @@test_ARCH_CHECKED and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_ARCHIVE @@test_ARCH_CHECKED: cmp eax,BST_CHECKED jne @@test_RO_UNCHECKED or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_ARCHIVE @@test_RO_UNCHECKED: invoke IsDlgButtonChecked,hDlg,IDC_READONLY cmp eax,BST_UNCHECKED jne @@test_RO_CHECKED and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_READONLY @@test_RO_CHECKED: cmp eax,BST_CHECKED jne @@test_HID_UNCHECKED or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_READONLY @@test_HID_UNCHECKED: invoke IsDlgButtonChecked,hDlg,IDC_HIDDEN cmp eax,BST_UNCHECKED jne @@test_HID_CHECKED and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_HIDDEN @@test_HID_CHECKED: cmp eax,BST_CHECKED jne @@test_SYS_UNCHECKED or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_HIDDEN @@test_SYS_UNCHECKED: invoke IsDlgButtonChecked,hDlg,IDC_SYSTEM cmp eax,BST_UNCHECKED jne @@test_SYS_CHECKED and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_SYSTEM @@test_SYS_CHECKED: cmp eax,BST_CHECKED jne @@setAttr or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_SYSTEM @@setAttr: invoke SetFileAttributes,ADDR cBuffer,win32fileattr.dwFileAttributes ;Test, ob Zeitstempel geaendert werden soll invoke IsDlgButtonChecked,hDlg,IDC_DATETIME cmp eax,BST_UNCHECKED je @@DialogFertig ;Zeichenketten Datum und Uhrzeit holen invoke GetDlgItemText,hDlg,IDC_DATE,ADDR cDateString,13 invoke GetDlgItemText,hDlg,IDC_TIME,ADDR cTimeString,9 ;Zeichenketten in FILETIME-Struktur eintragen invoke convertDateTime,ADDR cDateString,ADDR cTimeString ;Datum und Uhrzeit der Datei setzen call setDateTime @@DialogFertig: invoke EndDialog,hDlg,TRUE ;Dialog mit TRUE beenden .ENDIF .IF ax==IDC_BTNCURRENT invoke GetSystemTime,ADDR stUTC invoke getDateTime,ADDR stUTC,hDlg invoke GetDlgItem,hDlg,IDC_DATE invoke EnableWindow,eax,TRUE invoke GetDlgItem,hDlg,IDC_TIME invoke EnableWindow,eax,TRUE mov eax,TRUE ret .ENDIF .IF ax==IDCANCEL invoke EndDialog,hDlg,FALSE ;Dialog mit FALSE beenden .ENDIF .ENDIF mov eax,FALSE ret AttrDlgProc ENDP erzeugeMenu PROC invoke CreateMenu mov hMenuBar,eax invoke CreateMenu mov hMenu,eax invoke AppendMenu,hMenu,MF_STRING,IDM_NEW,ADDR cMenuNew invoke AppendMenu,hMenu,MF_STRING,IDM_OPEN,ADDR cMenuOpen invoke AppendMenu,hMenu,MF_STRING,IDM_SAVE,ADDR cMenuSave invoke AppendMenu,hMenu,MF_STRING,IDM_SAVE,ADDR cMenuSaveAs invoke AppendMenu,hMenu,MF_SEPARATOR,0,NULL invoke AppendMenu,hMenu,MF_STRING,IDM_ATTRIBUTE,ADDR cMenuAttribute invoke AppendMenu,hMenu,MF_SEPARATOR,0,NULL invoke AppendMenu,hMenu,MF_STRING,IDM_EXIT,ADDR cMenuExit invoke AppendMenu,hMenuBar,MF_POPUP,hMenu,ADDR cMenuFile mov eax,hMenuBar ret erzeugeMenu ENDP getDateTime PROC pSystemTime:dword, hDlg:dword ;ermittelt aus der UTC-Systemzeit eine TZ-basierte Zeit und füllt den Dialog push ebx ;Register sichern push ecx push edx ;Systemzeit in lokale Zeit (mit Zeitzone) umwandeln invoke SystemTimeToTzSpecificLocalTime,NULL,pSystemTime,ADDR stLocal ;Tag, Monat, Jahr auf 32 Bit umsetzen movzx ebx,stLocal.wDay movzx ecx,stLocal.wMonth movzx edx,stLocal.wYear ;anhand des Datumformates in einen Puffer schreiben invoke wsprintf,ADDR cDateString,ADDR cDateFormat,ebx,ecx,edx ;und ins Dialogelement schreiben invoke SetDlgItemText,hDlg,IDC_DATE,ADDR cDateString ;Stunde, Minute, Sekunde auf 32 Bit umsetzen movzx ebx,stLocal.wHour movzx ecx,stLocal.wMinute movzx edx,stLocal.wSecond invoke wsprintf,ADDR cTimeString,ADDR cTimeFormat,ebx,ecx,edx invoke SetDlgItemText,hDlg,IDC_TIME,ADDR cTimeString ;gesicherte Register wieder vom Stack holen pop edx pop ecx pop ebx ret getDateTime ENDP convertDateTime PROC pDateString:dword, pTimeString:dword ;bedient sich des ASCII-Adjust-Befehls aad, um eine gepackte ;BCD in eine Binaerzahl zu konvertieren. Dazu muss aber der ;Inhalt von AX umgedreht werden (xchg) push ebx push ecx mov ebx,pDateString mov ax,[ebx] xchg al,ah sub ax,3030h aad mov stLocal.wDay,ax mov ax,[ebx+3] xchg al,ah sub ax,3030h aad mov stLocal.wMonth,ax mov ax,[ebx+6] xchg al,ah sub ax,3030h aad mov cl,100 mul cl mov stLocal.wYear,ax mov ax,[ebx+8] xchg al,ah sub ax,3030h aad add stLocal.wYear,ax mov ebx,pTimeString mov ax,[ebx] xchg al,ah sub ax,3030h aad mov stLocal.wHour,ax mov ax,[ebx+3] xchg al,ah sub ax,3030h aad mov stLocal.wMinute,ax mov ax,[ebx+6] xchg al,ah sub ax,3030h aad mov stLocal.wSecond,ax invoke SystemTimeToFileTime,ADDR stLocal,ADDR ftLocal invoke LocalFileTimeToFileTime,ADDR ftLocal,ADDR ftUTC invoke GetLastError pop ecx pop ebx ret convertDateTime ENDP setDateTime PROC LOCAL hFile:HANDLE invoke CreateFile,ADDR cBuffer,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL cmp eax,INVALID_HANDLE_VALUE mov hFile,eax je @@procend invoke SetFileTime,eax,NULL,NULL,ADDR ftUTC invoke GetLastError invoke CloseHandle,hFile @@procend: ret setDateTime ENDP END start |
#include "\masm32\include\resource.h" #define IDC_SUBDIR 9 #define IDC_ARCHIVE 10 #define IDC_READONLY 11 #define IDC_HIDDEN 12 #define IDC_SYSTEM 13 #define IDC_DATETIME 15 #define IDC_BTNCURRENT 16 #define IDC_DATE 18 #define IDC_TIME 20 #define IDC_BTNOK 25 #define IDC_BTNHELP 26 ATTRDIALOG DIALOGEX 40, 40, 160, 150 STYLE WS_POPUP|DS_MODALFRAME|WS_VISIBLE|WS_CAPTION|WS_SYSMENU CAPTION "Attribute ändern" FONT 8, "MS Sans Serif" BEGIN GROUPBOX "Attribute ändern",-1,5,17,150,70,WS_GROUP AUTO3STATE "&a Archiv-Bit", IDC_ARCHIVE,10,27,70,10,WS_GROUP|WS_TABSTOP AUTO3STATE "&r Nur Lesen-Bit", IDC_READONLY,10,39,70,10 AUTO3STATE "&h Versteckt-Bit", IDC_HIDDEN,10,51,70,10 AUTO3STATE "&s System-Bit", IDC_SYSTEM,10,63,70,10 GROUPBOX "",-1,5,93,150,37,WS_GROUP AUTOCHECKBOX "&Datum/Zeit ändern:",IDC_DATETIME,10,100,80,10,WS_GROUP|WS_TABSTOP PUSHBUTTON "A&ktuelles",IDC_BTNCURRENT,110,100,40,12 LTEXT "Datu&m:",-1,10,117,22,10 EDITTEXT IDC_DATE,35,115,42,12 LTEXT "&Zeit:",-1,90,117,14,10 EDITTEXT IDC_TIME,107,115,42,12 DEFPUSHBUTTON "OK",IDC_BTNOK,30,135,40,12 PUSHBUTTON "Abbrechen",IDCANCEL,72,135,40,12,WS_GROUP PUSHBUTTON "Hilfe",IDC_BTNHELP,114,135,40,12 END |
Die Eingabefelder für Datum und Uhrzeit werden nur freigeschaltet, wenn entweder das Kontrollkästchen auf Ja gestellt wurde, oder der Button Aktuelles geklickt wurde. Verantwortlich dafür sind die Funktionen GetDlgItem(), die aus der ID eines Steuerelements das Handle ermittelt sowie die Funktion EnableWindow(), die die Steuerelemente aktiviert und deaktiviert.
Wenn sie im Programm die Zeile
invoke DialogBoxParam,hInstance,ADDR szClassName,hWnd,eax,NULL ;Dialogbox starten
gegen die Zeile
invoke CreateDialogParam,hInstance,ADDR szClassName,hWnd,eax,NULL ;Dialogbox starten
austauschen, dann haben sie eine nichtmodale Dialogbox. Sie können damit
zwar zeitgleich mehrere Dialogboxen mit den Dateiattributen öffnen, aber
das ergibt bei dieser Anwendung zum einen nicht viel Sinn, zum anderen
funktioniert es zumindest bei mir auch nicht richtig mit der Navigation im
Dialog (Tab-Taste, Enter) sowie der Zeitstempelmanipulation. Für die
Navigation ist in MsgLoop() noch ein Aufruf von IsDialogMessage notwendig.
Was bisher gezeigt wurde, war die klassische Variante des Dialogs, der innerhalb eines Hauptfensters aufgerufen wurde. Windows bietet mindestens zwei Varianten, um einen Dialog auch ohne ein Hauptfenster aufzurufen, allerdings werde ich nur eine davon vorstellen.
Es kommt hier wieder der Dialog zum Ändern der Dateiattribute zum Einsatz. Da das Fenster mit der Liste der Dateien hier nicht vorhanden ist, bedienen sich diese Programme der Kommandozeile, die bisher recht stiefmütterlich behandelt wurde. Das verwendete Verfahren entspricht dem im Kapitel Kommandozeilenparameter. Da hier bestimmte Unicode-Funktionen verwendet werden, sind die Programme dieses Unterkapitels nicht unter Windows 95/98/Me lauffähig.
Da hierbei die Kommandozeilenargumente im Unicode-Format geliefert werden, müssen die Funktionen für die Dateiattribute, die mit einem Dateinamen arbeiten, ebenfalls durch ihre Unicode-Pendants ersetzt werden.
Gegenüber dem vorherigen Programm ändert sich einiges. Die Prozeduren MsgLoop() und WndProc() werden nicht mehr benötigt, da es nur noch die Dialogprozedur gibt, der Rest der Ereignisbehandlung geht über den Windows-Dialogmanager. Mymain() wird kürzer und beschränkt sich auf die Ermittlung der Kommandozeile. Sollte kein Argument angegeben werden, bricht das Programm mit einer Hinweismeldung ab. Ansonsten wird die Adresse des ersten Arguments in einer Variablen gespeichert.
Diese Variable wird den Dateinamen-orientierten Funktionen übergeben. Der Rest des Programms stimmt weitestgehend mit dem vorherigen Programm überein, abgesehen von den notwendigen Anpassungen der Funktionsnamen auf die Unicode-Variante.
Als letzte Aufgabe verbleibt in mymain dann nur noch der Aufruf von DialogBoxParam(). Der dritte Parameter muss hier NULL sein, da es kein Hauptfenster gibt.
;dlgfatr2 - Dialog von Kommandozeile als Hauptfenster
.686
.MODEL flat,stdcall
OPTION casemap:none
include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\gdi32.inc
include c:\masm32\include\shell32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\shell32.lib
getDateTime PROTO :dword, :dword
convertDateTime PROTO :dword, :dword
.CONST
IDC_ARCHIVE EQU 10
IDC_READONLY EQU 11
IDC_HIDDEN EQU 12
IDC_SYSTEM EQU 13
IDC_DATETIME EQU 15
IDC_BTNCURRENT EQU 16
IDC_DATE EQU 18
IDC_TIME EQU 20
IDC_BTNOK EQU 25
.DATA
cParamTitle db "Parameterfehler",0
cParamFehler db "Unzureichende Anzahl Parameter: mindestens ein Dateiname erforderlich",0
szClassName db "AttrDialog",0
cDateString db 13 dup (0)
cTimeString db 9 dup (0)
cDateFormat db "%02d.%02d.%04d",0
cTimeFormat db "%02d:%02d:%02d",0
.DATA? ;uninitialisierte Daten
hInstance HINSTANCE ?
pCmdline LPSTR ?
pArgs dword ? ;Zeiger auf ein Array
iArgs dword ? ;Anzahl Argumente
pFilename dword ? ;Adresse des Dateinamens in der Kommandozeile
win32fileattr WIN32_FILE_ATTRIBUTE_DATA <?>
ftUTC FILETIME <?>
ftLocal FILETIME <?>
stUTC SYSTEMTIME <?>
stLocal SYSTEMTIME <?>
.CODE
start:
call mymain
invoke LocalFree,pArgs
invoke ExitProcess,0
mymain PROC
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke GetCommandLineW ;Verwendung der Unicode-Version
mov pCmdline,eax
invoke CommandLineToArgvW,eax, ADDR iArgs
mov pArgs,eax
cmp iArgs,2 ;gibt es mindestens ein Programmargument
jge @@weiter
invoke MessageBox,0,ADDR cParamFehler,ADDR cParamTitle,MB_OK
ret ;wenn nicht, dann Abbruch mit Meldung
@@weiter:
mov eax,pArgs ;Startadresse des Arrays nach EAX
mov ebx,[eax + 4] ;Startadresse des ersten Programmarguments ermitteln
mov pFilename,ebx ;und speichern
;ab jetzt wie gewohnt weiter
invoke GetFileAttributesExW,pFilename,0,ADDR win32fileattr
mov eax,OFFSET AttrDlgProc ;Adresse der Dialogproc nach EAX
invoke DialogBoxParam,hInstance,ADDR szClassName,NULL,eax,NULL ;Dialogbox starten
ret
mymain ENDP
AttrDlgProc PROC hDlg:dword,uMsg:dword,wParam:dword,lParam:dword
.IF uMsg==WM_INITDIALOG
;ab hier Test der Attribute
test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_ARCHIVE
jz @@test_RO
invoke CheckDlgButton,hDlg,IDC_ARCHIVE,BST_CHECKED
@@test_RO:
test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_READONLY
jz @@test_HID
invoke CheckDlgButton,hDlg,IDC_READONLY,BST_CHECKED
@@test_HID:
test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_HIDDEN
jz @@test_SYS
invoke CheckDlgButton,hDlg,IDC_HIDDEN,BST_CHECKED
@@test_SYS:
test win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_SYSTEM
jz @@test_END
invoke CheckDlgButton,hDlg,IDC_SYSTEM,BST_CHECKED
@@test_END:
;Dateizeit in Systemzeit (UTC) umwandeln
invoke FileTimeToSystemTime,ADDR win32fileattr.ftLastWriteTime,ADDR stUTC
invoke getDateTime,ADDR stUTC,hDlg
invoke GetDlgItem,hDlg,IDC_DATE
invoke EnableWindow,eax,FALSE
invoke GetDlgItem,hDlg,IDC_TIME
invoke EnableWindow,eax,FALSE
mov eax,TRUE
ret
.ENDIF
.IF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDC_DATETIME
invoke IsDlgButtonChecked,hDlg,IDC_DATETIME
cmp eax,BST_UNCHECKED
jne @@dtchecked
invoke GetDlgItem,hDlg,IDC_DATE
invoke EnableWindow,eax,FALSE
invoke GetDlgItem,hDlg,IDC_TIME
invoke EnableWindow,eax,FALSE
jmp short @@dtende
@@dtchecked:
invoke GetDlgItem,hDlg,IDC_DATE
invoke EnableWindow,eax,TRUE
invoke GetDlgItem,hDlg,IDC_TIME
invoke EnableWindow,eax,TRUE
@@dtende:
.ELSEIF ax==IDC_BTNOK
;Test, ob Attribute angeklickt ist
invoke IsDlgButtonChecked,hDlg,IDC_ARCHIVE
cmp eax,BST_UNCHECKED
jne @@test_ARCH_CHECKED
and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_ARCHIVE
@@test_ARCH_CHECKED:
cmp eax,BST_CHECKED
jne @@test_RO_UNCHECKED
or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_ARCHIVE
@@test_RO_UNCHECKED:
invoke IsDlgButtonChecked,hDlg,IDC_READONLY
cmp eax,BST_UNCHECKED
jne @@test_RO_CHECKED
and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_READONLY
@@test_RO_CHECKED:
cmp eax,BST_CHECKED
jne @@test_HID_UNCHECKED
or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_READONLY
@@test_HID_UNCHECKED:
invoke IsDlgButtonChecked,hDlg,IDC_HIDDEN
cmp eax,BST_UNCHECKED
jne @@test_HID_CHECKED
and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_HIDDEN
@@test_HID_CHECKED:
cmp eax,BST_CHECKED
jne @@test_SYS_UNCHECKED
or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_HIDDEN
@@test_SYS_UNCHECKED:
invoke IsDlgButtonChecked,hDlg,IDC_SYSTEM
cmp eax,BST_UNCHECKED
jne @@test_SYS_CHECKED
and win32fileattr.dwFileAttributes,NOT FILE_ATTRIBUTE_SYSTEM
@@test_SYS_CHECKED:
cmp eax,BST_CHECKED
jne @@setAttr
or win32fileattr.dwFileAttributes,FILE_ATTRIBUTE_SYSTEM
@@setAttr:
invoke SetFileAttributesW,pFilename,win32fileattr.dwFileAttributes
;Test, ob Zeitstempel geaendert werden soll
invoke IsDlgButtonChecked,hDlg,IDC_DATETIME
cmp eax,BST_UNCHECKED
;Zeichenketten Datum und Uhrzeit holen
invoke GetDlgItemText,hDlg,IDC_DATE,ADDR cDateString,13
invoke GetDlgItemText,hDlg,IDC_TIME,ADDR cTimeString,9
;Zeichenketten in FILETIME-Struktur eintragen
invoke convertDateTime,ADDR cDateString,ADDR cTimeString
;Datum und Uhrzeit der Datei setzen
call setDateTime
@@DialogFertig:
invoke EndDialog,hDlg,TRUE ;Dialog mit TRUE beenden
.ENDIF
.IF ax==IDC_BTNCURRENT
invoke GetSystemTime,ADDR stUTC
invoke getDateTime,ADDR stUTC,hDlg
invoke GetDlgItem,hDlg,IDC_DATE
invoke EnableWindow,eax,TRUE
invoke GetDlgItem,hDlg,IDC_TIME
invoke EnableWindow,eax,TRUE
mov eax,TRUE
ret
.ENDIF
.IF ax==IDCANCEL
invoke EndDialog,hDlg,FALSE ;Dialog mit FALSE beenden
.ENDIF
.ENDIF
mov eax,FALSE
ret
AttrDlgProc ENDP
getDateTime PROC pSystemTime:dword, hDlg:dword
;ermittelt aus der UTC-Systemzeit eine TZ-basierte Zeit und füllt den Dialog
...
...
ret
getDateTime ENDP
convertDateTime PROC pDateString:dword, pTimeString:dword
;bedient sich des ASCII-Adjust-Befehls aad, um eine gepackte
;BCD in eine Binaerzahl zu konvertieren. Dazu muss aber der
;Inhalt von AX umgedreht werden (xchg)
...
...
ret
convertDateTime ENDP
setDateTime PROC
LOCAL hFile:HANDLE
invoke CreateFileW,pFilename,GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
cmp eax,INVALID_HANDLE_VALUE
mov hFile,eax
je @@procend
invoke SetFileTime,eax,NULL,NULL,ADDR ftUTC
invoke GetLastError
invoke CloseHandle,hFile
@@procend:
ret
setDateTime ENDP
END start
|
Der Inhalt von getDateTime() und convertDateTime() habe ich rausgenommen, er stimmt mit dem vorherigen Programm überein.
Windows stellt eine Reihe von vorgefertigten Dialogen bereit, von denen man vielen in der täglichen Arbeit begegnet.
| Dialog | Bedeutung |
|---|---|
| Color | Der Nutzer kann eine Farbe auswählen bzw. sich selbst zusammenstellen.
Die Funktion ChooseColor() sowie eine Struktur des Typs
CHOOSECOLOR werden benötigt |
Find | Standarddialog zur Textsuche, verwendet z. B. in Notepad.
Die Funktion FindText() sowie eine Struktur des Typs
FINDREPLACE werden benötigt |
Font | Der Standardschriftauswahldialog mit Schriftarten, Größen und
weiteren Attributen.
Die Funktion ChooseFont() sowie eine Struktur des Typs
CHOOSEFONT werden benötigt |
Open | Zeigt einen Dateiauswahldialog an. Der Nutzer kann eine Datei oder
Shell-Erweiterung wählen. Filtern nach Dateiendung und Laufwerkswechsel
sind ebenfalls möglich.
Die Funktion GetOpenFileName() sowie eine Struktur des Typs
OPENFILENAME werden benötigt |
Page Setup | Dialog Seite einrichten. Bietet die aktuelle Seitenkonfiguration wie
Abmessungen, Hoch/Querformat und Ränder zur Änderung an.
Die Funktion PageSetupDlg() sowie eine Struktur des Typs
PAGESETUPDLG werden benötigt |
Der Drucken-Dialog. Auswahl des Druckers sowie Einstellung von Druckoptionen.
Die Funktion PrintDlg() sowie eine Struktur des Typs PRINTDLG
werden benötigt |
Print Property Sheet | Erst ab Windows 2000/XP. Der Dialog besteht aus mehreren Karteikarten
(Sheets), wobei die erste Karteikarte dem Print-Dialog ähnelt. Durch
den Nutzer können anwendungsspezifische Karteikarten hinzugefügt
werden.
Die Funktion PrintDlgEx() sowie eine Struktur des Typs
PRINTDLGEX werden benötigt |
Replace | Standarddialog zum Suchen und Ersetzen, verwendet z. B. in Notepad.
Die Funktion ReplaceText() sowie eine Struktur des Typs
FINDREPLACE werden benötigt |
Save As | Zeigt einen Dateiauswahldialog an. Der Nutzer kann eine Datei oder
Shell-Erweiterung wählen. Filtern nach Dateiendung und Laufwerkswechsel
sind ebenfalls möglich.
Die Funktion GetSaveFileName() sowie eine Struktur des Typs
OPENFILENAME werden benötigt |
Es existiert zwar auch ein Print Setup-Dialog, aber in neuen Anwendungen sollte der Page Setup-Dialog verwendet werden.
Abgesehen vom Find- und Replace-Dialog sind alle Dialoge modal. Im Falle der beiden Ausnahmen müssen sie in der Nachrichtenschleife die Funktion IsDialogMessage() verwenden, damit diese Dialoge Tastatureingaben korrekt verarbeiten.
Gerade im Falle der Suchen- und Ersetzen-Dialoge werden von vielen Anwendungen eigene Dialoge eingesetzt, die z. B. über eine Eingabehistorie und erweiterte Suchoptionen (reguläre Ausdrücke, nur ganze Wörter suchen) verfügen.
| Vorheriges Kapitel (Fensterklassen) | Nach oben |
| Zum Inhaltsverzeichnis | |
| Zur Startseite |