Intro to the Windows API
Intro to the Windows API part 2: Creating a menu
In this tutorial, we will create a simple application which will demonstrate some of the more common controls in the Windows API. This application will allow us to input the title of the window using an edit control, and will look like this.

The code that we will use is losely based on code from earlier tutorials, except for a few additions, those being exception handling and an added header file to help organise our code better. Enough talking though, let's get straight to the code. I'll post the entire code here, then explain.
WinMain.h
#ifndef WINMAIN_H
#define WINMAIN_H
#include <Windows.h>
#include <exception>
/* 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='*'\"")
// 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
{
IDC_FILE_EXIT = 10000,
IDC_BUTTON_SHOW_MSG,
IDC_EDIT_CTRL,
IDC_STATIC_TEXT
};
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void CreateWin32Window(HINSTANCE hInstance, int width, int height);
#endif /* WINMAIN_H */
WinMain.cpp
#include "WinMain.h"
// Global variables
const char *clsName = "WinAPI";
char *title = "Windows API";
bool running = TRUE;
HWND hWnd = NULL;
/*
* Creates a menu and assigns it to the window
* specified by the HWND passed.
*/
void CreateMainMenu(HWND hWnd)
{
HMENU hMenu = CreateMenu();
ADDPOPUPMENU(hMenu, "&File");
ADDMENUITEM(hMenu, IDC_FILE_EXIT, "&Exit");
SetMenu(hWnd, hMenu);
}
/*
* Creates all the common controls and assigns
* them to the window specified by the HWND passed.
*/
void CreateControls(HWND hWnd, HINSTANCE hInstance)
{
HWND hStaticText = NULL;
HWND hButton = NULL;
HWND hEdit = NULL;
HWND hGroupBox = NULL;
// Create some static text
if ((hStaticText = CreateWindow( "Static",
"Type in the edit box to change the title",
WS_CHILD | WS_VISIBLE | SS_LEFT,
40,40, 640,20,
hWnd, NULL, hInstance, NULL)) == NULL)
{
throw std::exception("Failed to create controls");
}
// Create an edit control
if ((hEdit = CreateWindow( "Edit",
NULL,
WS_VISIBLE|WS_CHILD|WS_BORDER|ES_AUTOHSCROLL|ES_AUTOVSCROLL,
40,80,243,23,
hWnd, (HMENU)IDC_EDIT_CTRL,
hInstance, NULL)) == NULL)
{
throw std::exception("Failed to create controls");
}
// Create a group box control
if ((hGroupBox = CreateWindow( "Button",
"Title edit",
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
20, 10, 292, 185,
hWnd, NULL,
hInstance, NULL)) == NULL)
{
throw std::exception("Failed to create controls");
}
// Create a button control
if ((hButton = CreateWindow( "Button",
"Reset title",
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
95,130,120,28,
hWnd, (HMENU)IDC_BUTTON_SHOW_MSG,
hInstance, NULL)) == NULL)
{
throw std::exception("Failed to create controls");
}
// Create underlined text for the static control.
HFONT hFontBold = CreateFont( 16,0,
0,0,
560,FALSE,FALSE,FALSE,
ANSI_CHARSET,OUT_DEVICE_PRECIS,CLIP_MASK,
ANTIALIASED_QUALITY, DEFAULT_PITCH,
"MS Outlook");
// Use normal font for other controls.
HFONT hFont = CreateFont( 17,0,
0,0,
550,FALSE,FALSE,FALSE,
ANSI_CHARSET,OUT_DEVICE_PRECIS,CLIP_MASK,
ANTIALIASED_QUALITY, DEFAULT_PITCH,
"MS Outlook");
// Set the new font for the controls
SendMessage(hStaticText, WM_SETFONT, WPARAM (hFont), TRUE);
SendMessage(hGroupBox, WM_SETFONT, WPARAM (hFontBold), TRUE);
SendMessage(hButton, WM_SETFONT, WPARAM (hFont), TRUE);
SendMessage(hEdit, WM_SETFONT, WPARAM (hFont), TRUE);
}
/*
* Entry point of the application.
*/
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
try
{
// Create the main window
CreateWin32Window(hInstance, 340, 260);
// Create the main menu
CreateMainMenu(hWnd);
// Create controls
CreateControls(hWnd, hInstance);
}
catch (std::exception &e)
{
MessageBox(NULL, e.what(), "ERROR", MB_OK | MB_IConerror);
return EXIT_FAILURE;
}
// 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;
}
/*
* Creates a Win32 window
*/
void CreateWin32Window(HINSTANCE hInstance, int width, int height)
{
WNDCLASSEX WndEx;
WndEx.cbSize = sizeof(WNDCLASSEX);
WndEx.style = CS_HREDRAW | CS_VREDRAW;
WndEx.lpfnWndProc = (WNDPROC) WndProc;
WndEx.cbClsExtra = 0;
WndEx.cbWndExtra = 0;
WndEx.hIcon = LoadIcon(NULL, IDI_WINLOGO);
WndEx.hCursor = LoadCursor(NULL, IDC_ARROW);
WndEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndEx.lpszMenuName = NULL;
WndEx.lpszClassName = clsName;
WndEx.hInstance = hInstance;
WndEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Register the windows class
if (!RegisterClassEx(&WndEx))
{
throw std::exception("Window creation error: RegisterClassEx failed");
}
// Create the window
if (!(hWnd = CreateWindowEx(WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
clsName,
title,
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
width, height,
NULL,
NULL,
hInstance,
NULL)))
{
throw std::exception("Window creation error: CreateWindowEx failed");
}
}
/*
* Callback procedure to handle messages sent by the main window.
*/
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:
{
// The low word of wParam contains the menu ID.
switch(LOWORD(wParam))
{
case IDC_FILE_EXIT:
PostQuitMessage(0);
running = false;
break;
case IDC_BUTTON_SHOW_MSG:
SetWindowText(GetDlgItem(hWnd, IDC_EDIT_CTRL), NULL);
SetWindowText(hWnd, "Windows API");
MessageBox(hWnd, "Title reset", "Message", MB_ICONINFORMATION);
break;
}
// Has the user changed the text in the edit control?
if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_EDIT_CTRL)
{
static char text[256];
GetWindowText(GetDlgItem(hWnd, IDC_EDIT_CTRL), (LPSTR)text, 256);
SetWindowText(hWnd, (LPSTR)text);
}
} break;
// Make static and button control Bkgd transparent
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
{
SetBkMode((HDC)wParam, TRANSPARENT);
return (INT_PTR)(HBRUSH)GetStockObject(WHITE_BRUSH);
} break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
}
Creating the window
The window is created in a function called CreateWin32Window, which takes the HINSTANCE passed into WinMain and the width and height that we want our window to be. Inside of CreateWin32Window, things should look pretty familar, but the style of the window has been changed to make sure the user cannot resize it. Exception handling has also been added for good practice.
Creating the controls
The function CreateControls deals with the creation of all the controls which are present in the window, this function takes a Handle to the parents window (HWND) and an instance of the application (HISTANCE). The controls themselves are created using CreateWindow.
The first parameter of CreateWindow is a string containing the class name of the control we want to create, this basically describes the type of control that we want to use. For example, "Static" specifies a text control, and "Button" specifies a button control.
The second parameter is the title, but this may or may not be applicable, for example, a button may have a title, but an edit box does not.
The HMENU parameter specifies the ID of this control, we can use this for many purposes, such as getting a handle to the control, or catching messages it sends to the message loop.
CreateWindow also returns a handle to the created control, which is being assigned to a variable in this case.
In CreateControls, a font is specified for some of the controls. This is easy to do using CreateFont and is pretty self explanitory. Note that to assign the font to a control, a WM_SETFONT message is sent containing a HFONT in the WPARAM.
Intercepting the messages sent by the controls
In our message loop, you will notice a case for the IDC_BUTTON_SHOW_MSG ID. This message is sent when the user clicks out button, in which case we then reset the title of the window.
You may also notice the following code.
if (HIWORD(wParam) == EN_CHANGE && LOWORD(wParam) == IDC_EDIT_CTRL)
The purpose of this is to detect when a change has occured to the edit box, that is, the user has changed the text it contains. We react to this by simply grabbing the text from the text box and setting it to the title main window.
The WM_CTLCOLORBTN and WM_CTLCOLORSTATIC cases are for ensuring that our controls have a transparent background, so they don't stick out like a sore thumb.
Wrapping it up
This has been a pretty short tutorial, but there's not much more to say that hasen't already been said in the other tutorials.
Thanks for reading






MultiQuote






|