这几天在看菜单,今天自己动手写了一下,出现了一些错误。
首先我先自己简单的写了一个只包含菜单资源的文件:
#include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; //TCHAR szAppName[] = TEXT ("ownmenu") ; (改进后加的代码) TCHAR MenuName[] = TEXT("IDR_MENU1"); int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; 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 = MenuName ; (改进后加入的代码) wndclass.lpszMenuName = szAppName; //程序名称和菜单名称是一样的 wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Menu Demonstration"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HMENU hMenu ; //定义了菜单句柄 switch (message) { case WM_COMMAND: //选择一项被启用的菜单 hMenu = GetMenu (hwnd) ; switch (LOWORD (wParam)) //里面包含的是菜单ID { case ID_FILE_NEW: MessageBox(hwnd, TEXT("CREATE A NEW FILE"), TEXT("hello"), MB_OK); break; case ID_FILE_EXIT: SendMessage(hwnd, WM_CLOSE, 0, 0); break; } break ; return 0; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; }
注意一下系统默认的菜单ID是 IDR_MENU1
我创建的4个popup的初始ID是ID_FILE_NEW40001
ID_FILE_OPEN40002
ID_FILE_SAVE40003
ID_FILE_EXIT
然后我尝试更改ID,把数字给去掉
ID_FILE_NEW
ID_FILE_OPEN
ID_FILE_SAVE
ID_FILE_EXIT
一编译,发现错误提示说 ID_FILE_NEW
ID_FILE_OPEN
ID_FILE_SAVE
全部找不到。
然后我去resource.h的文件里面去找,发现宏定义里面竟然没有把我改好以后的ID变掉,还是
ID_FILE_NEW40001
ID_FILE_OPEN40002
ID_FILE_SAVE40003
那么我心里想,就手工把后面的数字去掉吧 -_-
然后再次编译,错误提示说 ID_FILE_NEW redefinition
ID_FILE_OPEN redefinition
ID_FILE_SAVE redefinition
这就奇怪了,为什么会说 重定义错误呢?
我在网上查了写有关redefinition 错误的资料
redefinition的错误
原因一般是由于你调用的函数没有在前面声明,而系统会默认为int型
而在后面你可能又写了函数体,但定义类型又不是int,所以报redefinition的错误
解决办法是恢复h文件,或在文件前面添加报redefinition错误的函数的声明
最后再看了下代码,终于发现问题所在了。
wndclass.lpszMenuName = szAppName;在定义窗口类的时候,我把菜单的名字和窗口的名字写做一样了,其实系统默认的窗口名字是IDR_MENU1
所以在编译资源的时候,.h文件根本就不会改变!
找到原因了,现在我有两种改进的方法:
1.将wndclass.lpszMenuName 命名为系统默认的 IDR_MENU1
2. 将菜单名称变成ownmenu
但是依旧要注意,当你每次改变了菜单ID的时候,最好同时检查resource.h里面有没有发生改变,否则就会出现我上面说的错误哦!!~~~
下面再看看菜单引用的3种方法吧,其实自己一开始的时候是把下面的第一种方法和第二种方法搞混了,才会导致上面的错误。
第一种方法:
在注册窗口类的时候就指定要菜单,在WNDCLASSEX结构体的成员lpszMenuName中,假设我把引入菜单的叫做”MenuDemo”,
那么就可以这样引用:
TCHAR szAppName[] = TEXT (“MenuDemo”)
//code
wndclass.lpszMenuName = szAppName ;
上面这个例子是把程序名称和菜单名称做了相同的引用,用这种方法的话比较方便,比如在你的程序里面还有图标,声音或者其他的一些自定义资源的话,这种方法看起来就显得简单多了。
在这里就指定了你要的菜单,在窗口建立之前就已经包含在窗口类中了,建立窗口的时候直接就出来菜单。这种方法比较简单,所以 在一般情况下都用这种方法引用菜单资源。但是这种方法有一种缺陷:由于是在窗口类中指定的,所有由该窗口类派生出的窗口都有
相同的菜单
第二种方法:
HMENU hMenu
TCHAR MenuName[] = TEXT(“MenuDemo”);
//code
hMenu = LoadMenu(hInstance,MenuName);
hWinMain = CreateWindowEx(WS_EX_CLIENTEDGE,szClassName,szCaptionMain,
WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,hMenu,hInstance,NULL);
通过在CreateWindowsEx第九个参数指定要引入的菜单。CreateWindow调用中的指定菜单可以覆盖窗口类中指定的任何菜单。
第三种方法:
如果我们把CreateWindowEx第九个参数设置为NULL,并且也不在窗口类中指定NULL菜单,就可以在窗口被创建后在给窗口指定指定一个菜单:
SetMenu(hwnd,hMenu);
举个例子 ,在消息处理函数内 static HMENU hMenuFile1,hMenuFile1;
在WM_CREATE:
hInstance = (HINSTANCE)GetWindowsLong(hwnd,GWL_HINSTANCE);
hMenuFile1 = LoadMenu(hInstance,TEXT(“MenuMain1″); //举个例子
hMenuFile2 = LoadMenu(hInstance,TEXT(“MenuMain2″);面选择资源脚本多次,每个菜单都不同的名字。每当窗口过程处理WM_CREATE消息
的时候windows就会动态的白菜单资源加载到内存中去。SetMenu(hwnd,hMenu)让程序显示hMenu指向的菜单,然后你可以任意
SetMenu(hwnd,hMenuFile1)或者SetMenu(hwnd,hMenuFile2)之间切换,在windows程序设计上面通过按键来实现,这样你甚至可以多创建几个菜单。
下面我们简单地来看看.rc跟菜单资源有关的代码
当我们打开 .rc的资源文件的时候,如果你的工程里面添加了菜单资源的话,那么你可以看到类似下面这样的代码:
OWNMENU MENU //OWNMENU是菜单的名称 BEGIN POPUP "&File" BEGIN MENUITEM "&New", ID_FILE_NEW MENUITEM "&Open", ID_FILE_OPEN MENUITEM "&Save", ID_FILE_SAVE MENUITEM "&Exit", ID_FILE_EXIT END END这里的BEGIN 和END跟Pascal程序比较像,如果大家愿意的话其实可以手工用 { 来代替BEGIN ,用 } 来代替 END。
MENUITEM "&New", ID_FILE_NEW 这里的MENUITEM还有另一个选项:POPUP
对于MENUITEM 菜单项会生成一个带有特定ID的WM_COMMAND消息,比如上面这行代码 有ID_FILE_NEW这个ID,当然我们可以在resource.h里面找到它的数字ID
对于POPUP pop-up,翻译过来以后的意思是突然出现,冒出来的意思,所以在windows编程里我们可以将它理解为"跳出来"的菜单。 该菜单项会激活一个弹出窗口,这时它没有相关联的ID
对于上面的代码,我们应该可以想象在菜单栏有File这个选项,当我们点击File的时候,会弹出New,Open,Save,Exit四个选项,每一个选项当你选中后都会触发WM_COMMAND消息,我们可以把功能写在里面。