1.菜单的分类
菜单其实是一个容器,容器中放的每个菜单项。
HMENU类型表示菜单,菜单每一项有相应的ID。
(1)窗口顶层菜单
窗口标题栏和客户区之间的区域。
(2)弹出式菜单
例如:右键菜单、下拉菜单等
(3)系统菜单
2.菜单的使用
(1)窗口顶层菜单
创建菜单:HMENU CreateMenu(VOID); //成功返回菜单句柄
添加菜单项:BOOL AppendMenu( HMENU hMenu, //菜单句柄
UINT uFlag, //菜单顶风格
UINT_PTR uIDNewItem, //菜单ID或下拉菜单句柄
LPCTSTR lpNewItem); //菜单项名称
菜单风格:MF_POPUP - 具有这种风格的菜单项,被点击后弹出下拉菜单或子菜单,uIDNewItem必须设置下拉菜单或子菜单句柄。
MF_SEPARATOR - 具有这种风格的菜单项,为分割线。
MF_STRING - 具有这个风格的菜单项,被点击后能发出消息(WM_COMMAND)。
设置到窗口:BOOL SetMenu( HWND hWnd, //窗口句柄
HMENU hMenu); //菜单句柄
(2)弹出式菜单
创建菜单:HMENU CreatePopupMenu(VOID); //成功返回菜单句柄
加入顶层菜单:BOOL AppendMenu( HMENU hMenu, //菜单句柄
UINT uFlag, //菜单顶风格
UINT_PTR uIDNewItem, //菜单ID或下拉菜单句柄
LPCTSTR lpNewItem); //菜单项名称
(3)菜单命令处理
WM_COMMAND消息
wPARAM:HIWORD - 对于菜单为0
LOWORD - 菜单项ID
lPARAM:对于菜单为NULL
命令处理过程:获取(WPARAM的LOWORD)菜单ID,根据ID处理
(4)菜单项的状态
在增加菜单项可以设置菜单项的状态,可以使用菜单API(CheckMenuItem、EnableMenuItem)修改状态。
//设置菜单项勾选/非勾选状态
DWOED CheckMenuItem( HMENU hMenu, //菜单句柄
UINT uIDCheckItem, //菜单项ID或位置(索引)
UINT uCheck); //具体状态
uCheck:MF_BYCOMMAND - uIDCheckItem参数必须填写菜单项ID
MF_POSITION - uIDCheckItem参数必须填写菜单项位置
MF_CHECKED - 菜单项勾选
MF_UNCHECKED - 菜单项非勾选
//设置菜单项可用/不可用/灰色状态
BOOL EnableMenuItem( HMENU hMenu, //菜单句柄
UINT uIDEnableItem, //菜单项ID或位置(索引)
UINT uEnable); //具体状态
uEnable:MF_BYCOMMAND - uIDEnableItem参数必须填写菜单项ID
MF_POSITION - uIDEnableItem参数必须填写菜单项位置
MF_DISABLE - 不可用
MF_ENABLE - 可用
MF_GRAYED - 灰色状态
函数使用时,需要通过设置MF_BYPOSITION或者MF_BYCOMMAND,确定使用菜单索引号或者菜单ID。
(5)WM_INITMENUPOPUP
在菜单被激活但为显示时,窗口会收到这个消息
wPARAM:即将显示的菜单句柄
lPARAM:LOWORD - 被点击顶层菜单索引(位置)
HIWORD - 即将显示菜单是否为窗口菜单
窗口菜单:顶层菜单/系统菜单属于窗口菜单,弹出式菜单不属于窗口菜单。
相关示例代码:

