菜单在你的窗口中是最重要的一部分。当前菜单将一系列服务程序提供给用户使用。用户没有必要去阅读程序手册来了解如何使用这个应用程序,他只要细读一下所有的菜单项就能够对这个特殊的应用程序的功能有一个全面的了解并且可以立即开始使用它。因为菜单给用户程序提供了方便快捷的运行方式。所以当你在你的应用程序中加入菜单时应该遵循一般的标准。
最简便的放置方式是:一开始的那两个菜单项应该是“文件”和“编辑”而最后一个是“帮助”。你能在“编辑”和“帮助”之间插入你自己的菜单项。如果一个菜单项调用一个对话框,你应该为这个菜单项的名字追加一个省略号(。。。 )
菜单是一类资源。 这里有几种类型的资源例如:对话框,字符串表,图表,位图,菜单等等。资源通常被说明在一个分离的文件中,被调用的资源文件通常以.rc为后缀。你可以在连接程序的时候将这些资源和程序源代码组合在一起。最后的产物是一个含有指令和资源的可执行文件。
你能用任何文本编辑软件来写资源脚本。 它们是用来描述在某特定程序中的资源的外部特征和属性的短语组合。 虽然你能在文本编辑中写资源脚本,但它是相当麻烦的。 一个更好的方案是用资源编辑器,它容易让你设计出一个可视的资源。 资源编辑器通常被包含在编译程序包之中,如:visual c++ ,Borland C ++ 等。
你应该像这样来描述一个菜单资源:
MyMenu MENU
{
[menu list here]
}
C程序设计员可以认为它和声明一个结构有相似之处。MyMenu是菜单资源的名字,跟随在MENU关键字后面波形括号中的是菜单项。作为选择,如果你想,你还可以用BEGIN和END来替代这个波形括号。这种句法对PASCAL的程序员来说更称心。
菜单列表中的菜单项可以用MENUITEM或 POPUP语句来声明。
MENUITEM语句定义一个菜单项,当它被选择时并不调用弹出式菜单。 句法如下:
MENUITEM "&text", ID [,options]
首先,通过在关键字MENUITEM后跟随一文本用来作为你想使用的菜单项字串. 注意这个符号(&),它后面的第一个字符将被加上下划线.
文本字串后面的 是这个菜单项的ID值.ID值是一个数字,当菜单项被选择时,它作为消息中的菜单项标识传递给窗口过程.同样的,每一个菜单项的ID值都必须是唯一的.
Options是可选项,可用的选项如下所示:
GRAYED 表该菜单项处于非激活状态, 即当其被选中时不会产生 WM_COMMAND消息。该菜单项显示为灰色.
INACTIVE 表该菜单项处于非激活状态, 即当其被选中时不会产生 WM_COMMAND消息。该菜单以正常颜色显示
MENUBREAK 这个菜单项和以后的菜单项列到新的一列中
HELP 这个菜单项和以后的菜单项右对齐.
你能用一个或多个上面的选项,当用到多个选项时,用OR操作将它们组合.
注意::: INACTIVE 和GRAYED不能被组合在一起.
POPUP statement has the following syntax:
弹出式的菜单句法如下:
POPUP "&text" [,options]
{
[menu list]
}
POPUP定义了这样一个菜单项,当它被选择时,弹出一个小窗口这个小窗口中包含一系列菜单项.这些菜单项能被MENUITEM 或 POPUP声明,这里有一种特别的MENUITEM语句,MENUITEM SEPARATOR ,它将在弹出式窗口中画一条水平线.
在你完成菜单资源脚本的定义后,下一步的工作就是在程序中引用它.
你能用如下方式在程序中的两个不同的地方使用它:
在WNDCLASSEX结构成员变量lpszMenuName中。看,如果你有一个菜单的名字是“FirstMenu“你能像这样把这个菜单分配在你的窗口中:
.DATA
MenuName db "FirstMenu",0
...........................
...........................
.CODE
...........................
mov wc.lpszMenuName, OFFSET MenuName
...........................
In menu handle parameter of CreateWindowEx like this:
在作为CreateWindowEx函数的参数的菜单句柄中像这样使用:
.DATA
MenuName db "FirstMenu",0
hMenu HMENU ?
...........................
...........................
.CODE
...........................
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu, eax
invoke CreateWindowEx,NULL,OFFSET ClsName,\
OFFSET Caption, WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,\
NULL,\
hMenu,\
hInst,\
NULL\
...........................
如此,你可能会问,为什么要定义两种不同的引用方法?
当你在WNDCLASSEX结构中引用这个菜单时,这个菜单就变成窗口类的默认菜单。
每一个基于这个类创建的窗口都将有着相同的菜单。
如果你想在基于同于一个窗口类创建的不同窗口中拥有不同的菜单,你必须用第二种形式。既然这样,任何窗口只要传递一个菜单句柄
给CreateWindowEx函数,它将忽略那个在WNDCLASSEX结构中定义的默认菜单。
下一步,我们将分析当用户选中一个菜单项时,这个菜单项是如何通知窗口过程的。当用户选择一个菜单项时,窗口过程将接收到一个WM_COMMAND消息。消息中的低字节参数 wParam包含这个被选中的菜单项的ID 值。
现在,我们已经有充分的信息来创建和使用一个菜单,让我们试一试。
例子:
第一个例子将显示通过在窗口类中指定一菜单名的方式来创建和使用这个菜单。
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.const
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName ; Put our menu name here
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
**************************************************************************************************************************
Menu.rc
**************************************************************************************************************************
#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}
分析:
首先,让我们分析一下资源文件.
#define IDM_TEST 1 /* equal to IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
上面这些行用来声明在菜单资源脚本中使用的ID值。你能给菜单项的ID分配任何值只要这些值在菜单中是唯一的。
FirstMenu MENU
Declare your menu with MENU keyword.
用MENU关键字声明你的菜单。
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
定义了一个有四个菜单项的弹出式菜单,第三个菜单是分隔符。
MENUITEM "&Test", IDM_TEST
定义一个菜单项在主菜单中。
下面我们将分析源代码。
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
MenuName是指在资源文件中的菜单名。注意,你能在资源文件中定义不止一个的菜单所以你必须指出那一个菜单是你想使用的。 剩下的三行定义的是当用户选择了适当的菜单项时在消息框中显示的字符串文本。
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
定义在窗口过程中使用的菜单项ID值。这些值必须同定义在资源文件中的值一样。
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
我们在窗口过程中处理WM_COMMAND消息。当用户选择一个菜单项时,这个菜单项的ID被存贮在WM_COMMAND消息的低位字wParam中并传递给窗口过程 。所以我们用eax存储这个参数。(wparam)我们用在ax中的值来和我们以前定义的ID值比较并且回应它。(消息处理代码)在第一个例子中, 当用户选择了test,say hello 和 say goodbye 菜单项时。我们仅在消息框中显示一字符。
如果用户选择的是exit菜单项,我们将用我们的窗口句柄作为DestroyWindow 的参数并调用这个函数来关闭我们的窗口。
如你所见,在窗口类中指定一个菜单名不仅是非常简单的而且更为直接。然而,你还能用另外一种方法来在你的窗口中装载一个菜单。我不想在这里展示所有的源代码。两种方法的资源文件是一样的,它们只是在源文件中有较小的一点改变。下面是源文件。
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ? ; handle of our menu
定义一个句柄变量用来存储我们的菜单句柄。
invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu,eax
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,\
hInst,NULL
在调用CreateWindowEx之前,我们用实例句柄和指向我们菜单名的指针作为LoadMenu的参数来调用它。loadMenu返回我们在资源文件中定义的菜单句柄并作为参数传递给CreatewindowEx 函数。
CreatePopupMenu
函数功能:该函数创建一个下拉式菜单、子菜单或快捷菜单。此菜单最初是空的,但可用函数InserMenultem来插入或追加菜单项。也可用函数InsertMenu来插人菜单项,用AppendMenu来追加菜单项。
函数原型:HMENU CreatePopupMenu(VOID)
参数:无。
返回值:如果函数调用成功,返回值是新创建菜单的句柄。如果函数调用失败,返回值是NULL。若想获得更多的错误信息,请调用GetLastError函数。
备注:一个应用程序可增加新菜单到已存在的菜单上,或者可以调用函数TrackPopupMenuEx或TrackPopupMenu来显示快捷菜单。与被分配给一个窗口的菜单相联系的资源会被自动释放。如果此菜单未被分配给一个窗口,应用程序必须在关闭之前释放与菜单相连的资源。应用程序通过调用函数DestroyMenu来释放菜单资源。Windows95环境下,系统可支持最多16,364个菜单句柄。
TrackPopupMenu
函数功能:该函数在指定位置显示快捷菜单,并跟踪菜单项的选择。快捷菜单可出现在屏幕上的任何位置。
函数原型:BOOL TrackPopupMenu(HMENU hMenu,UINT uFlags,int x,int y,int nReserved,HWND hWnd,
CONST RECT* prcRect);
参数
hMenu:被显示的快捷菜单的句柄。此句柄可为调用CreatePopupMenu创建的新快捷菜单的句柄,也可以为调用
GetSubMenu取得的与一个已存在菜单项相联系的子菜单的句柄。
uFlags:一种指定功能选项的位标志。用下列标志位之一来确定函数如何水平放置快捷菜单:
TPM_CENTERALIGN:若设置此标志,函数将按参数x指定的坐标水平居中放置快捷菜单。
TPM_LEFTALIGN: 若设置此标志,函数使快捷菜单的左边界与由参数X指定的坐标对齐。
TPM_RIGHTALIGN: 若设置此标志,函数使快捷菜单的右边界与由参数X指定的坐标对齐。
用下列标志位之一来确定函数如何垂直放置快捷菜单:
TPM_BOTTOMALIGN: 若设置此标志,函数使快捷菜单的下边界与由参数y指定的坐标对齐。
TPM_TOPALIGN: 若设置此标志,函数使快捷菜单的上边界与由参数y指定的坐标对齐。
TPM_VCENTERALIGN; 若设置此标志,函数将按参数y指定的坐标垂直居中放置快捷菜单
用下列标志位之一来确定在菜单没有父窗口的情况下用户的选择:
TPM_NONOTIFY: 若设置此标志,当用户单击菜单项时函数不发送通知消息。
TPM_RETURNCMD; 若设置此标志;函数将用户所选菜单项的标识符返回到返回值里。
(补充:当TrackPopupMenu的返回值大于0,就说明用户从弹出菜单中选择了一个菜单。以返回的ID号为参数wParam的值,
程序给自己发送了一个WM_SYSCOMMAND消息)
用下列标志位之一来确定在快捷菜单跟踪哪一个鼠标键:
TPM_LEFTBUTTON: 若设置此标志,用户只能用鼠标左键选择菜单项。
TPM_RIGHTBUTTON:若设置此标志,用户能用鼠标左、右键选择菜单项。
X:在屏幕坐标下,快捷菜单的水平位置。
Y:在屏幕坐标下,快捷菜单的垂直位置。
NReserved:保留值,必须为零。
HWnd:拥有快捷菜单的窗口的句柄。此窗口接收来自菜单的所有消息。函数返回前,此窗口不接受来自菜单的WM_COMMAND消息。
如果在参数uFlags里指定了TPM_NONOTIFY值,此函数不向hWnd标识的窗口发消息。 但必须给hWnd里传一个窗口句柄,可以是应用程序里的任一个窗口句柄。
PrcRect:未用。
返回值:如果在参数uFlags里指定了TPM_RETURNCMD值,则返回值是用户选择的菜单项的标识符。如果用户未作选择就取消了菜单或发生了错误,则退回值是零。如果没在参数uFlags里指定TPM_RETURNCMD值,若函数调用成功,返回非零值,若函数调用失败,返回零。若想获得更多的错误信息,清调用GetLastError
函数:
备注:Windows CE不支持参数uFlags取下列值:TPM_NONOTIFY;TPM_LEFTBUTTON;TPM_RIGHTBUTTON。
GetCursorPos
函数功能:该函数检取光标的位置,以屏幕坐标表示。
函数原型:BOOL GetCursorPos(LPPOlNT IpPoint);
参数:
IpPint:POINT结构指针,该结构接收光标的屏幕坐标。
使用时要先定义一个数据结构:
Public Type POINTAPI
x As Long
y As Long
End Type
例如: dim biao as POINTAPI
GetCursorPos biao
那么biao.x用来存放当前光标的x轴坐标,biao.y用来存放当前y轴的坐标。
LOCAL @stPos:POINT
invoke GetCursorPos,addr @stPos
invoke TrackPopupMenu,hMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWndListView,NULL
返回值:如果成功,返回值非零;如果失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。
备注:1.光标的位置通常以屏幕坐标的形式给出,它并不受包含该光标的窗口的映射模式的影响。该调用过程必须具有对窗口站的WINSTA_READATTRIBUTES访问权限。
2.此函数为api函数,调用时要函数声明:Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
AppendMenu
函数功能:该函数在指定的菜单条、下拉式菜单、子菜单或快捷菜单的末尾追加一个新菜单项。此函数可指定菜单项的内容、
外观和性能。函数AppendMenu己被lnsertMenultem取代。但如果不需要lnsertMenultem的扩展特性,
仍可使用AppendMenu。
函数原型:BOOL AppendMenu(hMenu hMenu,UINT uFlags,UINT uIDNewltem,LPCTSTR lpNewltem);
参数:
hMenu: 将被修改的菜单条、下拉式菜单、子菜单、或快捷菜单的句柄。
UFlags: 控制新菜单项的外观和性能的标志。此参数可以是备注里所列值的组合。
UIDNewltem: 指定新菜单项的标识符,或者当uFlags设置为MF_POPUP时,表示下拉式菜单或子菜单的句柄。
LpNewltem: 指定新菜单项的内容。此参数的含义取决于参数uFlags是否包含MF_BITMAP, MF_OWNERDRAW或MF_STRING标志,
如下所示:
MF_BITMAP: 含有位图句柄。MF_STRING:以`\O’结束的字符串的指针。
MF_OWNERDRAW:含有被应用程序应用的32位值,可以保留与菜单项有关的附加数据。当菜单被创建或其外观被修改时,
此值在消息WM_MEASURE或WM_DRAWITEM的参数IParam指向的结构,成员itemData里。
返回值:如果函数调用成功,返回非零值;如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError函数。
备注:一旦菜单被修改,无论它是否在显示窗口里,应用程序必须调用函数DrawMenuBar。
为了使键盘加速键能控制位留或自己绘制的菜单项,菜单的拥有者必须处理WM_MENUCHAR消息。
参见自绘制菜单和WM_MENUCHAR消息。
下列标志可被设置在参数uFlags里:
MF_BITMAP:将一个位图用作菜单项。参数lpNewltem里含有该位图的句柄。
MF_CHECKED:在菜单项旁边放置一个选取标记。如果应用程序提供一个选取标记,位图(参见SetMenultemBitmaps),
则将选取标记位图放置在菜单项旁边。
MF_DISABLED:使菜单项无效,使该项不能被选择,但不使菜单项变灰。
MF_ENABLED:使菜单项有效,使该项能被选择,并使其从变灰的状态恢复。
MF_GRAYED:使菜单项无效并变灰,使其不能被选择。
MF_MENUBARBREAK:对菜单条的功能同MF_MENUBREAK标志。对下拉式菜单、子菜单或快捷菜单,新列和旧列被垂直线分开。
MF_MENUBREAK:将菜单项放置于新行(对菜单条),或新列(对下拉式菜单、子菜单或快捷菜单)且无分割列。
MF_OWNERDRAW:指定该菜单项为自绘制菜单项。菜单第一次显示前,拥有菜单的窗口接收一个WM_MEASUREITEM消息来得到
菜单项的宽和高。然后,只要菜单项被修改,都将发送WM_DRAWITEM消息给菜单拥有者的窗口程序。
MF_POPUP:指定菜单打开一个下拉式菜单或子菜单。参数uIDNewltem下拉式菜单或子菜单的句柄。此标志用来给菜单条、
打开一个下拉式菜单或于菜单的菜单项、子菜单或快捷菜单加一个名字。
MF_SEPARATOR:画一条水平区分线。此标志只被下拉式菜单、子菜单或快捷菜单使用。此区分线不能被变灰、无效或加亮。
参数IpNewltem和uIDNewltem无用。
MF_STRING:指定菜单项是一个正文字符串;参数lpNewltem指向该字符串。
MF_UNCHECKED:不放置选取标记在菜单项旁边(缺省)。如果应用程序提供一个选取标记位图(参见SetMenultemBitmaps)
则将选取标记位图放置在菜单项旁边。
下列标志组不能被一起使用:
MF_DISABLED,MF_ENABLED和MF_GRAYED;MF_BITMAP,MF_STRING和MF_OWNERDRAW
MF_MENUBARBREAK和MF_MENUBREAK;MF_CHECKED和MF_UNCHECKED
Windows CE环境下,不支持参数fuFlags使用下列标志:
MF_BITMAP;MF_DOSABLE;MF_GRAYED
MF_GRAYED可用来代替MF_DISABLED和MFS_GRAYED。
Windows CE 1.0不支持层叠式菜单。在使用Windows CE 1.0时,不能将一个MF_POPUP菜单插入到另一个下拉式菜单中。Window CE 1.0不支持下列标志:
MF_POPUP;MF_MENUBREAK;MF_MENUBARBREAK
Windows CE 2.0或更高版本中,支持上述标志,也支持层叠式菜单。
lnsertMenultem
函数功能:该函数在菜单的指定位置插入一个新菜单项。
函数原型:BOOL WINAPI InsertMenutem(HMENU hMenu,UINT ultem,BOOL fByPosition,LPMENUITEMINFO lpmii );
参数:
hMenu:新菜单项将被插入其中的菜单的句柄。
ultem:在其前面插入新菜单项的菜单项的标识符或位置。此参数的含义取决于参数fByPosition的值。
fByFosition:用于确定ultem的含义的值。如果此参数为FALSE,Ultem表示菜单项的标识符。否则,ultem 表示菜单项的位置。
lpmii:指向结构MENUITEMINFO的指针,该结构中包含了新菜单项的信息。
返回值:如果函数调用成功,返回非零值;如果函数调用失败,返回值是零。若想获得更多的错误信息,请调用GetLastError函数。
备注:为了使键盘加速键能控制位图或自己绘制的菜单项,菜单的拥有者必须处理WM_MENUCHAR消息。
参见自绘制菜单和WM_MENUCHAR消息。
速查:Windows NT:4.0及以上版本;Windows:95及以上版本;Windows CE:不支持;头文件:winuser.h; 输入库:user32.lib Unicode:在Windows NT环境下,以Unicode和ANSI方式实现。