Win32汇编教程五:菜单和加速键的使用

有关菜单和加速键

菜单是Windows标准界面的最重要的组成部分,窗口的菜单条位于标题栏的下方,这个菜单通常被称为主菜单,列在主菜单下面的菜单项被称为下拉式菜单,或弹出式菜单、子菜单等,而在标题栏左边的图标上点击也会弹出一个菜单,叫做系统菜单。加速键实际上是菜单项的快捷键,应用程序常在菜单项的右边标出激活这个菜单项的快捷键,这就是加速键。菜单的结构是可嵌套的,也就是说,你可以在选择一个菜单项时弹出另一个菜单。菜单项的种类有正常的、被禁用的、灰化的、水平分隔线等。本节的示范程序演示了各种类型的菜单:你可以在主菜单中看到正常的和禁用的、灰化的菜单,可以用右键单击窗口的任一部分弹出一个“弹出式菜单”,也可以看到我在系统菜单中添加了几项新的内容。
在编程的处理中,菜单是在资源文件中定义的(当然,你可以不用资源文件,而在程序中用AppendMenu一项一项的添加,但使用资源文件无疑是最简单的办法),然后在程序中用LoadMenu来获得菜单句柄再使用。在资源文件中定义菜单的语法如下:

菜单ID    menu    discardable
BEGIN
        popup   "主菜单项一"
        BEGIN
                menuitem        "弹出式菜单项一",      命令ID    [,OPTION]
                menuitem        "弹出式菜单项二",      命令ID    [,OPTION]
                menuitem        separator
                menuitem        "弹出式菜单项三",      命令ID    [,OPTION]
                ...
        END
        popup   "主菜单项二"
        BEGIN
                menuitem        "弹出式菜单项一",      命令ID    [,OPTION]
                menuitem        "弹出式菜单项二",      命令ID    [,OPTION]
*                menuitem        "弹出式菜单项三",      命令ID    [,OPTION]
                ...
                popup           "嵌套的菜单项"
                BEGIN
                menuitem        "弹出式菜单项一",      命令ID    [,OPTION]
                menuitem        "弹出式菜单项二",      命令ID    [,OPTION]
                menuitem        "弹出式菜单项三",      命令ID    [,OPTION]
                ...
                END
        END
        ...
END

菜单ID就是我们在程序中用LoadMenu装入菜单用到的资源编号,menuitem separator 定义了分隔菜单项用的水平线,菜单项定义中的option是属性,如GRAYED是灰化的,INACTIVE是被禁用的等等。而加速键实际上就是定义了对应于各个菜单项的热键,定义方法如下:

加速键ID   accelerators
BEGIN
                VK_F1,  对应的菜单命令ID,      VIRTKEY
                VK_F2,  对应的菜单命令ID,      VIRTKEY
                ...
                "A",    对应的菜单命令ID,      VIRTKEY,CONTROL
                "B",    对应的菜单命令ID,      VIRTKEY,CONTROL
END

其中,加速键ID是我们在程序中用LoadAccelerator装入加速键的资源编号,下面的每一项定义了一个键,VK_F1表示用F1,“A”表示键A,下面的VIRTKEY是必需的,再下面的CONTROL“或SHIFT、ALT”表示用CONTROL键组合,也就是说,如果你定义了:"C",IDM_COPY,VIRTKEY,CONTROL 而且在菜单定义中定义了 menuitem "拷贝",IDM_COPY,那么,你在程序中按下Ctrl-C实际上就是执行了菜单项“拷贝”。
菜单和加速键的编程是很简单的,初始化的部分你需要做以下事情:

  1. -
  2. 取得程序的实例句柄(hInstance)
  3. 用LoadMenu装入菜单,得到菜单句柄
  4. 用LoadAccelerator装入加速键,得到加速键句柄
  5. 注册窗口类
  6. 创建窗口时在参数中制定菜单句柄
  7. 显示窗口
  8. 然后进入消息循环,在消息循环中用TranslateAccelerator来进行加速键的检测(详见源程序)

当窗口显示后,当一个菜单项或一个加速键被按下时,Windows向窗口过程发送WM_COMMAND消息,而当一个系统菜单中的菜单项被按下时,Windows 向窗口过程发送WM_SYSCOMMAND,菜单项命令的ID就包括在wParam的低16位中,在一般的编程中,如果我们不对系统菜单消息进行处理,那么只需在WM_COMMAND消息的处理中建立一段 .if/.elseif/.elseif .../.endif的语句对各个菜单命令ID进行处理就行了。

使用菜单和加速键的源程序

                .386
                .model flat, stdcall
                option casemap :none   ; case sensitive
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       Include 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
include         windows.inc
include         user32.inc
include         kernel32.inc
include         comctl32.inc
include         comdlg32.inc
include         shell32.inc
include         gdi32.inc
 
includelib      user32.lib
includelib      kernel32.lib
includelib      comctl32.lib
includelib      comdlg32.lib
includelib      shell32.lib
includelib      gdi32.lib
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       Equ 数据
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
IDI_MAIN        equ             1000            ;icon
IDA_MAIN        equ             2000            ;Accelerator
 
