分享
 
 
 

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

王朝system·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

--------------------------------------------------------------------------------

有关菜单和加速键

菜单是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实际上就是执行了菜单项“拷贝”。

菜单和加速键的编程是很简单的,初始化的部分你需要做以下事情:

取得程序的实例句柄(hInstance)

用LoadMenu装入菜单,得到菜单句柄

用LoadAccelerator装入加速键,得到加速键句柄

注册窗口类

创建窗口时在参数中制定菜单句柄

显示窗口

然后进入消息循环,在消息循环中用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使用。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有