Iczelion - 08 - Menüs

Tutorial 8: Menüs


In diesem Tutorial werden wir lernen, wie wir mit Menüs in unseren Fenstern arbeiten.

Laden Sie das Beispiel 1 und herunter.

Theorie:

Menüs sind eine der wichtigsten Komponenten in ihrem Fenster. Menüs präsentieren eine Liste an Funktionen, die ein Programm den Benutzer bietet. Der Benutzer muss nicht erst das Handbuch lesen, das mit dem Programm kommt, um das Programm nutzen zu können, sondern er kann die Menüs durchgehen um sich einen Überblick zu beschaffen, was das Programm leisten kann und sofort anfangen damit herumzuspielen. Da ein Menü ein Tool ist, das dem Benutzer schnellen Zugriff erlaubt, sollten Sie dem Standard folgen. Kurz gesagt sollten die ersten beiden Menü-Elemente Datei und Bearbeiten sein und das letzte sollte Hilfe sein. Sie können ihre eigenen Menü-Elemente zwischen Bearbeiten und Hilfe einfügen. Wenn ein Menü-Element eine Dialog-Box aufruft, sollten Sie eine Auslassung (...) an den Menü-String anhängen.

Menüs sind Arten von Ressourcen. Es gibt verschiedene Arten von Ressourcen, so wie Dialog-Boxen, Icons, Bitmaps, Menüs, etc. Ressourcen werden in einer separaten Datei gespeichert, die Ressource-Datei genannt wird und normalerweise die Endung .rc hat. Sie fügen dann die Ressourcen mit ihrem Source-Code während des Linkens zusammen. Das endgültige Produkt ist eine ausführbare Datei, welche beides enthält, Befehle und Ressourcen.

Sie können Ressourcen-Skripts mit jeglichem Text-Editor schreiben. Sie setzen sich aus Sätzen zusammen, die das Aussehen und andere Attribute der Ressourcen beschreiben, die in einem Programm verwendet werden. Obwohl Sie ihre Ressource-Skripts in einem Text-Editor schreiben können, ist es recht mühsam. Eine bessere Alternative ist das Benutzen eines Ressource-Editors, mit welchem Sie mit Leichtigkeit Ressourcen visuell designen können. Ressource-Editors sind normalerweise in Compiler-Umgebungen wie Visual C++, Borland C++, etc. enthalten.

Sie beschreiben eine Menü-Ressource folgendermaßen:

MyMenu MENU { [Menü liste hier] }
C Programmierer erkennen vielleicht die Ähnlichkeit zur Deklaration einer Struktur. MyMenu ist der Menü-Name gefolgt von dem Schlüsselwort MENU und der Menü-Liste in den geschweiften Klammern. Alternativ können Sie statt den geschweiften Klammern auch BEGIN und END benutzen, wenn Sie wollen. Diese Syntax wird Pascal-Programmierern mehr vertraut sein.

Menü-Liste kann entweder ein MENUITEM oder ein POPUP Statement sein.

MENUITEM Statement definiert eine Menü-Zeile, was kein Popup-Menü aufruft, wenn es ausgewählt wird. Die Syntax ist wie folgt:

MENUITEM "&text", ID [,options]
Sie beginnt mit dem Schlüsselwort MENUITEM gefolgt von dem Text, den Sie in der Menü-Zeile anzeigen lassen wollen. Beachten Sie das Ampersand-Zeichen (&). Es verursacht den folgenden Buchstaben unterstrichen darzustellen. Nach dem Text folgt die ID des Menü-Elements. Die ID ist eine Zahl, die benutzt wird, um das Menü-Element in der Nachricht, die an die Fensterprozedur gesendet wird, wenn es ausgewählt wird, zu identifizieren. Demnach muss jede Menü-ID einmalig sein.

Optionen sind optional. Verfügbare Optionen sind folgende:

  • GRAYED
  •   Das Menü-Elemnt ist nicht verfügbar und erzeugt keine WM_COMMAND-Nachricht. Der Text ist grau.
  • INACTIVE
  • Das Menü-Elemnt ist nicht verfügbar und erzeugt keine WM_COMMAND-Nachricht. Der Text wird normal angezeigt.
  • MENUBREAK
  •   Dieses Element und die folgenden Elemente erscheinen in einer neuen Zeile des Menüs.
  • HELP
  •   Dieses Element und die folgenden Elemente sind rechts-ausgerichtet.
Sie können eins der oben genannten Optionen benutzen oder Sie mit dem OR-Operator kombinieren. Achten Sie darauf, dass INACTIVE und GRAYED nicht miteinander kombiniert werden können.