IDM_MAIN        equ             4000
IDM_OPEN        equ             4101
IDM_OPTION      equ             4102
IDM_EXIT        equ             4103
IDM_SETFONT     equ             4201
IDM_SETCOLOR    equ             4202
IDM_FIND        equ             4203
IDM_FINDPREV    equ             4204
IDM_FINDNEXT    equ             4205
IDM_TOOLBAR     equ             4206
IDM_TOOLBARTEXT equ             4207
IDM_INPUTBAR    equ             4208
IDM_STATUSBAR   equ             4209
IDM_HELP        equ             4301
IDM_ABOUT       equ             4302
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
                .data?
 
hIcon           dd              ?
hInstance       dd              ?
hWinMain        dd              ?
hMenu           dd              ?
hSubMenu        dd              ?
szBuffer        db      256 dup (?)
dwFlag          dd              ?
;********************************************************************
;       标志位定义
F_TOOLBAR       equ     00000001b
F_TOOLBARTEXT   equ     00000010b
F_INPUTBAR      equ     00000100b
F_STATUSBAR     equ     00001000b
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
                .data
 
szClassName     db      "Menu Example",0
szCaptionMain   db      '菜单应用示例',0
szMenuHelp      db      "帮助主题(&H)",0
szMenuAbout     db      "关于本程序(&A)...",0
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
 
                .code
 
include         Debug.asm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       程序开始
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
                call    _WinMain
                invoke  ExitProcess,NULL
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       主窗口程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain        proc
                local   @stWcMain:WNDCLASSEX
                local   @stMsg:MSG
                local   @hAccelerator
 
                invoke  InitCommonControls
                invoke  GetModuleHandle,NULL
                mov     hInstance,eax
                invoke  LoadIcon,hInstance,IDI_MAIN
                mov     hIcon,eax
                invoke  LoadMenu,hInstance,IDM_MAIN
                mov     hMenu,eax
;*************** 注册窗口类 *****************************************
                invoke  LoadCursor,0,IDC_ARROW
                mov     @stWcMain.hCursor,eax
                mov     @stWcMain.cbSize,sizeof WNDCLASSEX
                mov     @stWcMain.hIconSm,0
                mov     @stWcMain.style,CS_HREDRAW or CS_VREDRAW
                mov     @stWcMain.lpfnWndproc,offset WndMainProc
                mov     @stWcMain.cbClsExtra,0
                mov     @stWcMain.cbWndExtra,0
                mov     eax,hInstance
                mov     @stWcMain.hInstance,eax
                mov     @stWcMain.hIcon,0
                mov     @stWcMain.hbrBackground,COLOR_WINDOW + 1
                mov     @stWcMain.lpszClassName,offset szClassName
                mov     @stWcMain.lpszMenuName,0
                invoke  RegisterClassEx,addr @stWcMain
;*************** 建立输出窗口 ***************************************
                invoke  CreateWindowEx,WS_EX_CLIENTEDGE,/
                        offset szClassName,offset szCaptionMain,/
                        WS_OVERLAPPEDWINDOW OR WS_VSCROLL OR WS_HSCROLL,/
                        100,100,550,300,/
                        NULL,hMenu,hInstance,NULL
 
                invoke  ShowWindow,hWinMain,SW_SHOWNORMAL
                invoke  UpdateWindow,hWinMain
;*************** 消息循环 *******************************************
                invoke  LoadAccelerators,hInstance,IDA_MAIN
                mov     @hAccelerator,eax
                .while  TRUE
                        invoke  GetMessage,addr @stMsg,NULL,0,0
                        .break  .if eax == 0
                        invoke  TranslateAccelerator,hWinMain,@hAccelerator,addr @stMsg
                        .if     eax == 0
                                invoke  TranslateMessage,addr @stMsg
                                invoke  DispatchMessage,addr @stMsg
                        .endif
                .endw
                ret
 
_WinMain        endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
WndMainProc     proc    uses ebx edi esi, /
                hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
                local   @stPos:POINT
 
                mov     eax,uMsg
                .if     eax ==  WM_CREATE
                        mov     eax,hWnd
                        mov     hWinMain,eax
                        call    _Init
;********************************************************************
                .elseif eax ==  WM_COMMAND
                   .if  lParam == 0
                        mov     eax,wParam
                        movzx   eax,ax
                        .if     eax ==  IDM_EXIT
                                call    _Quit
                        .elseif eax ==  IDM_TOOLBAR
                                xor     dwFlag,F_TOOLBAR
                                call    _MenuStatus
                        .elseif eax ==  IDM_TOOLBARTEXT
                                xor     dwFlag,F_TOOLBARTEXT
                                call    _MenuStatus
                        .elseif eax ==  IDM_INPUTBAR
                                xor     dwFlag,F_INPUTBAR
                                call    _MenuStatus
                        .elseif eax ==  IDM_STATUSBAR
                                xor     dwFlag,F_STATUSBAR
                                call    _MenuStatus
                        .else
                                _Debug  "菜单命令","命令ID",eax
                        .endif
                   .endif
