1. 程式人生 > >《Windows程式設計》讀書筆十 選單和其他資源

《Windows程式設計》讀書筆十 選單和其他資源

第十章  選單和資源

windows通過LoadIcon LoadCursor等函式來載入資源

圖示

滑鼠指標

字串

自定義資源

選單

鍵盤加速鍵

對話方塊

點陣圖

10.1  圖示,滑鼠指標,字串和自定義資源

10.1.1 向程式新增圖示

Tools->Options->Build->Export makefile when saving project file.

匯出mak檔案

注:該方法在VS2015中已經不可用。VS2015需要自己寫makefile或者使用makefile project來生成mak檔案,然後使用nmake編譯檔案。

nmake的配置方法參考

http://www.cnblogs.com/cvbnm/articles/1954872.html

原始碼

#include <windows.h>   
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static      TCHAR szAppName[] = TEXT("IconDemo");
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class      

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));//LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);  
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		TEXT("Icon Demo"),      //Window caption      
		WS_OVERLAPPEDWINDOW,            //Window Style      
		CW_USEDEFAULT,                  //initial x position      
		CW_USEDEFAULT,                  //initial y position      
		CW_USEDEFAULT,                  //initial x size      
		CW_USEDEFAULT,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters      

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HICON	hIcon;
	static int		cxIcon, cyIcon, cxClient, cyClient;
	HDC				hdc;
	HINSTANCE		hInstance;
	PAINTSTRUCT		ps;
	int				x, y;

	switch (message) //get the message      
	{
	case WM_CREATE:
		hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
		hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
		cxIcon = GetSystemMetrics(SM_CXICON);
		cyIcon = GetSystemMetrics(SM_CYICON);
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		for (y = 0; y < cyClient; y += cyIcon)
			for (x = 0; x < cxClient; x += cxIcon)
				DrawIcon(hdc, x, y, hIcon);

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

這段程式碼不能夠直接編譯

還需要建立資原始檔,使用AddItem來增加icondemo.rc檔案,VS會自動建立Resource.h標頭檔案。

使用Resource Editor中的Add Resource 新增圖示資源


原書中的圖示是32x32的,在VS2015和Win7時代已經支援256x256的超大圖示了

同時將ICON的ID改為IDI_ICON

然後可以編譯程式了

執行結果


SM_CXICON  和SM_CYICON 為32x32

更小的圖示是  SM_CXSMSIZE   和  SM_CYSMSIZE

10.1.2 獲得圖示的控制代碼

hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));

#define MAKEINTRESOURCE (i) (LPTSTR) ((DWORD)((WORD)(i)))

資源ID也可以直接使用字串

10.3.3  在應用程式中使用圖示

RegisterClassEx   使用  WNDCLASSEX結構

有額外欄位

ebSize   表示WNDCLASSEX結構大小

hIconSm  應該設定為小圖示的控制代碼。

因此你需要設定兩個圖示控制代碼, 一個標準一個是小

在程式執行時改變圖示,使用  SetClassLong函式實現。

SetClassLong(hwnd, GCL_HICON, LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ALTICON));

不想儲存圖示控制代碼可以使用一下函式

DrawIcon(hdc, x, y, GetClassLong(hwnd, GCL_HICON));

hIcon 是特例 生成的控制代碼不需要手動銷燬,

10.1.4   使用自定義滑鼠指標

和圖示類似,在資源編輯器中新增指標

然後

wndclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));

或者用文字名字來定義滑鼠指標

當滑鼠在基於此類建立的視窗上,自定義的滑鼠指標就會顯示出來。

更改子視窗的hCursor欄位

SetClassLong(hwndChild, GCL_HCURSOR, LoadCursor(hInstance, TEXT("childcursor"));

也可以使用SetCursor(hCursor);  來設定滑鼠指標

應該在WM_MOUSEMOVE時呼叫SetCursor 否則一旦移動滑鼠指標windows會重繪滑鼠指標

10.1.5 字串資源

使用資源管理器新建String Table

使用LoadString 來載入字串

LoadString(hInstance, id, szBuffer, iMaxLength);  支援c語言的格式設定符號

所有字串表都是以Unicode格式保持,LoadStringW直接載入Unicode文字,  LoadStringA則會進行內碼表轉換

10.1.6 自定義資源

hResource = LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_BINTYPE), TEXT("BINTYPE")));

需要訪問文字時

pData = LockResource(hResource);

使用完以後釋放

FreeResource(hResource);

一個使用圖示,字串和自定義資源的例子

poepoem.cpp

#include <windows.h>   
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

HINSTANCE hInst;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static      TCHAR szAppName[16], szCaption[64], szErrMsg[64];
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class   
	hInst = hInstance;

	LoadString(hInstance, IDS_APPNAME, szAppName, sizeof(szAppName) / sizeof(TCHAR));

	LoadString(hInstance, IDS_CAPTION, szCaption, sizeof(szCaption) / sizeof(TCHAR));

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(hInstance, szAppName);//LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		//Support the ANSI encode.
		LoadStringA(hInstance, IDS_APPNAME, (char*)szAppName, sizeof(szAppName));
		LoadStringA(hInstance, IDS_ERRMSG, (char*)szErrMsg, sizeof(szErrMsg));

		MessageBoxA(NULL, (char *)szErrMsg,
			(char *)szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		szCaption,      //Window caption      
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,            //Window Style      
		CW_USEDEFAULT,                  //initial x position      
		CW_USEDEFAULT,                  //initial y position      
		CW_USEDEFAULT,                  //initial x size      
		CW_USEDEFAULT,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters      

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static char*		pText;
	static HGLOBAL		hResource;
	static HWND			hScroll;
	static int			iPosition, cxChar, cyChar, cyClient, iNumLines, xScroll;
	HDC					hdc;
	PAINTSTRUCT			ps;
	RECT				rect;
	TEXTMETRIC			tm;
	int ilength;

	switch (message) //get the message      
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);
		GetTextMetrics(hdc, &tm);
		cxChar = tm.tmAveCharWidth;
		cyChar = tm.tmHeight + tm.tmExternalLeading;
		ReleaseDC(hwnd, hdc);

		xScroll = GetSystemMetrics(SM_CXVSCROLL);
		hScroll = CreateWindow(TEXT("scrollbar"), NULL,
			WS_CHILD | WS_VISIBLE | SBS_VERT,
			0, 0, 0, 0,
			hwnd, (HMENU)1, hInst, NULL);

		hResource = LoadResource(hInst, FindResource(hInst, TEXT("AnnabelLee"),
			TEXT("TEXT")));

		pText = (char *)LockResource(hResource);
		iNumLines = 0;

		while (*pText != TEXT('\\') && *pText != TEXT('\0'))
		{
			if (*pText == TEXT('\n'))
				iNumLines++;
			pText = AnsiNext(pText);
		}
		//*pText = TEXT('\0');

		SetScrollRange(hScroll, SB_CTL, 0, iNumLines, FALSE);
		SetScrollPos(hScroll, SB_CTL, 0, FALSE);
		return 0;

	case WM_SIZE:
		MoveWindow(hScroll, LOWORD(lParam) - xScroll, 0,
			xScroll, cyClient = HIWORD(lParam), TRUE);
		SetFocus(hwnd);
		return 0;

	case WM_SETFOCUS:
		SetFocus(hScroll);
		return 0;

	case WM_VSCROLL:
		switch (wParam)
		{
		case SB_TOP:
			iPosition = 0;
			break;
		case SB_BOTTOM:
			iPosition = iNumLines;
			break;
		case SB_LINEUP:
			iPosition -= 1;
			break;
		case SB_LINEDOWN:
			iPosition += 1;
			break;
		case SB_PAGEUP:
			iPosition -= cyClient / cyChar;
			break;
		case SB_PAGEDOWN:
			iPosition += cyClient / cyChar;
			break;
		case SB_THUMBPOSITION:
		case SB_THUMBTRACK:
			iPosition = LOWORD(lParam);
			break;
		}
		iPosition = max(0, min(iPosition, iNumLines));

		if (iPosition != GetScrollPos(hScroll, SB_CTL))
		{
			SetScrollPos(hScroll, SB_CTL, iPosition, TRUE);
			InvalidateRect(hwnd, NULL, TRUE);
		}
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		pText = (char*)LockResource(hResource);

		GetClientRect(hwnd, &rect);
		rect.left += cxChar;
		rect.top += cyChar * (1 - iPosition);
		DrawTextA(hdc, pText, -1, &rect, DT_EXTERNALLEADING);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		FreeResource(hResource);
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}


poepoem.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Chinese (Simplified, PRC) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
PoePoem                 ICON                    "POEPOEM.ICO"


/////////////////////////////////////////////////////////////////////////////
//
// TEXT
//
ANNABELLEE              TEXT    DISCARDABLE     "poepoem.txt"

/////////////////////////////////////////////////////////////////////////////
//
// String Table
//

