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

    技术2022-05-11  102

    --------------------------------------------------------------------------------有关菜单和加速键菜单是Windows标准界面的最重要的组成部分,窗口的菜单条位于标题栏的下方,这个菜单通常被称为主菜单,列在主菜单下面的菜单项被称为下拉式菜单,或弹出式菜单、子菜单等,而在标题栏左边的图标上点击也会弹出一个菜单,叫做系统菜单。加速键实际上是菜单项的快捷键,应用程序常在菜单项的右边标出激活这个菜单项的快捷键,这就是加速键。菜单的结构是可嵌套的,也就是说,你可以在选择一个菜单项时弹出另一个菜单。菜单项的种类有正常的、被禁用的、灰化的、水平分隔线等。本节的示范程序演示了各种类型的菜单:你可以在主菜单中看到正常的和禁用的、灰化的菜单,可以用右键单击窗口的任一部分弹出一个“弹出式菜单”,也可以看到我在系统菜单中添加了几项新的内容。在编程的处理中,菜单是在资源文件中定义的(当然,你可以不用资源文件,而在程序中用AppendMenu一项一项的添加,但使用资源文件无疑是最简单的办法),然后在程序中用LoadMenu来获得菜单句柄再使用。在资源文件中定义菜单的语法如下:菜单ID menu discardableBEGINpopup "主菜单项一"BEGINmenuitem "弹出式菜单项一", 命令ID [,OPTION]menuitem "弹出式菜单项二", 命令ID [,OPTION]menuitem separatormenuitem "弹出式菜单项三", 命令ID [,OPTION]...ENDpopup "主菜单项二"BEGINmenuitem "弹出式菜单项一", 命令ID [,OPTION]menuitem "弹出式菜单项二", 命令ID [,OPTION]menuitem "弹出式菜单项三", 命令ID [,OPTION]...popup "嵌套的菜单项"BEGINmenuitem "弹出式菜单项一", 命令ID [,OPTION]menuitem "弹出式菜单项二", 命令ID [,OPTION]menuitem "弹出式菜单项三", 命令ID [,OPTION]...ENDEND...END菜单ID就是我们在程序中用LoadMenu装入菜单用到的资源编号,menuitem separator 定义了分隔菜单项用的水平线,菜单项定义中的option是属性,如GRAYED是灰化的,INACTIVE是被禁用的等等。而加速键实际上就是定义了对应于各个菜单项的热键,定义方法如下:加速键ID acceleratorsBEGINVK_F1, 对应的菜单命令ID, VIRTKEYVK_F2, 对应的菜单命令ID, VIRTKEY..."A", 对应的菜单命令ID, VIRTKEY,CONTROL"B", 对应的菜单命令ID, VIRTKEY,CONTROLEND其中,加速键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, stdcalloption casemap :none ; case sensitive;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Include 数据;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>include windows.incinclude user32.incinclude kernel32.incinclude comctl32.incinclude comdlg32.incinclude shell32.incinclude gdi32.incincludelib user32.libincludelib kernel32.libincludelib comctl32.libincludelib comdlg32.libincludelib shell32.libincludelib gdi32.lib;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Equ 数据;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>IDI_MAIN equ 1000 ;iconIDA_MAIN equ 2000 ;AcceleratorIDM_MAIN equ 4000IDM_OPEN equ 4101IDM_OPTION equ 4102IDM_EXIT equ 4103IDM_SETFONT equ 4201IDM_SETCOLOR equ 4202IDM_FIND equ 4203IDM_FINDPREV equ 4204IDM_FINDNEXT equ 4205IDM_TOOLBAR equ 4206IDM_TOOLBARTEXT equ 4207IDM_INPUTBAR equ 4208IDM_STATUSBAR equ 4209IDM_HELP equ 4301IDM_ABOUT equ 4302;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 数据段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.data?hIcon dd ?hInstance dd ?hWinMain dd ?hMenu dd ?hSubMenu dd ?szBuffer db 256 dup (?)dwFlag dd ?;********************************************************************; 标志位定义F_TOOLBAR equ 00000001bF_TOOLBARTEXT equ 00000010bF_INPUTBAR equ 00000100bF_STATUSBAR equ 00001000b;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 数据段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.dataszClassName db "Menu Example",0szCaptionMain db '菜单应用示例',0szMenuHelp db "帮助主题(&H)",0szMenuAbout db "关于本程序(&A)...",0;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 代码段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.codeinclude Debug.asm;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 程序开始;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>start:call _WinMaininvoke ExitProcess,NULL;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 主窗口程序;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>_WinMain proclocal @stWcMain:WNDCLASSEXlocal @stMsg:MSGlocal @hAcceleratorinvoke InitCommonControlsinvoke GetModuleHandle,NULLmov hInstance,eaxinvoke LoadIcon,hInstance,IDI_MAINmov hIcon,eaxinvoke LoadMenu,hInstance,IDM_MAINmov hMenu,eax;*************** 注册窗口类 *****************************************invoke LoadCursor,0,IDC_ARROWmov @stWcMain.hCursor,eaxmov @stWcMain.cbSize,sizeof WNDCLASSEXmov @stWcMain.hIconSm,0mov @stWcMain.style,CS_HREDRAW or CS_VREDRAWmov @stWcMain.lpfnWndProc,offset WndMainProcmov @stWcMain.cbClsExtra,0mov @stWcMain.cbWndExtra,0mov eax,hInstancemov @stWcMain.hInstance,eaxmov @stWcMain.hIcon,0mov @stWcMain.hbrBackground,COLOR_WINDOW + 1mov @stWcMain.lpszClassName,offset szClassNamemov @stWcMain.lpszMenuName,0invoke 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,NULLinvoke ShowWindow,hWinMain,SW_SHOWNORMALinvoke UpdateWindow,hWinMain;*************** 消息循环 *******************************************invoke LoadAccelerators,hInstance,IDA_MAINmov @hAccelerator,eax.while TRUEinvoke GetMessage,addr @stMsg,NULL,0,0.break .if eax == 0invoke TranslateAccelerator,hWinMain,@hAccelerator,addr @stMsg.if eax == 0invoke TranslateMessage,addr @stMsginvoke DispatchMessage,addr @stMsg.endif.endwret_WinMain endp;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>WndMainProc proc uses ebx edi esi, /hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORDlocal @stPos:POINTmov eax,uMsg.if eax == WM_CREATEmov eax,hWndmov hWinMain,eaxcall _Init;********************************************************************.elseif eax == WM_COMMAND.if lParam == 0mov eax,wParammovzx eax,ax.if eax == IDM_EXITcall _Quit.elseif eax == IDM_TOOLBARxor dwFlag,F_TOOLBARcall _MenuStatus.elseif eax == IDM_TOOLBARTEXTxor dwFlag,F_TOOLBARTEXTcall _MenuStatus.elseif eax == IDM_INPUTBARxor dwFlag,F_INPUTBARcall _MenuStatus.elseif eax == IDM_STATUSBARxor dwFlag,F_STATUSBARcall _MenuStatus.else_Debug "菜单命令","命令ID",eax.endif.endif;********************************************************************.elseif eax == WM_SYSCOMMANDmov eax,wParammovzx eax,ax.if eax == IDM_HELP || eax == IDM_ABOUT_Debug "菜单命令","命令ID",eax.elseinvoke DefWindowProc,hWnd,uMsg,wParam,lParamret.endif;********************************************************************; 按下右键时弹出一个POPUP菜单;********************************************************************.elseif eax == WM_RBUTTONDOWNinvoke GetCursorPos,addr @stPosinvoke TrackPopupMenu,hSubMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL;********************************************************************.elseif eax == WM_CLOSEcall _Quit;********************************************************************.elseinvoke DefWindowProc,hWnd,uMsg,wParam,lParamret.endif;********************************************************************; 注意:WndProc 处理 Windows 消息后,必须在 Eax 中返回 0; 但是由 DefWindowProc 处理后的返回值不能改变,否则窗口; 将无法显示!;********************************************************************xor eax,eaxretWndMainProc endp;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 主窗口控制子程序;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>_Init proclocal @hSysMenuinvoke SendMessage,hWinMain,WM_SETICON,ICON_SMALL,hIcon;********************************************************************; POPUP菜单要用到子菜单才能实现;********************************************************************invoke GetSubMenu,hMenu,1mov hSubMenu,eaxcall _MenuStatus;********************************************************************; 在系统菜单中添加菜单项;********************************************************************invoke GetSystemMenu,hWinMain,FALSEmov @hSysMenu,eaxinvoke AppendMenu,@hSysMenu,MF_SEPARATOR,0,NULLinvoke AppendMenu,@hSysMenu,MF_STRING,IDM_HELP,offset szMenuHelpinvoke AppendMenu,@hSysMenu,MF_STRING,IDM_ABOUT,offset szMenuAboutret_Init endp;********************************************************************; 根据标志位设置相应菜单项的状态;********************************************************************_MenuStatus proctest dwFlag,F_INPUTBAR.if ZERO?invoke CheckMenuItem,hMenu,IDM_INPUTBAR,MF_UNCHECKED.elseinvoke CheckMenuItem,hMenu,IDM_INPUTBAR,MF_CHECKED.endiftest dwFlag,F_TOOLBAR.if ZERO?invoke CheckMenuItem,hMenu,IDM_TOOLBAR,MF_UNCHECKED.elseinvoke CheckMenuItem,hMenu,IDM_TOOLBAR,MF_CHECKED.endiftest dwFlag,F_TOOLBARTEXT.if ZERO?invoke CheckMenuItem,hMenu,IDM_TOOLBARTEXT,MF_UNCHECKED.elseinvoke CheckMenuItem,hMenu,IDM_TOOLBARTEXT,MF_CHECKED.endiftest dwFlag,F_STATUSBAR.if ZERO?invoke CheckMenuItem,hMenu,IDM_STATUSBAR,MF_UNCHECKED.elseinvoke CheckMenuItem,hMenu,IDM_STATUSBAR,MF_CHECKED.endifret_MenuStatus endp;********************************************************************_Quit procinvoke DestroyWindow,hWinMaininvoke PostQuitMessage,NULLret_Quit endp;********************************************************************end start程序的分析让我们来简单分析一下这个程序,首先这个程序和上一节的最简单的窗口程序的不同之处就是消息循环,如下:.while TRUEinvoke GetMessage,addr @stMsg,NULL,0,0.break .if eax == 0invoke TranslateAccelerator,hWinMain,@hAccelerator,addr @stMsg.if eax == 0invoke TranslateMessage,addr @stMsginvoke DispatchMessage,addr @stMsg.endif.endw 在循环中的TranslateAccelerator用来确定存放在MSG结构中的消息是不是键盘消息,如果是,它查找句柄@hAccelerator对应的加速键表,如果找到了一个匹配项,那么它将用命令ID向窗口发送WM_COMMAND消息,同时返回非0值,这时候表示消息已经被处理,不用再调用下面的TranslateMessage 和 DispatchMessage 了,如果不是,那么它将返回0,消息循环继续。另外,要说明的是弹出式菜单,在程序中我们响应WM_RBUTTONDOWN消息对按下右键进行处理, 然后调用GetCursorPos取得当前鼠标坐标,然后使用TrackPopupMenu在鼠标位置上弹出一个菜单,但是在资源文件中,“弹出式菜单”是无法直接定义的,所以在初始化部分,我们使用GetSubMenu 取出弹出式子菜单的句柄供TrackPopupMenu使用。

     


    最新回复(0)