邪恶八进制信息安全团队技术讨论组's Archiver

金州 2006-1-5 02:52

[转载]Win32汇编教程5-菜单和加速键的使用

作者:罗云彬

本节的内容是上一节内容的扩展,所以示范的源程序是在上一节的基础上扩展的。

有关菜单和加速键

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

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

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

    .386
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;  Programmed by 罗云彬, [email]bigluo@telekbird.com.cn[/email]
;  Website: [url]http://asm.yeah.net[/url]
;  LuoYunBin's Win32 ASM page (罗云彬的编程乐园)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    .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使用。

页: [1]
© 1999-2008 EvilOctal Security Team