Menus
Menus are added to a program in two steps, first you create the menu, using either a resource editor, or a normal text editor, and then you add code to support it. The resource editor of your compiler will actually maintain a text file for you, with a '.rc' extention. If you don't want to use the resource editor of your compiler, you can edit the .rc file directly, using notepad.


The Menu Resource
A sample .rc file might contain the following for a menu definition:
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
NOTE: I have eliminated portions of the file to save space in this handout. If the compiler created a .rc file, do not remove anything from it.
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_MENU1 MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "E&xit", ID_MENUEXIT
END
END

In this example, IDR_MENU1 is the name of the menu, and MENU identifies it as a menu resource. There are two menu items defined, "File" and "Exit". The File menu item is a pop-up menu, which contains only the Exit menu item. The ID_MENUEXIT is the ID for the menu item. This is an integer value, which can be found in the resource.h file. For example, the resource .h file should have an entry somewhere that looks like:
#define IDR_MENU1 101
#define ID_MENUEXIT 40001

The exact values (101, and 40001) are not important, but should be unique in the resource.h file. If you use a resource editor, it will automatically insure this, and will also make entries in both the .rc file, and the resource.h file.

Now that the menu is defined (the entire menu has a unique ID, ID_MENU1, and each menu item that performs a task has a unique ID, ID_MENUEXIT), you must make the changes to your source code.

When your program is compiled, the .rc file is converted into a .res file, and the .res file is eventualy linked with your .EXE file. Windows will know how to find the resource in the .EXE file.