POPUP Statement hat folgende Syntax:
POPUP "&text" [,options] { [menü liste] }
Das POPUP Statement definiert eine Menü-Zeile, die, wenn Sie ausgewählt wird, aufklappt und eine Liste weiterer Menü-Elemente in einem kleinen Popup-Fenster anzeigt. Die Menü-Liste kann entweder ein MENUTIEM oder POPUP ein Statement sein. Es gibt ein besonderes MENUITEMOPUP Statement, MENUITEM SEPARATOR, welches eine horizontale Linie in das Popup-Fenster zeichnet.

Als nächsten Schritt, nachdem Sie ihr Menü Ressource-Skript beendet haben, ist, dieses in ihrem Programm anzusprechen.

Sie können das an zwei verschiedenen Orten in ihrem Programm machen.

  • Im lpszMenuName Element der WNDCLASSEX Struktur. Sagen wir, Sie haben ein Menü names "FirstMenu", dann können Sie ihr Menü ihrem Fenster so zuordnen:


.DATA MenuName db "FirstMenu",0 ........................... ........................... .CODE ........................... mov wc.lpszMenuName, OFFSET MenuName ...........................


  • Im Menü Handle Parameter von CreateWindowEx so wie hier:


.DATA MenuName db "FirstMenu",0 hMenu HMENU ? ........................... ........................... .CODE ........................... invoke LoadMenu, hInst, OFFSET MenuName mov hMenu, eax invoke CreateWindowEx,NULL,OFFSET ClsName,\ OFFSET Caption, WS_OVERLAPPEDWINDOW,\ CW_USEDEFAULT,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,\ NULL,\ hMenu,\ hInst,\ NULL\ ...........................


Sie werden sich vielleicht Fragen was der Unterschied zwischen diesen beiden Methoden ist.

Wenn Sie das Menü in der WNDCLASSEX-Struktur refernzieren, wird das Menü das Standard-Menü für die Fenster-Klasse. Jedes Fenster dieser Klasse wird das selbe Menü haben.

Wenn Sie für jedes Fenster der selben Klasse verschiedene Menüs haben möchten, müssen Sie die zweite Methode wählen. In diesem Fall wird jedes Fenster, das ein Menü-Handle in seiner CreateWindowEx-Funktion übergibt, ein Menü haben, dass das Standard-Menü in der WNDCLASSEX-Struktur 'überschreibt'.

Als nächstes werden wir uns anschauen, wie ein Menü die Fenster-Prozedur benachrichtigt, wenn der Benutzer ein Menü-Element auswählt.

Wenn der Benutzer ein Menü-Element auswählt, erhält die Fenster-Prozedur eine WM_COMMAND-Nachricht. Das untere Word von wParam enthält die Menü-ID des ausgewählten Elements.

Nun haben wir die wichtigsten Informationen um ein Menü zu erstellen und zu benutzen. Tun wir's.

Beispiel:

Das erste Beispiel zeigt, wie man ein Menü erzeugt und benutzt, indem der Menü-Name in einer Fenster-Klasse spezifiziert wird.

.386 .model flat,stdcall option casemap:none WinMain proto :DWORD,:DWORD,:DWORD,:DWORD include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib .data ClassName db "SimpleWinClass",0 AppName db "Our First Window",0 MenuName db "FirstMenu",0 ; Der Name unseres Menüs in der Ressource-Datei. Test_string db "You selected Test menu item",0 Hello_string db "Hello, my friend",0 Goodbye_string db "See you again, bye",0 .data? hInstance HINSTANCE ? CommandLine LPSTR ? .const IDM_TEST equ 1 ; Menu IDs IDM_HELLO equ 2 IDM_GOODBYE equ 3 IDM_EXIT equ 4 .code start: invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND 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,OFFSET MenuName ; Hier kommt unser Menü-Name hin 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,NULL,0,0 .BREAK .IF (!eax) invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_COMMAND mov eax,wParam .IF ax==IDM_TEST invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK .ELSEIF ax==IDM_HELLO invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK .ELSEIF ax==IDM_GOODBYE invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK .ELSE invoke DestroyWindow,hWnd .ENDIF .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp end start ************************************************************************************************************************** Menu.rc ************************************************************************************************************************** #define IDM_TEST 1 #define IDM_HELLO 2 #define IDM_GOODBYE 3 #define IDM_EXIT 4 FirstMenu MENU { POPUP "&PopUp" { MENUITEM "&Say Hello",IDM_HELLO MENUITEM "Say &GoodBye", IDM_GOODBYE MENUITEM SEPARATOR MENUITEM "E&xit",IDM_EXIT } MENUITEM "&Test", IDM_TEST }