#include "stdafx.h" #include "stdio.h" HINSTANCE g_hInstance = 0; //接收当前程序实例句柄 HMENU hFile = 0; HANDLE g_hOutput = 0; void OnCreate(HWND hWnd) { //创建(顶层)菜单 HMENU hMain = CreateMenu(); //创建下拉菜单 hFile = CreatePopupMenu(); AppendMenu(hFile, MF_STRING|MF_CHECKED, 1003, "新建"); AppendMenu(hFile, MF_SEPARATOR, 0, ""); AppendMenu(hFile, MF_STRING|MF_MENUBARBREAK, 1004, "退出"); HMENU hHelp = CreatePopupMenu(); AppendMenu(hHelp, MF_STRING|MF_GRAYED, 1005, "关于"); //添加菜单项 //MF_POPUP风格的菜单被点击后才会弹出菜单,而且第三个参数必须为下拉菜单的句柄 AppendMenu(hMain, MF_POPUP, (UINT)hFile, "文件"); AppendMenu(hMain, MF_POPUP, (UINT)hHelp, "帮助"); //将菜单设置到窗口 SetMenu(hWnd, hMain); } int g_state = 0;//标志量(标识新建菜单项当前状态) void OnCommand(HWND hWnd, WPARAM wParam) { switch (LOWORD(wParam)) { case 1003: if (0 == g_state) { CheckMenuItem(hFile, 1003, MF_BYCOMMAND | MF_UNCHECKED); } else { CheckMenuItem(hFile, 0, MF_BYPOSITION | MF_CHECKED); } g_state = !g_state; break; case 1004: MessageBox(hWnd, "退出被点击", "Info", MB_OK); break; case 1005: MessageBox(hWnd, "关于被点击", "Info", MB_OK); break; default: break; } } void OnInitMenuPopup(HWND hWnd, WPARAM wParam, LPARAM lParam) { char szText[256] = { 0 }; sprintf_s(szText, "即将显示菜单的句柄:%d,被点击的菜单项位置:%d,是否为窗口菜单:%d ", wParam, LOWORD(lParam), HIWORD(lParam)); WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL); } //窗口处理函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITMENUPOPUP: OnInitMenuPopup(hWnd, wParam, lParam); break; case WM_COMMAND: OnCommand(hWnd, wParam); break; case WM_CREATE: OnCreate(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, msg, wParam, lParam); } //注册窗口类 BOOL Register(LPSTR lpClassName, WNDPROC wndProc) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof(wce); wce.cbClsExtra = 200; wce.cbClsExtra = 200; wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInstance; wce.lpfnWndProc = wndProc; wce.lpszClassName = lpClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW | CS_VREDRAW; ATOM nAtom = RegisterClassEx(&wce); if (0 == nAtom) { return FALSE; } return TRUE; } //创建主窗口 HWND CreateMainWindow(LPSTR lpClassName, LPSTR lpWndName) { HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInstance, NULL); return hWnd; } //创建子窗口 HWND CreateChildWindow(LPSTR lpClassName, LPSTR lpWndName, HWND hParent) { HWND hChild = CreateWindowEx(0, lpClassName, lpWndName, WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 200, 200, hParent, NULL, g_hInstance, NULL); return hChild; } //显示窗口 void Display(HWND hWnd) { ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); } //消息循环 void Message() { MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg); } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { AllocConsole(); g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE); g_hInstance = hInstance; Register("Main", WndProc); HWND hWnd = CreateMainWindow("Main", "window"); Display(hWnd); Message(); return 0; }
运行结果:
(6)系统菜单
系统菜单的获取:HMENU GetSystemMenu( HWND hWnd, //窗口句柄
BOOL bRevert); //重置选项
返回获取到的系统菜单句柄
bRevert:TRUE - 删除旧菜单,恢复到默认的系统菜单
FALSE - 返回当前的系统菜单句柄
系统菜单的修改:AppendMenu
DeleteMenu( HMENU hMenu, //菜单句柄
UINT uPosition, //菜单项ID或位置
UINT uFlags); //决定uPosition参数填写什么
系统菜单命令响应:
通过WM_SYSCOMMAND响应菜单命令。
wPARAM:LOWORD - 命令ID
相关示例代码:

#include "stdafx.h" HINSTANCE g_hInstance = 0; //接收当前程序实例句柄 void OnCreate(HWND hWnd) { HMENU hSys = GetSystemMenu(hWnd, FALSE); for (int i = 0; i < 6; i++) { DeleteMenu(hSys, 0, MF_BYPOSITION); } AppendMenu(hSys, MF_SEPARATOR, 0, ""); AppendMenu(hSys, MF_STRING, 1001, "我的菜单项"); } void OnSysCommand(HWND hWnd, WPARAM wParam) { switch (LOWORD(wParam)) { case 1001: MessageBox(hWnd, "我的菜单项被点击", "Info", MB_OK); break; } } //窗口处理函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_SYSCOMMAND: OnSysCommand(hWnd, wParam); break; case WM_CREATE: OnCreate(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, msg, wParam, lParam); } //注册窗口类 BOOL Register(LPSTR lpClassName, WNDPROC wndProc) { WNDCLASSEX wce = { 0 }; wce.cbSize = sizeof(wce); wce.cbClsExtra = 200; wce.cbClsExtra = 200; wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wce.hCursor = NULL; wce.hIcon = NULL; wce.hIconSm = NULL; wce.hInstance = g_hInstance; wce.lpfnWndProc = wndProc; wce.lpszClassName = lpClassName; wce.lpszMenuName = NULL; wce.style = CS_HREDRAW | CS_VREDRAW; ATOM nAtom = RegisterClassEx(&wce); if (0 == nAtom) { return FALSE; } return TRUE; } //创建主窗口 HWND CreateMainWindow(LPSTR lpClassName, LPSTR lpWndName) { HWND hWnd = CreateWindowEx(0, lpClassName, lpWndName, WS_OVERLAPPEDWINDOW, 100, 100, 500, 300, NULL, NULL, g_hInstance, NULL); return hWnd; } //显示窗口 void Display(HWND hWnd) { ShowWindow(hWnd, SW_SHOW); UpdateWindow(hWnd); } //消息循环 void Message() { MSG nMsg = { 0 }; while (GetMessage(&nMsg, NULL, 0, 0)) { TranslateMessage(&nMsg); DispatchMessage(&nMsg); } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { g_hInstance = hInstance; Register("Main", WndProc); HWND hWnd = CreateMainWindow("Main", "window"); Display(hWnd); Message(); return 0; }
运行结果: