转自:http://blog.csdn.net/tcjiaan/article/details/8510260
MAKEINTRESOURCE是一个资源名转换的宏,这个宏是把一个数字类型转换成指针类型的宏,它不存在释放的问题。
菜单加入窗口的两种方式
第一种是类级别的,也就是我们在设计窗口类时,直接指定给WNDCLASS结构的lpszMenuName成员,这样做意味着,在调用CreateWindow函数创建窗口时,无论你是否为窗口指定菜单,最终显示的窗口上都会有菜单,因为它是基于整个窗口类的。
-
- wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN);
- HWND hm = CreateWindow(
- L"MainWd",
- L"我的应用程序",
- WS_OVERLAPPEDWINDOW,
- 25,
- 18,
- 380,
- 280,
- NULL,
- NULL,
- hthisInstance,
- NULL);
这样在我们创建窗口时,哪怕你把hMenu参数设为NULL,最后显示的窗口都会有菜单,因为菜单是属于窗口类本身的。
另一种方式,就是不设置为类级别的菜单,而是在调用CreateWindow时指定给hMenu参数。
- HWND hm = CreateWindow(
- L"MainWd",
- L"我的应用程序",
- WS_OVERLAPPEDWINDOW,
- 25,
- 18,
- 380,
- 280,
- NULL,
- LoadMenu(hthisInstance,MAKEINTRESOURCE(IDR_MAIN)),
- hthisInstance,
- NULL);
同时我们把设计窗口类时设置菜单的代码注释掉。
然后,我们运行这个程序,它还是有菜单的。
方法一,用代码添加菜单
这种方法的思路是:首先在全局范围内定义一个HMENU的变量,用来保存窗口中菜单栏的句柄,根菜单(菜单栏)可以CreateMenu函数来创建,接着可以使用AppendMenu函数或者InsertMenuItem函数来创建菜单项。
句柄就是内存中各种资源的ID,比如图标,图片,字符串等。我们的菜单也是一种资源。
下面我写了一个函数,用来动态创建菜单。
- void CreateMyMenu()
- {
- hRoot = CreateMenu();
- if(!hRoot)
- return;
- HMENU pop1 = CreatePopupMenu();
- AppendMenu(hRoot,
- MF_POPUP,
- (UINT_PTR)pop1,
- L"操作");
-
- AppendMenu(pop1,
- MF_STRING,
- IDM_OPT1,
- L"飞机");
-
-
- MENUITEMINFO mif;
- mif.cbSize = sizeof(MENUITEMINFO);
- mif.cch = 100;
- mif.dwItemData = NULL;
- mif.dwTypeData = L"机关枪";
- mif.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
- mif.fState = MFS_ENABLED;
- mif.fType = MIIM_STRING;
- mif.wID = IDM_OPT2;
-
- InsertMenuItem(pop1,IDM_OPT2,FALSE,&mif);
-
- }
hRoot是在外部定义的全局变量,保存菜单栏的标识。完整的代码如下:
- #include <Windows.h>
-
- #define IDM_OPT1 301
- #define IDM_OPT2 302
-
- HMENU hRoot;
- void CreateMyMenu();
-
- LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-
-
- int CALLBACK WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR cmdLine,
- int nShow)
- {
- CreateMyMenu();
- WCHAR* cn = L"Myapp";
- WNDCLASS wc={ };
- wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
- wc.lpszClassName = cn;
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.hInstance = hInstance;
- wc.lpfnWndProc = (WNDPROC)MyWinProce;
- RegisterClass(&wc);
- HWND hm = CreateWindow(cn,
- L"我的应用程序",
- WS_OVERLAPPEDWINDOW,
- 20,
- 15,
- 420,
- 360,
- NULL,
- hRoot,
- hInstance,
- NULL);
- if( hm == NULL )
- return 0;
- ShowWindow(hm,nShow);
- MSG msg;
- while(GetMessage(&msg,NULL,0,0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return 0;
- }
-
-
- LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- switch(msg)
- {
- case WM_DESTROY:
-
- PostQuitMessage(0);
- return 0;
- default:
- return DefWindowProc(hwnd, msg, wParam,lParam);
- }
- }
-
- void CreateMyMenu()
- {
- hRoot = CreateMenu();
- if(!hRoot)
- return;
- HMENU pop1 = CreatePopupMenu();
- AppendMenu(hRoot,
- MF_POPUP,
- (UINT_PTR)pop1,
- L"操作");
-
- AppendMenu(pop1,
- MF_STRING,
- IDM_OPT1,
- L"飞机");
-
-
- MENUITEMINFO mif;
- mif.cbSize = sizeof(MENUITEMINFO);
- mif.cch = 100;
- mif.dwItemData = NULL;
- mif.dwTypeData = L"机关枪";
- mif.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
- mif.fState = MFS_ENABLED;
- mif.fType = MIIM_STRING;
- mif.wID = IDM_OPT2;
-
- InsertMenuItem(pop1,IDM_OPT2,FALSE,&mif);
-
- }
方法二,通过编辑资源来添加菜单
上面的方法虽然创建了菜单,但你也看到了,是相当地不方便,所以,下面我重点介绍一下用资源编辑器来创建菜单资源。
在你的开发工具上,依次找到菜单项【视图】【资源视图】。
在资源视图中,右击项目根节点,从弹出的菜单中选择【添加】【资源】。
在随后弹出的对话框中,选择Menu,单击右边的“新建”按钮。
可以通过属性窗口来重命名菜单的ID。
我们可以使用可视化视图来建立菜单,为了可以在代码中使用,给需要的菜单一个ID,这个名字你可以自己喜欢,只是惯用的是以IDM_开头,意思是Menu ID,比如IDC开头的,意思是Control ID等等。
编辑好之后,保存,在【解决方案资源管理器】中你会看到一个resource.h文件,其实我们为资源定义的ID都以宏的形式声明的,不信你打开看看。
资源ID都是数字,只是为它定义个名字,方便识别罢了,就好像人们平时只叫你的名字或者小名,你见过谁会叫你的身份证号码的?
开发工具生成的ID有时候会有问题,或者有些ID我们在程序中没用上,如果你觉得它们留在代码文件中会影响市容的话,你可以这样:
1、在【资源视图】窗口中,右击,从弹出的快捷菜单中选择【资源符号...】,弹出一个窗口,这里可以看到应用程序中的资源ID列表,以及哪些ID已被使用,但是,这个窗口中显示的内容,有时候不准确,有些ID明明没有被使用,它右边却打上了勾。
这里可以修改ID的值,也可以新建资源ID,所以,你也可以在这里预先为资源分配ID,然后在属性窗口设置资源的标识时,从下拉列表中选择指定的ID。资源ID的名字和数值不能重复,但是,不同的资源是可以使用同一个资源ID的。例如,通常在应用程序中,某些菜单项的功能和工具栏上的按钮是一一对应的,功能相同,这种情况下,我们可以考虑让它们共用一个ID。
2、你可以直接打开resource.h头文件,直接在上面修改。
响应菜单命令
当用户单击某个菜单项后,窗口处理程序(WindowProc)会收到一条WM_COMMAND消息,它的两个附加参数如下:
在收到WM_COMMAND后,我们可以用LOWORD取得它的低数位,上表中已经说明,wParam的低位值表示菜单的资源ID,我们通过它的值与哪个菜单的ID相等,就知道用户点击了哪个菜单项。
所以,我们的程序代码现在应为:
- #include <Windows.h>
- #include "resource.h"
-
- LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-
-
- int CALLBACK WinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR cmdLine,
- int nShow)
- {
- WCHAR* cn = L"Myapp";
- WNDCLASS wc={ };
- wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
- wc.lpszClassName = cn;
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.hInstance = hInstance;
- wc.lpfnWndProc = (WNDPROC)MyWinProce;
- RegisterClass(&wc);
- HWND hm = CreateWindow(cn,
- L"我的应用程序",
- WS_OVERLAPPEDWINDOW,
- 20,
- 15,
- 420,
- 360,
- NULL,
-
- LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN)),
- hInstance,
- NULL);
- if( hm == NULL )
- return 0;
- ShowWindow(hm,nShow);
- MSG msg;
- while(GetMessage(&msg,NULL,0,0))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return 0;
- }
-
-
- LRESULT CALLBACK MyWinProce(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- switch(msg)
- {
- case WM_COMMAND:
- {
-
-
- switch(LOWORD(wParam))
- {
- case IDM_PLANE:
- MessageBox(hwnd,L"灰机来了。",L"提示",MB_OK);
- break;
- case IDM_GUN:
- MessageBox(hwnd,L"让炮弹飞。",L"提示",MB_OK);
- break;
- case IDM_MT_GUN:
- MessageBox(hwnd,L"山炮欲来风满楼。",L"提示",MB_OK);
- break;
- default:
- break;
- }
- }
- return 0;
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
- default:
- return DefWindowProc(hwnd, msg, wParam,lParam);
- }
- }
在注册窗口类时,如要设置菜单,调用LoadMenu函数,第一个参数是当前程序实例的句柄,从WinMain的参数中获得,第二个参数是菜单的ID,因为这里要名字,字符串,而我们的ID都是数值,可通过MAKEINTRESOURCE宏转换。至于MessageBox函数就不用我介绍了。