Analyse:

Lassen Sie uns erst die Ressource-Datei analysieren.

#define IDM_TEST 1 /* equal to IDM_TEST equ 1*/ #define IDM_HELLO 2 #define IDM_GOODBYE 3 #define IDM_EXIT 4
Die oberen Zeilen definieren die Menü-IDs, die vom Menü-Skript benutzt werden. Sie können jeden Wert einer ID zuweisen, solange dieser Wert einmalig in dem Menü ist.

FirstMenu MENU


Deklarieren Sie ihr Menü mit dem Schlüsselwort MENU.

POPUP "&PopUp" { MENUITEM "&Say Hello",IDM_HELLO MENUITEM "Say &GoodBye", IDM_GOODBYE MENUITEM SEPARATOR MENUITEM "E&xit",IDM_EXIT }


Definieren Sie ein Popup-Menü mit vier Menü-Elementen, wobei der dritte ein Menü-Seperator ist.

 MENUITEM "&Test", IDM_TEST


Definiert eine Menü-Zeile im Hauptmenü.

Als nächstes werden wir den Source Code unter die Lupe nehmen.

MenuName db "FirstMenu",0 ; Der Namen unseres Menüs in der Ressource-Datei. Test_string db "You selected Test menu item",0 Hello_string db "Hello, my friend",0 Goodbye_string db "See you again, bye",0


MenuName ist der Name des Menüs in der Ressource-Datei. Beachten Sie, dass Sie mehr als ein Menü in der Ressource-Datei definieren können, so dass Sie genauer spezifizieren müssen, welches Menü Sie benutzen wollen. Die übrigen drei Zeilen definieren die Texte, welche in den MessageBoxen angezeigt werden, die von den einzelnen Menü-Elementen, die der Benutzer anwählt, aufgerufen werden.

IDM_TEST equ 1 ; Menu IDs IDM_HELLO equ 2 IDM_GOODBYE equ 3 IDM_EXIT equ 4


Definiert Menü-IDs für die Benutzung in der Fenster-Prozedur. Diese Werte MÜSSEN identisch zu denen in der Ressource-Datei definierten sein.

.ELSEIF uMsg==WM_COMMAND mov eax,wParam .IF ax==IDM_TEST invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK .ELSEIF ax==IDM_HELLO invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK .ELSEIF ax==IDM_GOODBYE invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK .ELSE invoke DestroyWindow,hWnd .ENDIF


In der Fenster-Prozedur bearbeiten wir die WM_COMMAND Nachricht. Wenn der Benutzer ein Menü-Element auswählt, wird die Menü-ID des Menü-Elements an die Fensterprozedur im unteren Word von wParam gesendet, zusammen mit der WM_COMMAND Nachricht. So, dass wir den Wert von wParam in EAX speichern und den Wert von AX mit der Menü-ID vergleichen, die wir vorher definiert haben, und entsprechend reagieren. In den ersten drei Fällen, wenn der Benutzer das Menü-Element Test, Say Hello oder Say GoodBye auswählt, zeigen wir lediglich einen String in einer Message Box an.

Wenn der Benutzer das Menü-Element Exit auswählt, rufen wir DestroyWindow, zusammen mit dem Handle unseres Fensters als Parameter, auf, damit unser Fenster geschlossen wird.

Wie Sie sehen können, ist das spezifizieren eines Menü-Namens in einer Fenster-Klasse ziemlich einfach. Wie dem auch sei, Sie können auch eine alternative Methode benutzen, um ein Menü in ihrem Fenster zu laden. Ich werde hier nicht den kompletten Source Code hier zeigen. Die Ressource-Datei bleibt bei beiden Methoden die selbe. Es gibt nur wenige kleine Änderungen im Source, welche ich unten zeigen werde.

.data? hInstance HINSTANCE ? CommandLine LPSTR ? hMenu HMENU ? ; handle unseres Menüs Definiert eine Variable des Typs HMENU, um unser Menü-Handle zu speichern. invoke LoadMenu, hInst, OFFSET MenuName mov hMenu,eax INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\ hInst,NULL


Bevor CreateWindowEx aufgerufen wird, rufen wir LoadMenu mit dem Instanz-Handle und einen Pointer auf den Namen unseres Menüs auf. LoadMenu gibt das Handle unseres Menüs aus der Ressource-Datei zurück, welches wir dann CreateWindowEx übergeben.


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