STRINGTABLE
BEGIN
    IDS_APPNAME             "PoePoem"
    IDS_CAPTION             """Annabel Lee"" by Edgar Allan Poe"
    IDS_ERRMSG              "This program requires Windows NT!"
END

#endif    // Chinese (Simplified, PRC) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by poepoem.rc
//
#define IDS_APPNAME                     102
#define IDS_CAPTION                     103
#define IDS_ERRMSG                      104

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

執行結果


10.2.1和選單有關的概念

主選單or頂級選單

子選單or 下拉選單

彈出選單可以被選中,頂級選單不能被選中

選單可以被啟用,或禁用。 活動 ,非活動。

10.2.2 選單結構

每個選單有三個特徵定義。  選單顯示什麼, 文字或點陣圖

ID號或者指向選單的控制代碼

選單的屬性,是否禁用或選中

10.2.3 定義選單

使用&F  windows會使用ALT+F 啟用選單

gray  選單變灰

enabled  文字變灰

checked  選單可選

Separator 選項會繪製一條水平的分割線

\t後面的文字會被放置在右邊足夠遠的新欄中

\a 會使後面的文字右對齊

指定ID值是windows在選單訊息傳送給視窗過程的數字。ID值在一個選單中應該是唯一的。

使用IDM_XXX 開頭

10.2.4 在程式中引用選單

wndClass.lpszMenuName = szAppName; //給選單指定一個和程式一樣的名字選單資源的ID

或者

hMenu = LoadMenu(hInstance, TEXT("MyMenu"));

或者

hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(ID_MENU));

作為CreateWindow的引數hMenu

CreateWindow中指定的選單會覆蓋視窗類中指定的任何選單。如果第九個引數為NULL 則基於視窗類中指定的選單建立視窗。

也可以在視窗建立後

SetMenu(hwnd, hMenu); 來設定選單

動態改變視窗

當視窗被銷燬時,附加到該視窗的任何選單也將被銷燬。而在程式結束前,任何沒有附加到視窗的選單應該通過DestoryMenu呼叫被顯示銷燬(DestroyIcon, DestroyCursor等 銷燬自己建立的其他資源控制代碼,自定義資源使用FreeResource)

10.2.5 選單和訊息

當用戶選擇選單windows會像視窗過程傳送幾個不同訊息。多數情況下,應用程式可以忽略大部分交給DefWindowProc處理

有一個訊息如下WM_INITMENU

wParam   主選單控制代碼

lParam    0

即使使用者選擇了第一項,wParam也是主選單的控制代碼。

WM_MENUSELECT 訊息。移動滑鼠時,這對實現狀態列非常有用

LOWORD(wParam) 所選的選單項, 選單ID或者彈出選單的索引

HIWORD(wParam) 所選的標記

lParam    鎖選選單項的控制代碼

選中標記可以是, MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP, MF_POPUP, MF_HELP, MF_SYSMENU 或MF_MOUSESELECT.

windows 要顯示彈出選單,會像視窗過程傳送一個帶有如下引數的WM_INITMENUPOPUP

wParam   彈出選單的控制代碼

LOWORD(lParam) 彈出選單的索引

HIWORD(lParam) 1代表系統選單,0代表其他選單

最重要的是WM_COMMAND 

如果子選單和子視窗控制元件使用相同的ID

檢查lParam 對於視窗,該值是0


WM_SYSCOMMAND 類似於WM_COMMAND 不過他表示使用者從系統選單選擇了一個啟用的選單項

wParam   選單ID

lParam    0

如果WM_SYSCOMMAND是滑鼠單擊的結果,LOWORD(lParam) HIWORD(lParam)將包含滑鼠指標的x和y座標

系統選單

SC_SIZE, SC_MOVE  SC_MINIMIZE  SC_MAXIMIZE  SC_NEXTWINDOW, SC_PREVWINDOW, SC_CLOSE< SC_VSCROLL, SC_HSCROLL, SC_ARRANGE,

SC_RESTORE,  SC_TASKLIST.

WM_MENUCHAR 按下Alt和不對應於任何選單項的字元犍  或者   彈出選單時,使用者按了一個不對應任何彈出選單項的字元鍵

LOWORD(wParam)    字元碼

HIWORD(wParam)    選擇碼

lParam    選單控制代碼

選擇碼如下

0  沒有彈出選單顯示

MF_POPUP 彈出選單被顯示

MF_SYSMENU 系統彈出選單被顯示

一般程式會把改訊息傳給DefWindowProc, 然後DefWindowProc返回0, 使得windows發出嘟嘟聲。

10.2.6 範例程式

menudemo.cpp

#include <windows.h>   
#include "resource.h"

#define ID_TIMER	1

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

TCHAR szAppName[] = TEXT("MenuDemo");

HINSTANCE hInst;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class   
	hInst = hInstance;

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = szAppName;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		TEXT("Menu Demonstration"),      //Window caption      
		WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,            //Window Style      
		CW_USEDEFAULT,                  //initial x position      
		CW_USEDEFAULT,                  //initial y position      
		CW_USEDEFAULT,                  //initial x size      
		CW_USEDEFAULT,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters      

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int	idColor[5] = {WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, 
								  DKGRAY_BRUSH, BLACK_BRUSH};
	static int	iSelection = IDM_BKGND_WHITE;
	HMENU		hMenu;

	switch (message) //get the message      
	{
	case WM_COMMAND:
		hMenu = GetMenu(hwnd);

		switch (LOWORD(wParam))
		{
		case IDM_FILE_NEW:
		case IDM_FILE_OPEN:
		case IDM_FILE_SAVE:
		case IDM_FILE_SAVE_AS:
			MessageBeep(0);
			return 0;

		case IDM_FILE_EXIT:
			SendMessage(hwnd, WM_CLOSE, 0, 0);

		case IDM_EDIT_UNDO:
		case IDM_EDIT_CUT:
		case IDM_EDIT_COPY:
		case IDM_EDIT_PASTE:
		case IDM_EDIT_CLEAR:
			MessageBeep(0);
			return 0;

		//The logic below assumes that IDM_WHITE through IDM_BLACK 
		//are consecutive numbers in the order show here.
		case IDM_BKGND_WHITE:
		case IDM_BKGND_LTGRAY:
		case IDM_BKGND_GRAY:
		case IDM_BKGND_DKGRAY:
		case IDM_BKGND_BLACK:
			CheckMenuItem(hMenu, iSelection, MF_UNCHECKED);
			iSelection = LOWORD(wParam);
			CheckMenuItem(hMenu, iSelection, MF_CHECKED);

			SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
				GetStockObject(idColor[LOWORD(wParam) - IDM_BKGND_WHITE]));

			InvalidateRect(hwnd, NULL ,TRUE);
			return 0;

		case IDM_TIMER_START:
			if (SetTimer(hwnd, ID_TIMER, 1000, NULL))
			{
				EnableMenuItem(hMenu, IDM_TIMER_START, MF_GRAYED);
				EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_ENABLED);
			}
			return 0;

		case IDM_TIMER_STOP:
			KillTimer(hwnd, ID_TIMER);
			EnableMenuItem(hMenu, IDM_TIMER_START, MF_ENABLED);
			EnableMenuItem(hMenu, IDM_TIMER_STOP, MF_GRAYED);
			return 0;

		case IDM_APP_HELP:
			MessageBox(hwnd, TEXT("Help not yet implemented!"),
				szAppName, MB_ICONEXCLAMATION | MB_OK);
			return 0;

		case IDM_APP_ABOUT:
			MessageBox(hwnd, TEXT("Menu Demonstration Program\n")
				TEXT("(c) Charles Petzold, 1998"),
				szAppName, MB_ICONINFORMATION | MB_OK);
			return 0;
		}

		break;

	case WM_TIMER:
		MessageBeep(0);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}


menudemo.rc

//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

MENUDEMO MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&New",                        ID_MENUITEM40020
        MENUITEM "&Open",                       IDM_FILE_OPEN
        MENUITEM "&Save",                       IDM_FILE_SAVE
        MENUITEM "Save &As...",                 IDM_FILE_SAVE_AS
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_APP_EXIT
    END
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo",                       IDM_EDIT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "C&ut",                        IDM_EDIT_CUT
        MENUITEM "&Copy",                       IDM_EDIT_COPY
        MENUITEM "&Paste",                      IDM_EDIT_PASTE
        MENUITEM "De&lete",                     IDM_EDIT_CLEAR
    END
    POPUP "&Background"
    BEGIN
        MENUITEM "&White",                      IDM_BKGND_WHITE, CHECKED
        MENUITEM "&Light Gray",                 IDM_BKGND_LTGRAY
        MENUITEM "&Gray",                       IDM_BKGND_GRAY
        MENUITEM "&Dark Gray",                  IDM_BKGND_DKGRAY
        MENUITEM "&Black",                      IDM_BKGND_BLACK
    END
    POPUP "&Timer"
    BEGIN
        MENUITEM "&Start",                      IDM_TIMER_START
        MENUITEM "S&top",                       IDM_TIMER_STOP, GRAYED
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&Help...",                    IDM_APP_HELP
        MENUITEM "&About MenuDemo...",          IDM_APP_ABOUT
    END
END

#endif    // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by menudemo.rc
//
#define IDM_FILE_NEW                    40001
#define IDM_FILE_OPEN                   40002
#define IDM_FILE_SAVE                   40003
#define IDM_FILE_SAVE_AS                40004
#define IDM_FILE_EXIT                   40005
#define IDM_EDIT_UNDO                   40006
#define IDM_EDIT_CUT                    40007
#define IDM_EDIT_COPY                   40008
#define IDM_Menu                        40009
#define IDM_EDIT_CLEAR                  40010
#define IDM_BKGND_WHITE                 40011
#define IDM_BKGND_LTGRAY                40012
#define IDM_BKGND_GRAY                  40013
#define IDM_BKGND_DKGRAY                40014
#define IDM_BKGND_BLACK                 40015
#define IDM_TIMER_START                 40016
#define IDM_TIMER_STOP                  40017
#define IDM_APP_HELP                    40018
#define IDM_APP_ABOUT                   40019
#define IDM_EDIT_PASTE                  40039

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40040
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

執行結果如下


10.2.7 選單設計中的規範

10.2.8 定義選單的繁瑣方式

可以不適用資源指令碼而使用CreateMenu和AppendMenu 動態建立選單。完成定以後可以把選單控制代碼傳遞給CreateWindow 或者使用SetMenu來設定視窗選單。

hMenu = CreateMenu();

對於頂級選單和彈出選單必須使用不同的控制代碼

hMenuPop = CreateMenu();

AppendMenu(hMenuPopup, MF_STRING, IDM_FILE_NEW, "&NEW");

...

AppendMenu(hMenu, MF_POPUP, hMenuPopup, "&File");

hMenuPop = CreateMenu();

...

具體例子參考截圖



第三種方法,  使用LoadMenuIndirect函式接受一個指向MENUITEMPLATE型別的結構指標,並返回一個指向選單的控制代碼。

10.2.9 浮動彈出選單

popmenu.cpp

#include <windows.h>   
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

HINSTANCE	hInst;
TCHAR		szAppName[] = TEXT("PopMenu");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class   
	hInst = hInstance;

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		TEXT("PopupMenu Demonstration"),      //Window caption      
		WS_OVERLAPPEDWINDOW,            //Window Style      
		CW_USEDEFAULT,                  //initial x position      
		CW_USEDEFAULT,                  //initial y position      
		CW_USEDEFAULT,                  //initial x size      
		CW_USEDEFAULT,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters      

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HMENU	hMenu;
	static int		idColor[5] = { WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,
		DKGRAY_BRUSH, BLACK_BRUSH };
	static int		iSelection = IDM_BKGND_WHITE;
	POINT			point;


	switch (message) //get the message      
	{
	case WM_CREATE:
		hMenu = LoadMenu(hInst, szAppName);
		hMenu = GetSubMenu(hMenu, 0);
		return 0;

	case WM_RBUTTONUP:
		point.x = LOWORD(lParam);
		point.y = HIWORD(lParam);
		ClientToScreen(hwnd, &point);

		TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, point.x, point.y,
			0, hwnd, NULL);
		return 0;
	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_FILE_NEW:
		case IDM_FILE_OPEN:
		case IDM_FILE_SAVE:
		case IDM_FILE_SAVE_AS:
			MessageBeep(0);
			return 0;

		case IDM_APP_EXIT:
			SendMessage(hwnd, WM_CLOSE, 0, 0);

		case IDM_EDIT_UNDO:
		case IDM_EDIT_CUT:
		case IDM_EDIT_COPY:
		case IDM_EDIT_PASTE:
		case IDM_EDIT_CLEAR:
			MessageBeep(0);
			return 0;

			//The logic below assumes that IDM_WHITE through IDM_BLACK 
			//are consecutive numbers in the order show here.
		case IDM_BKGND_WHITE:
		case IDM_BKGND_LTGRAY:
		case IDM_BKGND_GRAY:
		case IDM_BKGND_DKGRAY:
		case IDM_BKGND_BLACK:
			CheckMenuItem(hMenu, iSelection, MF_UNCHECKED);
			iSelection = LOWORD(wParam);
			CheckMenuItem(hMenu, iSelection, MF_CHECKED);

			SetClassLong(hwnd, GCL_HBRBACKGROUND, (LONG)
				GetStockObject(idColor[LOWORD(wParam) - IDM_BKGND_WHITE]));

			InvalidateRect(hwnd, NULL, TRUE);
			return 0;

		case IDM_APP_HELP:
			MessageBox(hwnd, TEXT("Help not yet implemented!"),
				szAppName, MB_ICONEXCLAMATION | MB_OK);
			return 0;

		case IDM_APP_ABOUT:
			MessageBox(hwnd, TEXT("Menu Demonstration Program\n")
				TEXT("(c) Charles Petzold, 1998"),
				szAppName, MB_ICONINFORMATION | MB_OK);
			return 0;
		}

		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

popmenu.rc
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

POPMENU MENU DISCARDABLE 
BEGIN
    POPUP "MyMenu"
    BEGIN
        POPUP "&File"
        BEGIN
            MENUITEM "&New",                        IDM_FILE_NEW
            MENUITEM "&Open",                       IDM_FILE_OPEN
            MENUITEM "&Save",                       IDM_FILE_SAVE
            MENUITEM "Save &As",                    IDM_FILE_SAVE_AS
            MENUITEM SEPARATOR
            MENUITEM "E&xit",                       IDM_APP_EXIT
        END
        POPUP "&Edit"
        BEGIN
            MENUITEM "&Undo",                       IDM_EDIT_UNDO
            MENUITEM SEPARATOR
            MENUITEM "Cu&t",                        IDM_EDIT_CUT
            MENUITEM "&Copy",                       IDM_EDIT_COPY
            MENUITEM "&Paste",                      IDM_EDIT_PASTE
            MENUITEM "De&lete",                     IDM_EDIT_CLEAR
        END
        POPUP "&Background"
        BEGIN
            MENUITEM "&White",                      IDM_BKGND_WHITE, CHECKED
            MENUITEM "&Light Gray",                 IDM_BKGND_LTGRAY
            MENUITEM "&Gray",                       IDM_BKGND_GRAY
            MENUITEM "&Dark Gray",                  IDM_BKGND_DKGRAY
            MENUITEM "&Black",                      IDM_BKGND_BLACK
        END
        POPUP "&Help"
        BEGIN
            MENUITEM "&Help...",                    IDM_APP_HELP
            MENUITEM "&About PopMenu...",           IDM_APP_ABOUT
        END
    END
END

#endif    // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by PopMenu.rc
//
#define IDM_FILE_NEW                    40001
#define IDM_FILE_OPEN                   40002
#define IDM_FILE_SAVE                   40003
#define IDM_FILE_SAVE_AS                40004
#define IDM_APP_EXIT                    40005
#define IDM_EDIT_UNDO                   40006
#define IDM_EDIT_CUT                    40007
#define IDM_EDIT_COPY                   40008
#define IDM_EDIT_PASTE                  40009
#define IDM_EDIT_CLEAR                  40010
#define IDM_BKGND_WHITE                 40011
#define IDM_BKGND_LTGRAY                40012
#define IDM_BKGND_GRAY                  40013
#define IDM_BKGND_DKGRAY                40014
#define IDM_BKGND_BLACK                 40015
#define IDM_HELP_HELP                   40016
#define IDM_APP_HELP                    40016
#define IDM_APP_ABOUT                   40017

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40018
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

執行結果


10.2.10 使用系統選單

包含WS_SYSMENU樣式建立視窗,在標題欄左邊會有一個系統該選單。

系統選單加入ID號必須小於0xF000. 否則會和windows標準命令的ID衝突

並且處理完WM_SYSCOMMAND訊息要把其他訊息傳遞給DefWindowProc

#include <windows.h>   

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

#define IDM_SYS_ABOUT	1
#define IDM_SYS_HELP	2
#define IDM_SYS_REMOVE	3

static	TCHAR	szAppName[] = TEXT("PoorMenu");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	HMENU		hMenu;
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class   

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		TEXT("The Poor-Person's Menu"),      //Window caption      
		WS_OVERLAPPEDWINDOW,            //Window Style      
		CW_USEDEFAULT,                  //initial x position      
		CW_USEDEFAULT,                  //initial y position      
		CW_USEDEFAULT,                  //initial x size      
		CW_USEDEFAULT,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters   

	hMenu = GetSystemMenu(hwnd, FALSE);

	AppendMenu(hMenu, MF_SEPARATOR,	0,					NULL);
	AppendMenu(hMenu, MF_STRING,	IDM_SYS_ABOUT,		TEXT("About..."));
	AppendMenu(hMenu, MF_STRING,	IDM_SYS_HELP,		TEXT("Help..."));
	AppendMenu(hMenu, MF_STRING,	IDM_SYS_REMOVE,		TEXT("Remove Additions"));

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) //get the message      
	{
	case WM_SYSCOMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_SYS_ABOUT:
			MessageBox(hwnd, TEXT("Menu Demonstration Program\n")
				TEXT("(c) Charles Petzold, 1998"),
				szAppName, MB_ICONINFORMATION | MB_OK);
			return 0;

		case IDM_SYS_HELP:
			MessageBox(hwnd, TEXT("Help not yet implemented!"),
				szAppName, MB_ICONEXCLAMATION | MB_OK);
			return 0;

		case IDM_SYS_REMOVE:
			GetSystemMenu(hwnd, TRUE);
			return 0;
		}
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}
GetSystemMenu(hwnd, FALSE)  表明系統選單將要被修改

GetSystemMenu(hwnd, TRUE); 還原被修改的系統選單

10.2.11 改變選單

windows3.0 以前使用ChangeMenu

現在使用 

AppendMenu 在選單末尾追加一個新選單項

DeleteMenu 從選單中刪除已存在選單項並銷燬他

InsertMenu 像選單中插入一個新選單

ModifyMenu  修改一個已經存在的選單項

RemoveMenu 從選單中去除一個已有的選單項

DeleteMenu 會銷燬該彈出選單而Remove不會。

10.2.12 其他選單命令
改變一個頂級選單以後,知道windows重繪選單才能顯示。

DrawMenuBar(hwnd) 強制重繪選單

hwnd為視窗控制代碼

hMenuPopup = GetSubMenu(hMenu, iPosition);  //獲得彈出選單的控制代碼

iPosition為彈出選單在頂級選單中的索引

iCount = GetMenuItemCount(hMenu) //頂級選單或彈出選單中現有選單項的數目

id = GetMenuItemID(hMenuPopup, iPosition) // 獲得彈出選單中某個選單項的ID

CheckMenuItem(hMenu, id, iCheck); 在彈出選單中選擇和取消選擇某一選單項 MF_CHECKED 或 MF_UNCHECKED  hMenu是頂級選單控制代碼

如果hMenu是彈出選單,那麼id可以使iPosition

CheckMenuItem(hMenu, iPosition, MF_CHECKED | MF_BYPOSITION);  使用索引而非ID

EnableMenuItem(hMenu, id, MF_ENABLED);

第三個引數可以是  MF_ENABLED, MF_DISABLED, MF_GRAYED.   如果在頂級選單上使用,並且改選單項還有子選單。那麼第三個引數必須使用MF_BYPOSITION

HiliteMenuItem       MF_HILITE   MF_UNHILITE 加亮顯示 

iFlags = GetMenuString(hMenu, id, pString, iMaxCount, iFlag)   iFlag 可以使MF_BYCOMMAND 或者MF_BYPOSITION   //獲得選單項的string

iFlags = GetMenuState(hMenu, id, iFlag)   iFlag 是 MF_COMMAND 或者MF_BYPOSITION。 iFlags是當前標識的組合值

可能是 MF_CHECKED, MF_SIABLED, MF_GRAYED, MF_MENUBREAK, MF_MENUUNBREAK, MF_SEPARATOR

Destory(hMenu) 銷燬選單,使得該選單控制代碼無效

10.2.13選單的另類用法

#include <windows.h>   
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR	szAppName[] = TEXT("NoPopUps");
	HWND			hwnd;
	MSG				msg;
	WNDCLASS		wndClass;       //The window Class   

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		TEXT("No-Popup Nested Menu Demonstration"),      //Window caption      
		WS_OVERLAPPEDWINDOW,            //Window Style      
		CW_USEDEFAULT,                  //initial x position      
		CW_USEDEFAULT,                  //initial y position      
		CW_USEDEFAULT,                  //initial x size      
		CW_USEDEFAULT,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters      

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;

}

//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HMENU	hMenuMain, hMenuEdit, hMenuFile;
	HINSTANCE		hInstance;

	switch (message) //get the message      
	{
	case WM_CREATE:
		hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);

		hMenuMain = LoadMenu(hInstance, TEXT("MenuMain"));
		hMenuFile = LoadMenu(hInstance, TEXT("MenuFile"));
		hMenuEdit = LoadMenu(hInstance, TEXT("MenuEdit"));
		
		SetMenu(hwnd, hMenuMain);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_MAIN:
			SetMenu(hwnd, hMenuMain);
			return 0;

		case IDM_FILE:
			SetMenu(hwnd, hMenuFile);
			return 0;

		case IDM_EDIT:
			SetMenu(hwnd, hMenuEdit);

		case IDM_FILE_NEW:
		case IDM_FILE_OPEN:
		case IDM_FILE_SAVE:
		case IDM_FILE_SAVE_AS:
		case IDM_EDIT_UNDO:
		case IDM_EDIT_CUT:
		case IDM_EDIT_COPY:
		case IDM_EDIT_PASTE:
		case IDM_EDIT_CLEAR:
			MessageBeep(0);
			return 0;
		}

		break;

	case WM_DESTROY:
		SetMenu(hwnd, hMenuMain);
		DestroyMenu(hMenuFile);
		DestroyMenu(hMenuEdit);
		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

NOPOPUPS.rc
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Chinese (Simplified, PRC) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

MENUMAIN MENU
BEGIN
    MENUITEM "MAIN:",                       0, INACTIVE
    MENUITEM "&File...",                    IDM_FILE
    MENUITEM "&Edit...",                    IDM_EDIT
END

MENUFILE MENU
BEGIN
    MENUITEM "FILE:",                       0, INACTIVE
    MENUITEM "&New",                        IDM_FILE_NEW
    MENUITEM "&Open...",                    IDM_FILE_OPEN
    MENUITEM "&Save",                       IDM_FILE_SAVE
    MENUITEM "Save &As",                    IDM_FILE_SAVE_AS
    MENUITEM "(&Main)",                     IDM_MAIN
END

MENUEDIT MENU
BEGIN
    MENUITEM "EDIT:",                       ID_EDIT, INACTIVE
    MENUITEM "&Undo",                       IDM_EDIT_UNDO
    MENUITEM "Cu&t",                        IDM_EDIT_CUT
    MENUITEM "&Copy",                       IDM_EDIT_COPY
    MENUITEM "&Paste",                      IDM_EDIT_PASTE
    MENUITEM "De&lete",                     IDM_EDIT_CLEAR
    MENUITEM "(&Main)",                     IDM_MAIN
END

#endif    // Chinese (Simplified, PRC) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by nopopups.rc
//
#define IDM_FILE                        40001
#define IDM_EDIT                        40002
#define IDM_FILE_NEW                    40003
#define IDM_FILE_OPEN                   40004
#define IDM_FILE_SAVE                   40005
#define IDM_FILE_SAVE_AS                40006
#define IDM_MAIN                        40007
#define IDM_EDIT_UNDO                   40008
#define IDM_EDIT_CUT                    40009
#define IDM_EDIT_COPY                   40010
#define IDM_EDIT_PASTE                  40011
#define IDM_EDIT_CLEAR                  40012
#define ID_EDIT                         40013

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40014
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
執行結果



10.3 鍵盤加速

可以生成WM_COMMAND  或者有時候是 WM_SYSCOMMAND

10.3.1 為什麼你應該使用鍵盤加速鍵

windows會把WM_COMMAND訊息傳送給在windows函式TranslateAccelerator中指定的視窗過程,多視窗程式設計中非常重要

10.3.2 指定加速鍵的一些原則



常見加速鍵 F1  幫助,  F4~F6 用在多文件介面

10.3.3 加速鍵表

作為一種資源載入  每個加速鍵有一個ID和一個擊鍵組合

可以使虛擬程式碼或者ascii字元與shift, ctrl 或alt的組合。

Tab字元能把文字和加速犍分開,這樣加速鍵會排在第二列。

可以使用文字在選單項後面說明 例如  (Shift+F6)

10.3.4 載入加速鍵列表

使用LoadAccelerators 載入

HANDLE hAccel;

hAccel = LoadAccelerators(hInstance, TEXT("MyAccelerators"));

也可以使用 MAKEINTRESOURCE 使用ID, 或者使用 #數字

10.3.5  鍵盤按鍵

修改訊息迴圈

	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(hwnd, hAccel, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}	
	}
TranslateAccelerator 會優先判斷是否有加速鍵訊息,然後發給對於的視窗過程

當該返回返回0,說明訊息已經被翻譯過,不需要再處理。繼續嚇一條訊息

模態對話方塊或訊息框有輸入焦點時,TranslateAccelerator不翻譯鍵盤訊息,因為這些視窗的訊息不通過程式的訊息迴圈。

有些時候應用程式另一視窗擁有輸入焦點時,你不想翻譯鍵盤訊息。如何處理該情況參考11章

10.3.6 接受加速鍵訊息

對應系統選單項傳送  WM_SYSCOMMAND

否則傳送WM_COMMAND

WM_COMMAND 訊息

如果加速鍵對應某個選單項,視窗過程還會接收到WM_INITMENU  WM_INITMENUPOPUP   和  WM_MENUSELECT

在處理WM_INITMENUPOPUP時,可以啟用或禁用彈出選單的選單項

當一個選單項變灰,加速鍵不會對其傳送WM_COMMAND 或者WM_SYSOMMAND

如果視窗最小化,對映到系統選單的鍵盤加速鍵會對視窗傳送WM_SYSCOMMAND

沒對映到系統選單項的加速鍵,TranslateAccelerator也會想視窗過程傳送WM_COMMAND訊息

10.3.7 帶有選單項的POPPAD

#include <windows.h>
#include "resource.h"

#define ID_EDIT 1  

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.    

TCHAR   szAppName[] = TEXT("PopPad2");

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	PSTR szCmdLine, int iCmdShow)
{
	//static      TCHAR szAppName[] = TEXT("Colors1");  
	HACCEL		hAccel;
	HWND        hwnd;
	MSG         msg;
	WNDCLASS    wndClass;       //The window Class      

	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.      
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = hInstance;
	wndClass.hIcon = LoadIcon(hInstance, szAppName);// LoadIcon(NULL, IDI_APPLICATION);
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = CreateSolidBrush(0);//(HBRUSH)GetStockObject(WHITE_BRUSH);  
	wndClass.lpszMenuName = szAppName;
	wndClass.lpszClassName = szAppName;

	//Register the Window Class to the Windows System.       
	if (!RegisterClass(&wndClass))
	{
		MessageBox(NULL, TEXT("This program require Windows NT!"),
			szAppName, MB_ICONERROR);
		return 0;
	}

	//This function will generate an WM_CREATE message.      
	hwnd = CreateWindow(szAppName,      //Window class name      
		szAppName,      //Window caption      
		WS_OVERLAPPEDWINDOW,            //Window Style      
		GetSystemMetrics(SM_CXSCREEN) / 4,                  //initial x position      
		GetSystemMetrics(SM_CYSCREEN) / 4,                  //initial y position      
		GetSystemMetrics(SM_CXSCREEN) / 2,                  //initial x size      
		GetSystemMetrics(SM_CYSCREEN) / 2,                  //initial y size      
		NULL,                           //parent window handle      
		NULL,                           //window menu handle      
		hInstance,                      //program instance handle      
		NULL);                          //creation parameters      

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd); //This function will generate a WM_PAINT message.      

	hAccel = LoadAccelerators(hInstance, szAppName);

						/* The message loop for this program.
						if received the WM_QUIT message, the function will return 0.*/
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(hwnd, hAccel, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;

}

int AskConfirmation(HWND hwnd)
{
	return MessageBox(hwnd, TEXT("Really want to close PopPad2?"),
		szAppName, MB_YESNO | MB_ICONQUESTION);
}
//define the Window Procedure WndProc      
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndEdit;
	int			iSelect, iEnable;

	switch (message) //get the message      
	{
	case WM_CREATE:
		hwndEdit = CreateWindow(TEXT("edit"), NULL,
			WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
			WS_BORDER | ES_LEFT | ES_MULTILINE |
			ES_AUTOHSCROLL | ES_AUTOVSCROLL,
			0, 0, 0, 0,
			hwnd, (HMENU)ID_EDIT,
			((LPCREATESTRUCT)lParam)->hInstance, NULL);

		return 0;

	case WM_SETFOCUS:
		SetFocus(hwndEdit);
		return 0;

	case WM_SIZE:
		MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_INITMENUPOPUP:
		if (lParam == 1)
		{
			EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO,
				SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ?
				MF_ENABLED : MF_GRAYED);

			EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,
				IsClipboardFormatAvailable(CF_TEXT) ?
				MF_ENABLED : MF_GRAYED);

			iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0);

			if (HIWORD(iSelect) == LOWORD(iSelect))
				iEnable = MF_GRAYED;
			else
				iEnable = MF_ENABLED;

			EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, iEnable);
			EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, iEnable);
			EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, iEnable);
		}
	case WM_COMMAND:
		if (lParam)
		{
			if (LOWORD(wParam) == ID_EDIT)
				if (HIWORD(wParam) == EN_ERRSPACE ||
					HIWORD(wParam) == EN_MAXTEXT)
					MessageBox(hwnd, TEXT("Edit control out of spae."),
						szAppName, MB_OK | MB_ICONSTOP);
		}
		else switch (LOWORD(wParam))
		{
		case IDM_FILE_NEW:
		case IDM_FILE_OPEN:
		case IDM_FILE_SAVE:
		case IDM_FILE_SAVE_AS:
		case IDM_FILE_PRINT:
			MessageBeep(0);
			return 0;

		case IDM_APP_EXIT:
			SendMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;

		case IDM_EDIT_UNDO:
			SendMessage(hwndEdit, WM_UNDO, 0, 0);
			return 0;

		case IDM_EDIT_CUT:
			SendMessage(hwndEdit, WM_CUT, 0, 0);
			return 0;

		case IDM_EDIT_COPY:
			SendMessage(hwndEdit, WM_COPY, 0, 0);
			return 0;

		case IDM_EDIT_PASTE:
			SendMessage(hwndEdit, WM_PASTE, 0, 0);
			return 0;

		case IDM_EDIT_CLEAR:
			SendMessage(hwndEdit, WM_CLEAR, 0, 0);
			return 0;

		case IDM_EDIT_SELECT_ALL:
			SendMessage(hwndEdit, EM_SETSEL, 0, -1);
			return 0;

		case IDM_HELP_HELP:
			MessageBox(hwnd, TEXT("Help not yet implemented!"),
				szAppName, MB_ICONEXCLAMATION | MB_OK);
			return 0;

		case IDM_APP_ABOUT:
			MessageBox(hwnd, TEXT("Menu Demonstration Program\n")
				TEXT("(c) Charles Petzold, 1998"),
				szAppName, MB_ICONINFORMATION | MB_OK);
			return 0;

		}
		
		return 0;

	case WM_CLOSE:
		if (IDYES == AskConfirmation(hwnd))
			DestroyWindow(hwnd);
		return 0;

	case WM_QUERYENDSESSION:
		if (IDYES == AskConfirmation(hwnd))
			return 1;
		else
			return 0;

	case WM_DESTROY:

		PostQuitMessage(0);
		return 0;
	}
	return  DefWindowProc(hwnd, message, wParam, lParam);
}

POPPAD.rc
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Chinese (Simplified, PRC) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

POPPAD2 MENU
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&New",                        IDM_FILE_NEW
        MENUITEM "&Open...",                    IDM_FILE_OPEN
        MENUITEM "&Save",                       IDM_FILE_SAVE
        MENUITEM "Save &As...",                 IDM_FILE_SAVE_AS
        MENUITEM SEPARATOR
        MENUITEM "&Print",                      IDM_FILE_PRINT
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       IDM_APP_EXIT
    END
    POPUP "&Edit"
    BEGIN
        MENUITEM "&Undo\tCtrl+Z",               IDM_EDIT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&t\tCtrl+X",                IDM_EDIT_CUT
        MENUITEM "&Copy\tCtrl+C",               IDM_EDIT_COPY
        MENUITEM "&Paste\tCtrl+V",              IDM_EDIT_PASTE
        MENUITEM "De&lete\tDel",                IDM_EDIT_CLEAR
        MENUITEM SEPARATOR
        MENUITEM "&Select All",                 IDM_EDIT_SELECT_ALL
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&Help...",                    IDM_HELP_HELP
        MENUITEM "&About PopPad2...",           IDM_APP_ABOUT
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//

POPPAD2 ACCELERATORS
BEGIN
    VK_DELETE,      IDM_EDIT_CLEAR,         VIRTKEY, NOINVERT
    "^C",           IDM_EDIT_COPY,          ASCII,  NOINVERT
    "^X",           IDM_EDIT_CUT,           ASCII,  NOINVERT
    VK_DELETE,      IDM_EDIT_CUT,           VIRTKEY, SHIFT, NOINVERT
    "^V",           IDM_EDIT_PASTE,         ASCII,  NOINVERT
    VK_INSERT,      IDM_EDIT_PASTE,         VIRTKEY, CONTROL, NOINVERT
    VK_INSERT,      IDM_EDIT_PASTE,         VIRTKEY, SHIFT, NOINVERT
    "^Z",           IDM_EDIT_UNDO,          ASCII,  NOINVERT
    VK_BACK,        IDM_EDIT_UNDO,          VIRTKEY, ALT, NOINVERT
    VK_F1,          IDM_HELP_HELP,          VIRTKEY, NOINVERT
    "^A",           IDM_EDIT_SELECT_ALL,    ASCII,  NOINVERT
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
POPPAD2                 ICON                    "POPPAD2.ICO"

#endif    // Chinese (Simplified, PRC) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by poppad.rc
//
#define IDM_FILE_NEW                    40001
#define IDM_FILE_OPEN                   40002
#define IDM_FILE_SAVE                   40003
#define IDM_FILE_SAVE_AS                40004
#define IDM_FILE_PRINT                  40005
#define IDM_APP_EXIT                    40006
#define IDM_EDIT_UNDO                   40007
#define IDM_EDIT_CUT                    40008
#define IDM_EDIT_COPY                   40009
#define IDM_EDIT_PASTE                  40010
#define IDM_EDIT_CLEAR                  40011
#define IDM_EDIT_SELECT_ALL             40012
#define IDM_HELP_HELP                   40013
#define IDM_APP_ABOUT                   40014

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40028
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

10.3.8 啟動選單項

判斷是否可以做undo

EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO,
SendMessage(hwndEdit, EM_CANUNDO, 0, 0) ?
MF_ENABLED : MF_GRAYED);

判斷是否可以做Paste

EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,
IsClipboardFormatAvailable(CF_TEXT) ?
MF_ENABLED : MF_GRAYED);

判斷編輯框有文字被選中

iSelect = SendMessage(hwndEdit, EM_GETSEL, 0, 0);


if (HIWORD(iSele