;********************************************************************
                .elseif eax == WM_SYSCOMMAND
                        mov     eax,wParam
                        movzx   eax,ax
                        .if     eax == IDM_HELP || eax == IDM_ABOUT
                                _Debug  "菜单命令","命令ID",eax
                        .else
                                invoke  DefWindowProc,hWnd,uMsg,wParam,lParam
                                ret
                        .endif
;********************************************************************
;       按下右键时弹出一个POPUP菜单
;********************************************************************
                .elseif eax == WM_RBUTTONDOWN
                        invoke  GetCursorPos,addr @stPos
                        invoke  TrackPopupMenu,hSubMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL
;********************************************************************
                .elseif eax ==  WM_CLOSE
                        call    _Quit
;********************************************************************
                .else
                        invoke  DefWindowProc,hWnd,uMsg,wParam,lParam
                        ret
                .endif
;********************************************************************
;       注意:WndProc 处理 Windows 消息后,必须在 Eax 中返回 0
;       但是由 DefWindowProc 处理后的返回值不能改变,否则窗口
;       将无法显示!
;********************************************************************
                xor     eax,eax
                ret
 
WndMainProc     endp
 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;       主窗口控制子程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Init           proc
                local   @hSysMenu
 
                invoke  SendMessage,hWinMain,WM_SETICON,ICON_SMALL,hIcon
;********************************************************************
;       POPUP菜单要用到子菜单才能实现
;********************************************************************
                invoke  GetSubMenu,hMenu,1
                mov     hSubMenu,eax
                call    _MenuStatus
;********************************************************************
;       在系统菜单中添加菜单项
;********************************************************************
                invoke  GetSystemMenu,hWinMain,FALSE
                mov     @hSysMenu,eax
                invoke  AppendMenu,@hSysMenu,MF_SEPARATOR,0,NULL
                invoke  AppendMenu,@hSysMenu,MF_STRING,IDM_HELP,offset szMenuHelp
                invoke  AppendMenu,@hSysMenu,MF_STRING,IDM_ABOUT,offset szMenuAbout
 
                ret
 
_Init           endp
;********************************************************************
;       根据标志位设置相应菜单项的状态
;********************************************************************
_MenuStatus     proc
 
                test    dwFlag,F_INPUTBAR
                .if     ZERO?
                        invoke  CheckMenuItem,hMenu,IDM_INPUTBAR,MF_UNCHECKED
                .else
                        invoke  CheckMenuItem,hMenu,IDM_INPUTBAR,MF_CHECKED
                .endif
                test    dwFlag,F_TOOLBAR
                .if     ZERO?
                        invoke  CheckMenuItem,hMenu,IDM_TOOLBAR,MF_UNCHECKED
                .else
                        invoke  CheckMenuItem,hMenu,IDM_TOOLBAR,MF_CHECKED
                .endif
                test    dwFlag,F_TOOLBARTEXT
                .if     ZERO?
                        invoke  CheckMenuItem,hMenu,IDM_TOOLBARTEXT,MF_UNCHECKED
                .else
                        invoke  CheckMenuItem,hMenu,IDM_TOOLBARTEXT,MF_CHECKED
                .endif
                test    dwFlag,F_STATUSBAR
                .if     ZERO?
                        invoke  CheckMenuItem,hMenu,IDM_STATUSBAR,MF_UNCHECKED
                .else
                        invoke  CheckMenuItem,hMenu,IDM_STATUSBAR,MF_CHECKED
                .endif
                ret
 
_MenuStatus     endp
;********************************************************************
_Quit           proc
 
                invoke  DestroyWindow,hWinMain
                invoke  PostQuitMessage,NULL
                ret
 
_Quit           endp
;********************************************************************
                end     start
 

程序的分析

让我们来简单分析一下这个程序,首先这个程序和上一节的最简单的窗口程序的不同之处就是消息循环,如下:

                .while  TRUE
                        invoke  GetMessage,addr @stMsg,NULL,0,0
                        .break  .if eax == 0
                        invoke  TranslateAccelerator,hWinMain,@hAccelerator,addr @stMsg
                        .if     eax == 0
                                invoke  TranslateMessage,addr @stMsg
                                invoke  DispatchMessage,addr @stMsg
                        .endif
                .endw 

在循环中的TranslateAccelerator用来确定存放在MSG结构中的消息是不是键盘消息,如果是,它查找句柄@hAccelerator对应的加速键表,如果找到了一个匹配项,那么它将用命令ID向窗口发送WM_COMMAND消息,同时返回非0值,这时候表示消息已经被处理,不用再调用下面的TranslateMessage 和 DispatchMessage 了,如果不是,那么它将返回0,消息循环继续。
另外,要说明的是弹出式菜单,在程序中我们响应WM_RBUTTONDOWN消息对按下右键进行处理, 然后调用GetCursorPos取得当前鼠标坐标,然后使用TrackPopupMenu在鼠标位置上弹出一个菜单,但是在资源文件中,“弹出式菜单”是无法直接定义的,所以在初始化部分,我们使用GetSubMenu 取出弹出式子菜单的句柄供TrackPopupMenu使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值