;运行效果
;RadASM环境,win32汇编入门教程之十一
;在上一个教程里,我们学习了如何利用资源编辑器创建对话框,并把对话框载入主程序,但是它只是显示出来,没有什么功能
;那么,在这个教程里,我们通过单击按钮,了解其消息机制的内涵。这个很重要,是窗口系统的核心理念
;首先,我们把上一教程的代码复制过来,同时把改变的代码标示出来,然后来理解这些不同的代码
;这些看起来很简单,但却要深入理解,其实并不简单,只有理解了,后面的才能够更好的进行。
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
include KLM.Inc
includelib user32.lib
includelib kernel32.lib
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
invoke ExitProcess,eax
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg == WM_INITDIALOG
invoke GetDlgItem,hWnd,IDB01
mov hIDB01,eax
invoke GetDlgItem,hWnd,IDB02
mov hIDB02,eax
.elseif uMsg == WM_COMMAND
mov ebx,wParam ;wParam的底位字节是标识符
mov eax,lParam
.if eax == hIDB01
.if bx == IDB01
shr ebx,16
.if bx == BN_CLICKED
invoke MessageBox,NULL,addr sz01,addr szMsg,MB_OK
.endif
.endif
.elseif eax == hIDB02
.if bx == IDB02
shr ebx,16
.if bx == BN_CLICKED
invoke MessageBox,NULL,addr sz02,addr szMsg,MB_OK
.endif
.endif
.endif
.elseif uMsg == WM_CLOSE
invoke EndDialog, hWnd,NULL
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
DlgProc endp
end start
;首先,我们看这一段
; invoke GetDlgItem,hWnd,IDB01
; mov hIDB01,eax
; invoke GetDlgItem,hWnd,IDB02
; mov hIDB02,eax
;这一段的意思是通过GetDlgItem函数,得到两个按钮的句柄。它们的参数是标识符。
;这段代码放在 WM_INITDIALOG 消息里面,就是对话框初始化的时候。你放在后面的 WM_COMMAND 消息里面也可以,但总得在它们需要使用前,先得到句柄值。
;标识符和句柄是不同的意思。这里标识符就是IDB01和IDB02,是用来区分不同的控件的。
;主要是在资源里,要标示出各个控件,它的值可以随便定义,但是不能和同类的相同。
;比如,这两个按钮,当点击的时候,用标识符来区分它们。
;这里2个标识符设的是101和102,你设成11和12也可以,这个随便,只要不相同
;句柄则是代表这个控件,它的值一般是程序在运行的时候赋值的,所以你观察一下。
;这里的hIDB01和hIDB02就是句柄,它们是在头文件里面定义,但却是未初始化的
;句柄是用来给指定窗口赋值,按钮也是窗口。比如想改变按钮文本内容,总得知道改变谁的文本内容
;这个谁就是句柄。
;我们继续看
;uMsg == WM_COMMAND 这里开始就是窗口接收到的命令消息了。这是当窗口接收到控件的消息时发生的
;比如,当你点击了按钮,电脑就把这个消息发送到它的父窗口。让父窗口处理这个消息
;当这个消息来的时候,随同一起来的还有另外三个值:hWnd,wParam,lParam
;hWnd就是父窗口的句柄。这里用不上,就不理它了
;wParam这个参数,是32位的,也就是4字节的。它的低2字节是标识符,高2字节是控件消息。
;这里的控件消息,比如单击BN_CLICKED,或其它的什么消息。
;要知道,不同的控件,有不同的消息。这个我们可以上网查询。
;我们先通过这一句 mov ebx,wParam ,把值给ebx
;然后通过判断它的低位或高位,再使用它。
;像这一句 bx == IDB01 。
;就是通过判断 bx ,即判断ebx的低2字节是不是第1个按钮的标识符,来识别哪个按钮被按了。
;那ebx的高2位字节的值怎么得到?
;就是这句 shr ebx,16 ,把ebx的值往右边推16位,就是把高2字节的值覆盖到低2字节。而原来高2字节的值就变成0了。
;然后 bx == BN_CLICKED ,判断低2字节的值,就是原来高2字节的值是不是 BN_CLICKED 消息,即单击的消息,后面再做出处理。
;这个时候 lParam 的值是控件的句柄。这里有2个按钮,点了哪个按钮,那消息里就是哪个按钮的句柄。
;当控件多的时候,就需要进行明确的区分。
;mov eax,lParam ,把值给了eax
;然后判断是哪个控件,比如后面的 eax == hIDB01 ,就是指定了是第1个按钮。
;上面就是如何处理WM_COMMAND消息的机制。
;有时候,我们看到的代码并不像上面那样多,因为上面的是详细的处理。
;大部分的时候,是不用写这么多代码的。
;如果用心,你会发现,里面有东西是差不多一样的意思,就是wParam的低2字节是控件标识符,而lParam是控件句柄。都是指某个按钮。
;所以可以简化这些代码。这个你其实可以做到的。
;以下是头文件内容
;函数声明
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD ;对话框窗口函数
.data
DlgName db "MyDialog",0
szMsg db "提示",0
sz01 db "点击了第1个按钮",0
sz02 db "单击了第2个按钮",0
.data?
hInstance HINSTANCE ?
hIDB01 dd ?
hIDB02 dd ?
.const
IDB01 equ 101
IDB02 equ 102
;以下是资源Rc文件内容
#include "resource.h"
#define IDB01 101
#define IDB02 102
MyDialog DIALOG 10, 10, 110, 120
STYLE WS_SYSMENU
CAPTION "我是对话框程序"
FONT 12, "方正姚体"
BEGIN
PUSHBUTTON "我是第一个按钮",IDB01,10,30,90,12
CONTROL "我是第二个按钮",IDB02,"button",WS_CHILDWINDOW|WS_VISIBLE,10,60,90,12
END