Source Code Changes
You must make two changes to your program to use a menu. First, you must attach the menu (using the menu's unique ID) to a window, and then you must write the code to have the program respond to the menu selection event in the WndProc for your main window.


Attaching the menu
You attach a menu to a window class, not a specific window. Any window created using that class, will be created with that menu. Therefore, you must set the lpszMenuName member of the WNDCLASS structure appropriately, before calling RegisterClass.

If you made the menu name a string (enclosed the names in double quotes) in your .rc file, then you can simply use the same name for the lpszMenuName value. If you used an integer ID for the menu (which most resource editors do, and we did in our example above), you must use the MAKEINTRESOURCE macro with that ID, for the value of lpszMenuName:
WndClass.lpszMenuName = "MyMenu"; // If your using "MyMenu" in the .rc file
WndClass.lpszMenuName = MAKEINTRESOURCE( IDR_MENU1 ); // If your using IDR_MENU1


Responding to menu selections
Your program is sent a WM_COMMAND message when a menu item is selected. Actually, your program will receive several messages such as WM_SYSCOMMAND,WM_INITMENU, WM_MENUSELECT, and WM_INITMENPOPUP, but Windows will handle all these messages for you. It's the WM_COMMAND that is the end result of the user selecting a menu choice.

Message  WM_COMMAND
wParam  The low-word portion contains the sending-control's ID
The high-word portion is a notification code (1 if message is from an accelerator, 0 if a menu)
lParam  HWND of control generating message


Note: In Win16, the parameters were different: lParam's low-word was HWND of the sending control, and it's high-word was the notification code. But in Win16, an HWND was 16 bits in size, and in Win32 it's now 32 bits. So the HWND now fully occupies the lParam, meaning that the notification code was moved into wParam. In Win16, wParam was a 16 bit value, in Win32, it's a 32-bit value.

The basic processing for a menu choice (or a button-click), is to check the WM_COMMAND message, and inspect the ID code in the low-word of wParam (the LOWORD and HIWORD macros help access word halves of lParam), to decide what was selected/clicked:

case WM_COMMAND:
switch( LOWORD(wParam) )
{
case ID_MENUEXIT:
// Do something
break;
case ID_ADDBUTTON:
// Do something
break;
}
break;



Enabling/Disabling and Checking/unchecking
Menu items can also be enabled or disabled, and checked or unchecked. The EnableMenuItem function handles enabling and disabling, and the CheckMenuItem handles checking and unchecking. Both functions will require a handle to the menu (an HMENU), and the ID of the menu item to work against, as well as the desired menu state.

BOOL EnableMenuItem( HMENU hMenu, UINT uIDEnableItem, UINT uEnable );
 Parameter  Meaning
 hMenu  Handle to the menu
 uIDEnableItem  ID of the menuitem, or it's position (based on uCheck)
 uCheck  One of the following:
MF_BYCOMMAND - Indicates that UIDCheckItem contains a menu item ID (default)
MF_BYPOSITION - Indicates that uIDCheckItem contains a menu item position number
MF_DISABLED - Indicates that the menu item should be disabled, but not grayed
MF_ENABLED - Indicates that the menu item should be enabled
MF_GRAYED - Indicates that the menu item should be grayed and disabled

One of the MF_BYCOMMAND or MF_BYPOSITION flags can be combined with one of the MF_DISABLED, MF_ENABLED, or MF_GRAYED flags, using the | (OR) operator.

Returns the previous enabled status, or 0xFFFFFFFF upon error.


DWORD CheckMenuItem( HMENU hmenu, UINT uIDCheckItem, UINT uCheck );
 Parameter  Meaning
 hMenu  Handle to the menu
 uIDCheckItem  ID of the menuitem, or it's position (based on uCheck)
 uCheck  One of the following:
MF_BYCOMMAND - Indicates that UIDCheckItem contains a menu item ID (default)
MF_BYPOSITION - Indicates that uIDCheckItem contains a menu item position number
MF_CHECKED - Menu state should be changed to checked
MF_UNCHECKED - Menu state should be changed to unchecked

One of the MF_BYCOMMAND or MF_BYPOSITION flags can be combined with one of the MF_CHECKED or MF_UNCHECKED flags, using the | (OR) operator.

Returns the previous check status, or 0xFFFFFFFF upon error.


HMENU GetMenu( HWND hWnd );

GetMenu retrieves a menu handle for a window. The hWnd parameter is the handle to the window you want to get the menu handle for, and is most often the main window handle, or the hWnd parameter to your WndProc.

Case WM_COMMAND:
if( wParam==ID_MENUTOGGLE ) //ID_MENUTOGGLE must be a valid menu ID
{
HMENU hMenu;
hMenu = GetMenu( hWnd );
CheckMenuItem( hMenu, ID_MENUTOGGLE, (WasChecked) ? MF_UNCHECKED : MF_CHECKED );
WasChecked = !WasChecked; // WasChecked should be a global or static local variable.
}
break;



BOOL GetMenuItemInfo(HMENU hMenu, UINT uItem, BOOL fByPosition, LPMENUITEMINFO lpmii );
Retrieves detailed information about a menu item and it's state.
 Parameter  Meaning
 hMenu  Handle to the menu
 uItem  ID of the menuitem, or it's position (based on fByPosition)
 fByPosition  If TRUE then uItem contains the menu item position, if FALSE then it contains the menu item ID
 lpmii  Pointer to a MENUITEMINFO structure

  1. Returns the non-zero for success, zero for failure.


    Adding a menu

    Visual C++ 5.0
  2. Open the desired project.
  3. Select File/New... from the main menu
  4. Make sure that File is the current tab of the New dialog
  5. Select Resource Script
  6. Make up a File name, but it should reflect the project. (For example, the mhello project, should have an mhello file name. Visual C++ will append a '.rc' to the filename you enter.
  7. Click OK.
  8. You should now see a ResourceView tab appear in the window on the left hand side of the screen. Click it.
  9. At the top of the window on the left hand side of the screen, you should see a yellow folder next to ????? resources, where ????? is the name of your project.
  10. Right-click on the ????? Resources item, and select Insert from the pop-up menu.
  11. In the Insert Resource dialog, double-click Menu
  12. The resource editor should now present you with a blank menu, in a window with a caption of '?????.rc - IDR_MENU1 (Menu)'. Double-click on the first blank rectangle. In the Menu Item Properties box, enter &File for the caption (The & will underline the character after it).
  13. Now, click on the File menu item, and double-click the blank menu item below it.
  14. Again, the MenuItem Properties dialog appears. This time, make sure that ID says ID_MENUEXIT and that the Caption says E&xit. It should now look like:
  15. Where you would double-click to make a new menu entry
  16. You can now close the menu editor window (pictured above) if desired.
  17. If it's not already there, add the following line to your source file:
    #include "resource.h"
  18. Change the line where you are setting the lpszMenuName of your WNDCLASS from:
    WndClass.lpszMenuName = NULL;
    to:
    WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
  19. Compile and run the program. You should see the menu, but the Exit menu choice is not yet functional. You must add the code for it, outined below.
  20. Add the following code to the switch in your WndProc function:
    case WM_COMMAND:
    switch( LOWORD(wParam) ) // Note: switch within the switch
    {
    case ID_MENUEXIT:
    SendMessage( hWnd, WM_CLOSE, 0, 0 );
    break;
    default:
    return( DefWindowProc( hWnd, Message, wParam, lParam ) );
    }
    break;
  21. Compile and run your program. The Exit menu choice should now exit the program.

Borland C++ 5.0

  1. Open the desired project
  2. Double click the .rc file, listed in the Project window
  3. In the next window to appear, right-click on the Identifiers item.
  4. Select New Resource from the pop-up menu that appears
  5. Double click the MENU item in the New Resource window
  6. Borland C++ will automatically add a standard menu, complete with File/Exit, Help/About, etc. (You can control the type of menu it creates by selecting the Options button before double-clicking MENU in step #5)
  7. The default menu ID given to the entire menu is IDM_MENU1. Select the File/Exit menu item in the resource editor (not the compiler's main menu!) and Note it's ID in the Property Inspector window (it should be 108).
  8. Select the Resource/Identifiers item from the compilers main menu. Make a note of the value for the IDM_MENU1 item (it should be 1).
  9. Close the menu editor
  10. Close the .RC file, saving your changes when asked.
  11. In your source code file, make the following change:
    WndClass.lpszMenuName = NULL;
    to
    WndClass.lpszMenuName = MAKEINTRESOURCE( ??? );
  12. Where ??? represents the ID value for IDM_MENU1, from step 8
  13. Add the following code to your switch in your WndProc:
    case WM_COMMAND:
    switch( LOWORD(wParam) ) // Note: switch within the switch
    {
    case ??????: // Replace ??????? with the ID value noted in step 7
    SendMessage( hWnd, WM_CLOSE, 0, 0 );
    break;
    default:
    return( DefWindowProc( hWnd, Message, wParam, lParam ) );
    }
    break;
  14. Compile and run your program

    Note: Borland (to the best of my knowledge) does not maintain the IDs for menu items in a .h file like Microsoft does. This means that the IDs defined in the .rc file are not easily accessible to the .c source file. You can manually create the resource.h file, and place the definitions there, like Microsoft does. It is recommended that you not start using the ID values like 1, or 108, but instead manage the ID names with #define's in a .h file.