Tutorial 8: Menu

In this tutorial, we will learn how to incorporate a menu into our window.

Theory:

Menu is one of the most important component in your window. Menu presents a list of services a program offers to the user. The user doesn't have to read the manual included with the program to be able to use it, he can peruse the menu to get an overview of the capability of a particular program and start playing with it immediately. Since a menu is a tool to get the user up and running quickly, you should follow the standard. Succintly put, the first two menu items should be File and Edit and the last should be Help. You can insert your own menu items between Edit and Help. If a menu item invokes a dialog box, you should append an ellipsis (...) to the menu string.
Menu is a kind of resource. There are several kinds of resources such as dialog box, string table, icon, bitmap, menu etc. SpAsm is full featured with Editors for all kind of Resources. You just have to edit them, click on [OK], and the Resources will be included inside your exe file ready to be activated by your code. For Menu, read "Editors" /  "Menu_Editor" in SpAsmHelp.exe.

Options (checkBoxes at the bottom of the Editor), are optional. Available options are as follows:


The next step after you are finished with the menu resource script is to reference it in your program.
You can do this in two different places in your program.

So you may ask, what's the difference between these two methods?
When you reference the menu in the WNDCLASSEX structure, the menu becomes the "default" menu for the window class. Every window of that class will have the same menu.
If you want each window created from the same class to have different menus, you must choose the second form. In this case, any window that is passed a menu handle in its CreateWindowEx function will have a menu that "overrides" the default menu defined in the WNDCLASSEX structure.
Next we will examine how a menu notifies the window procedure when the user selects a menu item.
When the user selects a menu item, the window procedure will receive a WM_COMMAND message. The low word of wParam contains the menu ID of the selected menu item.
Now we have sufficient information to create and use a menu. Let's do it.

Example:

The first example shows how to create and use a menu by specifying the menu ID in the window class.

[M00_Menu  1000                  M00_Say_Hello  1001             M00_Say_GoodBye  1002
 M00_Exit  1003                  M00_Test  1004]
[hMenu: 0]

Main:
    call 'Kernel32.GetModuleHandleA' &NULL   | mov D§hInstance eax
    call 'User32.LoadIconA'  0  &IDI_WINLOGO | mov D§wc_hIcon eax  D§wc_hIconSm eax
    call 'User32.LoadCursorA' 0  &IDC_ARROW  | mov D§wc_hCursor eax
    call 'User32.RegisterClassExA'  WindowClassEX
    call 'User32.LoadMenuA' D§hInstance M00_Menu
      mov D§hMenu eax
    call 'User32.CreateWindowExA' &NULL ClassName AppName,
                                 &WS_OVERLAPPEDWINDOW,
                                 &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT &CW_USEDEFAULT,
                                 &NULL D§hMenu D§hInstance &NULL
      mov D§WindowHandle eax

    call 'User32.ShowWindow'  D§WindowHandle &SW_SHOWNORMAL
    call 'User32.UpdateWindow'  D§WindowHandle

L1: call 'User32.GetMessageA' FirstMessage 0 0 0 | cmp eax 0 | je L9>
        call 'User32.TranslateMessage'  FirstMessage
        call 'User32.DispatchMessageA'  FirstMessage
    jmp L1<

L9: call 'Kernel32.ExitProcess' 0

__________________________________________________________________________________
 

[Test_string:    'You selected Test menu item' 0
 Hello_string:   'Hello, my friend' 0
 Goodbye_string: 'See you again, bye' 0]
 

Proc MainWindowProc:
    Arguments @Adressee, @Message, @wParam, @lParam
 
    pushad
 
        .If D@Message e &WM_DESTROY
            call 'User32.PostQuitMessage' &NULL
 
        .Else_If D@Message e &WM_COMMAND
            mov eax D@wParam
            If eax = M00_Test
                call 'User32.MessageBoxA' &NULL  Test_string  AppName  &MB_OK
            Else_If eax = M00_Say_Hello
                call 'User32.MessageBoxA' &NULL Hello_string AppName &MB_OK
            Else_If eax = M00_Say_GoodBye
                call 'User32.MessageBoxA' &NULL  Goodbye_string  AppName &MB_OK
            Else
                call 'User32.DestroyWindow' D@Adressee
            End_If
 
        .Else
            popad
            call 'User32.DefWindowProcA' D@Adressee D@Message D@wParam D@lParam
            Exit
 
        .End_If
 
    popad | mov eax &FALSE
EndP

Analysis:

Let's analyze the resource file first.
 
 
[M00_Menu  1000                  M00_Say_Hello  1001             M00_Say_GoodBye  1002
 M00_Exit  1003                  M00_Test  1004]
 
The above lines define the menu IDs defined by the menu Editor (if you choose 1000 as menu Base ID).

These values MUST be those returned by the Menu Editor when you click on [Store Equates to CliBoard].
 

       .Else_If D@Message e &WM_COMMAND
            mov eax D@wParam
            If eax = M00_Test
                call 'User32.MessageBoxA' &NULL  Test_string  AppName  &MB_OK
            Else_If eax = M00_Say_Hello
                call 'User32.MessageBoxA' &NULL Hello_string AppName &MB_OK
            Else_If eax = M00_Say_GoodBye
                call 'User32.MessageBoxA' &NULL  Goodbye_string  AppName &MB_OK
            Else
                call 'User32.DestroyWindow' D@Adressee
            End_If

In the window procedure, we process WM_COMMAND messages. When the user selects a menu item, the menu ID of that menu item is sended to the window procedure in the low word of wParam along with the WM_COMMAND message. So when we store the value of wParam in eax, we compare the value in ax to the menu IDs we defined previously and act accordingly. In the first three cases, when the user selects Test, Say Hello, and Say GoodBye menu items, we just display a text string in a message box.
If the user selects Exit menu item, we call DestroyWindow with the handle of our window as its parameter which will close our window.
As you can see, specifying menu ID in a window class is quite easy and straightforward. However you can also use an alternate method to load a menu in your window. I won't show the entire source code here. The resource file is the same in both methods. There are some minor changes in the source file which I 'll show below.
 

 
[hMenu: ? ]                   ; handle of our menu
 
Define a variable of type HMENU to store our menu handle.

        call 'USER32.LoadMenuA'  D§hInstance  1000
        mov    D§hMenu eax
        call  'USER32.CreateWindowExA'  &NULL  ClassName  AppName,
           &WS_OVERLAPPEDWINDOW  &CW_USEDEFAULT,
           &CW_USEDEFAULT  &CW_USEDEFAULT  &CW_USEDEFAULT  &NULL  D§hMenu,
           D§hInstance  &NULL

Before calling CreateWindowEx, we call LoadMenu with the instance handle and a pointer to the name of our menu. LoadMenu returns the handle of our menu in the resource file which we pass to CreateWindowEx.


[Iczelion's Win32 Assembly HomePage]