Page 1 of 1

Intro to the Windows API Part 2: Creating a menu Rate Topic: -----

#1 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 615
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Posted 20 January 2011 - 06:47 AM

Hi,

Welcome to my second Intro to the Windows API tutorial, the first can be found here.

In this tutorial, we are going to take a look at how we can set up a menu bar. We will use two methods, the first will be using resources, the second will be doing it manually so to speak, with function calls to the Windows API.

What is a resource?
A resource is a pre-defined bit of data stored in your executable file. You can create resources using a resource script, which are files with the ".rc" extension. Resources can be made in a resource editor, which can be built into an IDE (such as Visual C++ Professional) or be on their own as a single program. In this tutorial, we will use software called "ResEdit", which you can download from here, as Visual C++ Express does not support resource editing.

Creating a resource file in ResEdit
First, start by creating a new project in the file menu, under New Project. Just name project resource. If you get a warning about file paths, then you need to add the include directory that contains the files you need. I added my MinGW include directory. You can configure the include paths in ResEdit under Options/preferences/general/include paths.

In ResEdit, we need to add a menu resource, so to the left under "Resources", right click and choose "Add Resource/Menu". Now you should rename the menu resource to something more meaningful, right click on the IDR_MENU1 under resources to the left, and choose rename. Name the menu IDR_MAIN_MENU.

Now that we have a menu, you need to add a drop down menu. We will create the classic File menu. In the main window, just under the IDR_MAIN_MENU tab in the "type here" box type, "&File", then hit enter. Now, under the file menu, create a menu item called "&Exit". Now select the "Exit" menu item and in the properties pane, change the ID to IDM_EXIT_APP.

Now that you have create the resource, save it and quit ResEdit.

Now you need to create a Win32 project in Visual C++, please refer to the first tutorial if you need to. To make use of the menu resource we just created, we need to take the resource.rc file and resource.h file that was created by our resource editor and add them to the project.

The first step is to put these files in the same directory as WinMain.cpp.
Now, in Visual C++, under the solution explorer, right click on "Resource files" and choose "Add/Existing..." then select your .rc file you created. Now do the same for your .h file, right click on "Header files" and choose "Add/Existing..." then select your .h file you created.

Now that all the files are added to the project, we need to make a few changes to the original code from the first tutorial to make use of our menu. Here is the whole modified code, the changes will be explained.

/* Create nice looking controls */
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' \
					   version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' \
						language='*'\"")

#include <Windows.h>	
#include "resource.h"	// We want to use our resource that we created, so we need to include the resouces header file. 

/// Global variables
const char *clsName = "WinAPI";
char *title = "Windows API";
bool running = true;
HWND hWnd = NULL;

LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain( HINSTANCE hInstance,		
					HINSTANCE hPrevInstance,	
					LPSTR lpCmdLine,			
					int nCmdShow)				
{			
	WNDCLASSEX	WndEx;								
	MSG			msg;

	// Set up the windows class structure
	WndEx.cbSize			= sizeof(WNDCLASSEX);					
	WndEx.style				= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;				
	WndEx.lpfnWndProc		= (WNDPROC)WndProc;							
	WndEx.cbClsExtra		= 0;								
	WndEx.cbWndExtra		= 0;									
	WndEx.hIcon				= LoadIcon(NULL, IDI_APPLICATION);	
	WndEx.hCursor			= LoadCursor(NULL, IDC_ARROW);		
	WndEx.hbrBackground		= NULL;								
	WndEx.lpszMenuName		= MAKEINTRESOURCE(IDR_MAIN_MENU);	// Create our menu from the resource we made 						
	WndEx.lpszClassName		= clsName;							
	WndEx.hInstance			= hInstance;										
	WndEx.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);		

	// Register the windows class
	if (!RegisterClassEx(&WndEx))
	{
		MessageBox(NULL, "Failed to register class", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}

	// Create the window
	if (!(hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
								clsName,							
								title,								
								WS_OVERLAPPEDWINDOW |				
								WS_CLIPSIBLINGS | WS_CLIPCHILDREN,				
								CW_USEDEFAULT, CW_USEDEFAULT,		
								460, 340,							
								NULL,							
								NULL,								
								hInstance,							
								NULL)))								
	{	
		MessageBox(NULL, "Failed to create the window", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}

	// The window is initially hidden, we need to show it
	ShowWindow(hWnd, SW_SHOW);	

	// The main message loop of our program 
	while(running)								
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	
		{
			TranslateMessage(&msg);			
			DispatchMessage(&msg);			
		}
	}

	return msg.wParam;
}


LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)									
	{
		case WM_CLOSE:	
			DestroyWindow(hWnd);
			return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
			running = false;
			return 0;

		case WM_COMMAND:											
		{
			switch(LOWORD(wParam))
			{  
				case IDM_EXIT_APP:
					PostQuitMessage(0);
					running = false;
					break;
			}

		} break;

		default:
			return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
}



#include "resource.h" 


The first change that has been made is the inclusion of the resource.h file, which contains all the definitions of the resource items that we created. When we need to use a resource, we must included our resource.h.

WndEx.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN_MENU);


On line 34, we have changed told the windows class structure that we want to use the IDR_MAIN_MENU menu we created, we use the macro "MAKEINTRESOURCE" to make use of the menu.

		case WM_COMMAND:											
		{
			switch(LOWORD(wParam))
			{  
				case IDM_EXIT_APP:
					PostQuitMessage(0);
					running = false;
					break;
			}

		} break;


Now that the menu is set up, we want to catch any "clicked" messages that are sent by the menu. These messages are sent as WM_COMMAND messages, and the specific menu item that was clicked is contained within the low word (bottom 16 bits) of wParam. We close the window if the exit menu item was clicked.

If you run the program, you should have a nice looking menu on your application which you can close using the Exit menu item.


Posted Image


Setting up a menu bar without resources

This may be prefered by some, and is actually very simple. Again, i will post the full code first, then explain it after.

/* Create nice looking controls */
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' \
					   version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' \
						language='*'\"")

#include <Windows.h>	

// Add new popup menu
#define ADDPOPUPMENU(hmenu, string) \
	HMENU hSubMenu = CreatePopupMenu(); \
	AppendMenu(hmenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, string);

// Add a menu item
#define ADDMENUITEM(hmenu, ID, string) \
	AppendMenu(hSubMenu, MF_STRING, ID, string);

enum 
{
	ID_FILE_EXIT,
	ID_FILE_MSG
};

/// Global variables
const char *clsName = "WinAPI";
char *title = "Windows API";
bool running = true;


void CreateMainMenu(HWND hWnd)
{
    HMENU hMenu = CreateMenu();

	ADDPOPUPMENU(hMenu, "&File");
	ADDMENUITEM(hMenu, ID_FILE_EXIT, "&Exit");
	ADDMENUITEM(hMenu, ID_FILE_MSG, "&Show message");

    SetMenu(hWnd, hMenu);
}

LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain( HINSTANCE hInstance,		
					HINSTANCE hPrevInstance,	
					LPSTR lpCmdLine,			
					int nCmdShow)				
{			
	WNDCLASSEX	WndEx;								
	MSG			msg;
	HWND		hWnd;

	// Set up the windows class structure
	WndEx.cbSize			= sizeof(WNDCLASSEX);					
	WndEx.style				= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;				
	WndEx.lpfnWndProc		= (WNDPROC)WndProc;							
	WndEx.cbClsExtra		= 0;								
	WndEx.cbWndExtra		= 0;									
	WndEx.hIcon				= LoadIcon(NULL, IDI_APPLICATION);	
	WndEx.hCursor			= LoadCursor(NULL, IDC_ARROW);		
	WndEx.hbrBackground		= NULL;								
	WndEx.lpszMenuName		= NULL;							
	WndEx.lpszClassName		= clsName;							
	WndEx.hInstance			= hInstance;										
	WndEx.hIconSm			= LoadIcon(NULL, IDI_APPLICATION);		

	// Register the windows class
	if (!RegisterClassEx(&WndEx))
	{
		MessageBox(NULL, "Failed to register class", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}

	// Create the window
	if (!(hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
								clsName,							
								title,								
								WS_OVERLAPPEDWINDOW |				
								WS_CLIPSIBLINGS | WS_CLIPCHILDREN,				
								CW_USEDEFAULT, CW_USEDEFAULT,		
								460, 340,							
								NULL,							
								NULL,								
								hInstance,							
								NULL)))								
	{	
		MessageBox(NULL, "Failed to create the window", "ERROR", MB_OK | MB_IConerror);
		return 0;
	}

	// Create the main menu
	CreateMainMenu(hWnd);

	// The window is initially hidden, we need to show it
	ShowWindow(hWnd, SW_SHOW);	

	// The main message loop of our program 
	while(running)								
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	
		{
			TranslateMessage(&msg);			
			DispatchMessage(&msg);			
		}
	}

	return msg.wParam;
}


LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)									
	{
		case WM_CLOSE:	
			DestroyWindow(hWnd);
			return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
			running = false;
			return 0;

		case WM_COMMAND:											
		{
			switch(LOWORD(wParam))
			{  
				case ID_FILE_EXIT:
					PostQuitMessage(0);
					running = false;
					break;

				case ID_FILE_MSG:
					MessageBox(hWnd, "Hello!", "Message", MB_ICONINFORMATION); 
					break;
			}

		} break;

		default:
			return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
}


// Add new popup menu
#define ADDPOPUPMENU(hmenu, string) \
	HMENU hSubMenu = CreatePopupMenu(); \
	AppendMenu(hmenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, string);

// Add a menu item
#define ADDMENUITEM(hmenu, ID, string) \
	AppendMenu(hSubMenu, MF_STRING, ID, string);


These macros are used to add pop up menues (such as the file menu) and items in those pop up menues such as "Exit". ADDPOPUPMENU takes a HMENU (Handle to the menu) as the first parameter and a string as its name. ADDMENUITEM takes a handle to the sub menu (created in ADDPOPUPMENU), the ID of the menu item to add and a string as its name.

enum 
{
	ID_FILE_EXIT,
	ID_FILE_MSG
};


These are the menu item ID's that are passed to ADDMENUITEM and caught in our message loop. Enumerating them just makes things more simple.

void CreateMainMenu(HWND hWnd)
{
    HMENU hMenu = CreateMenu();

	ADDPOPUPMENU(hMenu, "&File");
	ADDMENUITEM(hMenu, ID_FILE_EXIT, "&Exit");
	ADDMENUITEM(hMenu, ID_FILE_MSG, "&Show message");

    SetMenu(hWnd, hMenu);
}


It's pretty obvious what this does, it just sets up the main menu by first creating a Handle to the menu by calling CreateMenu, then using the macros we created to add a "File" menu with an "Exit" item and a "Show message" item. SetMenu is called to display the menu in the window.

		case WM_COMMAND:											
		{
			switch(LOWORD(wParam))
			{  
				case ID_FILE_EXIT:
					PostQuitMessage(0);
					running = false;
					break;

				case ID_FILE_MSG:
					MessageBox(hWnd, "Hello!", "Message", MB_ICONINFORMATION); 
					break;
			}

		} break;


This works in the same way as we did using resources, except i added a new menu item, "ID_FILE_MSG".

Conclusion

This tutorial went over the basic of resources and two ways in which we can create menues using the Windows API.

I hope it will be helpful to somebody, thanks for reading.

Is This A Good Question/Topic? 4
  • +

Page 1 of 1