发一个罗云斌menu代码的详细解释,由于自己是一个不小的菜鸟,如果有不足和错误之处还望指正,感激不尽
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Sample code for < Win32ASM Programming 2nd Edition>
; by 罗云彬, http://asm.
;程序详细注释:by 塘鱼
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Menu.asm
; 菜单资源的使用例子
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff Menu.asm
; rc Menu.rc
; Link /subsystem:windows Menu.obj Menu.res
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.386
.model flat, stdcall
option casemap :none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Equ 等值定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_MAIN equ 1000h ;图标
IDM_MAIN equ 2000h ;菜单
IDA_MAIN equ 2000h ;加速键
IDM_OPEN equ 4101h
IDM_OPTION equ 4102h
IDM_EXIT equ 4103h
IDM_SETFONT equ 4201h
IDM_SETCOLOR equ 4202h
IDM_INACT equ 4203h
IDM_GRAY equ 4204h
IDM_BIG equ 4205h
IDM_SMALL equ 4206h
IDM_LIST equ 4207h
IDM_DETAIL equ 4208h
IDM_TOOLBAR equ 4209h
IDM_TOOLBARTEXT equ 4210h
IDM_INPUTBAR equ 4211h
IDM_STATUSBAR equ 4212h
IDM_HELP equ 4301h
IDM_ABOUT equ 4302h
IDM_APP equ 4303h ;塘鱼定义的测试参数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ? ;实例句柄
hWinMain dd ? ;主程序句柄
hMenu dd ? ;主菜单句柄
hSubMenu dd ? ;子菜单句柄,注意这里的子菜单也必须是一个顶层菜单不能是二级菜单即;popup类型的
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.const
szClassName db 'Menu Example',0 ;定义程序窗口类名
szCaptionMain db 'Menu',0 ;定义程序名字
szMenuHelp db '帮助主题(&H)',0 ;定义菜单窗口HELP
szMenuAbout db '关于本程序(&A)...',0
szCaption db '菜单选择',0
szFormat db '您选择了菜单命令:%08x',0 ;窗口格式定义
szApp db '塘鱼',0 ;塘鱼定义的一个测试参数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_DisplayMenuItem proc _dwCommandID ;显示窗口菜单ID的函数,读者可以比较,是否和资源文件中的定义一致
local @szBuffer[256]:byte ;预定义一个256字节的区域作为缓冲区;buffer
pushad ;函数开始时,保护好现场
invoke wsprintf,addr @szBuffer,addr szFormat,_dwCommandID ;调用wsprintf函数格式化字符串format
invoke MessageBox,hWinMain,addr @szBuffer,offset szCaption,MB_OK ;调用窗口函数,显示格式化好了的字符串,其在buffer去中
popad ;函数结束后还原现场,pop掉所有被保护的参数
ret ;程序从此地返回
_DisplayMenuItem endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Quit proc ;退出程序
invoke DestroyWindow,hWinMain ;销毁程序窗口
invoke PostQuitMessage,NULL ;发送结束消息循环指令,从此退出循环,这个过程不可更改
ret ;程序从此地返回
_Quit endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi hWnd,uMsg,wParam,lParam ;过程处理程序的初始化定义
local @stPos:POINT ;定义了一个结构体:POINT,用来获取鼠标光标的信息
local @hSysMenu ;定义了系统菜单的句柄
mov eax,uMsg ;将消息结构体Msg的值放到eax中,分支处理eax
.if eax == WM_CREATE ;发出创建窗口命令
invoke GetSubMenu,hMenu,1 ;获得子菜单句柄,这里是查看菜单,因为:npos被指定为1
mov hSubMenu,eax
;********************************************************************
; 在系统菜单中添加菜单项
;********************************************************************
invoke GetSystemMenu,hWnd,FALSE ;获取系统菜单句柄,并用AppendMenu函数添加命令
mov @hSysMenu,eax
invoke AppendMenu,@hSysMenu,MF_SEPARATOR,0,NULL
;调用添加菜单函数,添加HELP和ABOUT菜单到系统菜单上
invoke AppendMenu,@hSysMenu,0,IDM_HELP,offset szMenuHelp
invoke AppendMenu,@hSysMenu,0,IDM_ABOUT,offset szMenuAbout
;这时候你可以做个小实验尝试一下即取消我注释掉的自己添加的菜单,编译连接。
;invoke AppendMenu,@hSysMenu,0,IDM_APP,offset szApp
;********************************************************************
; 处理菜单及加速键消息
;********************************************************************
.elseif eax == WM_COMMAND
invoke _DisplayMenuItem,wParam ;调用显示菜单函数显示菜单命令ID读者可以和资源文件中的定义比较,呵呵一定是一模一样的
mov eax,wParam ;wParam中间存放的是通知码和命令ID
movzx eax,ax ;将ax扩展成32位,这里的作用是忽视掉由加速键发送的命令,将其一并处理,这样做的好处是精简代码,把高16位扩展成了0
.if eax == IDM_EXIT ;分支判断程序是否结束
call _Quit ;如果结束,调用退出函数_quit
.elseif eax >= IDM_TOOLBAR && eax <= IDM_STATUSBAR ;程序检查,IDM_TOORBAR到IDM_STATUSBAR之间的命令是否被选中
mov ebx,eax ;如果被选中,用ebx保存被选定的命令ID
invoke GetMenuState,hMenu,ebx,MF_BYCOMMAND ;调用getmenustate函数检查菜单的状态,由于uflag中定义的是me_bycommand所以使用的是命令ID形式
.if eax == MF_CHECKED ;检测命令ID是否被选中,如果选中则撤销选中
mov eax,MF_UNCHECKED
.else
mov eax,MF_CHECKED ;检测命令ID是否被选中,如果未被选中则选中
.endif
invoke CheckMenuItem,hMenu,ebx,eax ;调用checkmenuitem函数,将IMD_BIG和IDM_DETAIL定义为互斥空间,在此区间内永远只有一个被选中
.elseif eax >= IDM_BIG && eax <= IDM_DETAIL
invoke CheckMenuRadioItem,hMenu,IDM_BIG,IDM_DETAIL,eax,MF_BYCOMMAND ;调用checkmenuradioitem函数检测互斥空间,eax中是被选中的命令ID其余的将被撤销
.endif
;********************************************************************
; 处理系统菜单消息
;********************************************************************
.elseif eax == WM_SYSCOMMAND ;调用WM_SYSCOMMAND命令处理系统命令
mov eax,wParam ;这一步主要是为了把wparam的高字节扩展成0
movzx eax,ax ;可以方便程序一并处理加速键和菜单命令,可以简化程序
.if eax == IDM_HELP || eax == IDM_ABOUT || eax == IDM_APP ;这是我们自己添加修改的系统菜单,我们调用_displaymenutiem函数
invoke _DisplayMenuItem,wParam ;可以让她显示出命令ID这是会和我们资源中的定义是一样的
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ;调用DefWindowProc函数是为了处理默认的系统消息,如放大移动缩小还原
ret ;程序从此地返回
.endif
;********************************************************************
; 按下右键时弹出一个POPUP菜单
;********************************************************************
.elseif eax == WM_RBUTTONDOWN ;右键菜单需要调用消息,WM_RBUTTONDOWN
invoke GetCursorPos,addr @stPos ;首先获取鼠标信息,存入以定义的结构体:@stPos
invoke TrackPopupMenu,hSubMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL ;调用函数TrackPopupMenu从客服区单击鼠标的方式弹出右键菜单@stPos.x,@stPos.y是结构体,@stPos中存放鼠标位置的参数
;********************************************************************
.elseif eax == WM_CLOSE ;收到关闭窗口的命令,调用QUTI函数销毁窗口,这一过程不可逆转
call _Quit
;********************************************************************
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ;否则按照默认方式处理窗口
ret
.endif
;********************************************************************
xor eax,eax ;清空eax的值并返回
ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @stWndClass:WNDCLASSEX ;定义WNDCLASSEX结构体用来存储窗口风格
local @stMsg:MSG ;定义消息结构体,用来给过程处理函数执行
local @hAccelerator ;定义参数用来保存加速键句柄,以备后面使用
invoke GetModuleHandle,NULL ;获取实例句柄
mov hInstance,eax
invoke LoadMenu,hInstance,IDM_MAIN ;调用loadmenu函数,获取菜单句柄,和要加载资源的命令ID这里是:IDM_MAIN
mov hMenu,eax
invoke LoadAccelerators,hInstance,IDA_MAIN ;获取加速键句柄
mov @hAccelerator,eax
;********************************************************************
; 注册窗口类
;********************************************************************
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass ;将结构体参数清0
invoke LoadIcon,hInstance,ICO_MAIN ;获取默认窗口图标和程序图标
mov @stWndClass.hIcon,eax
mov @stWndClass.hIconSm,eax
invoke LoadCursor,0,IDC_ARROW ;获取默认鼠标指针
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance ;传递句柄,由于两个操作数都在内存中,所以要用PUSH和POP指令
mov @stWndClass.cbSize,sizeof WNDCLASSEX ;定义结构体的大小,以方便微软对版本的区别
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW ;定义窗口风格
mov @stWndClass.lpfnWndProc,offset _ProcWinMain ;最重要的一个参数,用来确定调用窗口过程的地址
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1 ;定义窗口的背景,注意的是需要在参数后+1
mov @stWndClass.lpszClassName,offset szClassName ;要创建窗口类的名称
invoke RegisterClassEx,addr @stWndClass ;注册窗口函数其中Ex,是又16位扩展的风格
;********************************************************************
; 建立并显示窗口
;********************************************************************
invoke CreateWindowEx,WS_EX_CLIENTEDGE,\ ;建立窗口函数
offset szClassName,offset szCaptionMain,\
WS_OVERLAPPEDWINDOW,\
100,100,400,300,\
NULL,hMenu,hInstance,NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL ;显示窗口和刷新客服区
invoke UpdateWindow,hWinMain
;********************************************************************
; 消息循环
;********************************************************************
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0 ;从消息队列中获取消息
.break .if eax == 0
invoke TranslateAccelerator,hWinMain,@hAccelerator,addr @stMsg ;加速键消息处理(注:加速键即快捷键)
.if eax == 0 ;处理完毕后检查eax的值,如果返回值为0,即处理完毕就继续消息的转换和分配,这样做的好处就是,不会把加速键交给DISPATH函数分配执行
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg ;分配消息给过程处理函数
.endif
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start: ;程序入口点
call _WinMain
invoke ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start