窗口程序实验--行为与消息之间的关系
在这一节中,将通过不同的实验,进一步介绍窗口的运行。首先构造一个程序,在程序中将收到的消息查表翻译成文本以WM_XXX格式显示出来,并且将调用各个API函数的过程也显示出来,这样可以分析窗口的各种行为和消息之间的关系。
一、MsgWindow程序
为了把需要的内容显示出来,可以选择在客户区显示文本,但这样会引入新的消息,干扰实验,所以,这里选择了另一方法,就是先打开Windows附件中自带的Notepad记事本程序,然后在程序中将要显示的内容通过SendMessage发送到记事本中,可以通过查看记事本中的内容来了解MsgWindows的运行情况。
同样,先拷贝一份FirstWindow程序进行修改,共增加3个部分。第一部分是将消息查表转换为字符串,首先在.const段中增加两个表:16进制的消息编号列表dwMsgTable和字符串列表szStringTable,两表中的项目一一对应,代码如下:
.const
dwMsgTable dd WM_NULL
dd WM_CREATE
dd WM_DESTROY
dd WM_MOVE
dd WM_EXITSIZEMOVE
MSG_TABLE_LEN equ ($ - dwMsgTable) / sizeof dword
;注释:equ伪指令,是宏汇编特有的,是一种宏定义,而非高级语言中常量的概念。
;不会为符号分配空间。
;在汇编器编译的时候,直接将标识符MSG_TABLE_LEN替换为后面的值。
MSG_STRING_LEN equ sizeof szStringTable
szStringTable db ‘WM_NULL ’,0
db ‘WM_CREATE ’,0
db ‘WM_DESTROY ’,0
db ‘WM_MOVE ’,0
db ‘WM_EXITSIZEMOVE ’,0
szFormat db ‘WndProc: [%04x] %s %08x %08x’,0dh,0
MSG_TABLE_LEN定义了表的项数,MSG_STRING_LEN定义了字符串表中每一项的长度。sizeof操作符取的是szStringTable这一行中的数据长度,而非包括下面全部的字符串行。为了简化处理,全部字符串的长度保持相等。由于篇幅所限,这里没有列出全部的消息列表。程序在窗口过程的入口处调用_ShowMessage子程序来翻译消息并传给记事本。
_ProcWinMain proc uses ebx edi esi, hWnd, uMsg, wParam, lParam
invoke _ShowMessage, uMsg, wParam, lParam
mov eax, uMsg
.if eax == WM_XXX
_ShowMessage子程序用来将消息查表翻译成字符串,源程序如下 :
_ShowMessage proc _uMsg, _wParam, -lParam
local @suBuffer[128]:byte
pushad
;*************************************************
; 查找消息的说明字符串
; *************************************************
mov eax, _uMsg
mov edi, offset dwMsgTable
mov ecx, MSG_TABLE_LEN
cld
repnz scads
.if ZERO?
sub edi, offset dwMsgTable + sizeof dword
shr edi, 2
mov eax, edi
;eax寄存器中存放的是当前消息的索引号
mov ecx, MSG_STRING_LEN
mul ecx
add eax, offset szStringTable
; 索引号乘以项长度再加上消息字符串基址,就得到
;当前消息字符串的地址
;*************************************************
; 翻译格式并发送到Notepad窗口
; *************************************************
invoke wsprintf, addr @szBuffer, addr szFormat, \
_uMsg, eax, _wParam, _lParam
invoke _SendNotepad, addr @szBuffer
.endif
popad
ret
_ShowMessage endp
在这里要用到repnz scasd指令,scasd指令是把eax中的值从[edi]开始的内存中按双字比较,同时将edi加4,如果相等,则ZR标志置位,否则为NZ,repnz表示如果标志为NZ,则以ecx为重复次重复搜索,直到相等或ecx为零为止。
将ecx赋值为消息表的项数MSG_TABLE_LEN,将edi赋值为消息表的开始地址offset dwMsgTable,然后开始查找,停止后可以查看标志Zero位,如果是非ZERO,表示查完全部都没有找到,如果是ZERO,则表示找到表项。
当标志为ZERO时,edi指向找到项目的后一项,将edi减去一项的长度(sizeof dword)以及表的基址,再除以表项的长度(sizeof dword等于4,除以4等于右移两位,所以程序中用shr edi,2),就是消息在表中的索引了,接下来算出消息字符串的位置,位置等于:索引字符串长+字符串基址,代码如下:
mov ecx, MSG_STRING_LEN
mul ecx
add eax, offset szStringTable
这样,eax中就是字符串的地址了。最后将消息编号、名称和参数用wsprintf函数格式化成可以发送的字符串存放到@szBuffer中,并用_SendtoNotepad子程序将@szBuffer中的内容发送到记事本去。
程序增加的第二部分就是下面这个_SendtoNotepad子程序:
szDestClass db ‘Notepad’,0
_SendtoNotepad proc _lpsz
local @hWinNotepad
pushed
invoke FindWindow, addr szDestClass, NULL
.if eax
mov ecx,eax
invoke ChildWindowFromPoint, ecx, 20, 20
.endif
.if eax
mov @hWinNotepad, eax
mov esi,_lpsz
@@:
lodsb
or al,al
jz @F
movzx eax,al
invoke PostMessage,@hWinNotepad,WM_CHAR,eax,1
jmp @B
@@:
.endif
popad
ret
_SendtoNotepad endp
该子程序中首先用FindWindow函数来查找记事本程序是否已经运行,记事本程序的窗口类名称为“Notepad”,FindWindow可以用窗口类当做第一个参数来查找,如果找到,返回的是记事本的主窗口句柄,否则返回0。
要发送的是模拟按键的消息WM_CHAR,这样就好像在记事本中人工键入字符,但直接向记事本主窗口发送WM_CHAR消息是不行的,要向记事本窗口客户区中的编辑子窗口发送消息才行,所以程序中又用从位置获取子窗口句柄的函数ChildWindowFromPoint来获得编辑子窗口的句柄。
锁定了最后的目标即记事本中的编辑子窗口后,程序用PostMessage向它发送消息,根据字符串的长度,用一个循环每次发送一个WM_CHAR消息,WM_CHAR消息的wParam和lParam的含义是:
wParam = chCharCode //wParam的键值
lParam = lKeyData //lParam是键数据(重复次数)
程序中用movzx eax, al将键值扩展到参数所需的32位,当做wParam参数发送,lParam为1,表示键的值重复次数为1次,这样一来,记事本中就源源不断地显示出MsgWindow程序的运行轨迹了。
MsgWindow程序增加的第三部分是在每个函数的前后增加了显示状态的语句,它们只是简单地把一个字符串发送到记事本中去:
;定义一些字符串
szCreateWindow1 db ‘Creating Window…’,0dh,0
szCreateWindow2 db ‘CreateWindow end’,0dh,0
szShowWindow1 db ‘Showing Window…’,0dh,0
szShowWindow2 db ‘ShowWindown end’,0dh,0
szUpdateWindow1 db ‘Updating Window…’,0dh,0
szUpdateWindow2 db ‘UpdateWindow end’,0dh,0
szGetMsg1 db ‘Getting Message…’,0dh,0
szGetMsg2 db ‘[%04x]Message gotten’,odh,0
szDispatchMsg1 db ‘Dispatching Message…’,0dh,0
szDispatchMsg2 db ‘DispatchMessge end’,0dh,0
invoke _SendtoNotepad, addr szCreateWindow1
invoke CreateWindowEx,
invoke _SendtoNotepad, addr szCreateWindow2
invoke _SendtoNotepad, addr szShowWindow1
invoke ShowWindow, hWinMain, sw_SHOWNORMAL
invoke _SendtoNotepad, addr szShowWindow2
invoke _SendtoNotepad, addr szUpdateWindow1
invoke UpdateWindow, hWinMain
invoke _SendtoNotepad, addr szUpdateWindow2
实验一:验证收到消息的顺序
打开记事本,然后运行MsgWindow程序,记事本上出现的内容为:
Creating Window...
WndProc: [0024] WM_GETMINMAXINFO 00000000 0012f900
WndProc: [0081] WM_NCCREATE 00000000 0012f8e8
WndProc: [0083] WM_NCCALCSIZE 00000000 0012f920
WndProc: [0001] WM_CREATE 00000000 0012f8c4
CreateWindow end
Showing Window...
WndProc: [0018] WM_SHOWWINDOW 00000001 00000000
WndProc: [0046] WM_WINDOWPOSCHANGING 00000000 0012febc
WndProc: [0046] WM_WINDOWPOSCHANGING 00000000 0012febc
WndProc: [001c] WM_ACTIVATEAPP 00000001 000006d4
WndProc: [0086] WM_NCACTIVATE 00000001 00000000
WndProc: [000d] WM_GETTEXT 000001fe 0012f484
WndProc: [0006] WM_ACTIVATE 00000001 00000000
WndProc: [0007] WM_SETFOCUS 00000000 00000000
WndProc: [0085] WM_NCPAINT 00000001 00000000
WndProc: [000d] WM_GETTEXT 000001fe 0012f484
WndProc: [0014] WM_ERASEBKGND 01010058 00000000
WndProc: [0047] WM_WINDOWPOSCHANGED 00000000 0012febc
WndProc: [0005] WM_SIZE 00000000 0171024c
WndProc: [0003] WM_MOVE 00000000 007d006a
ShowWindow end
Updating Window...
WndProc: [000f] WM_PAINT 00000000 00000000
UpdateWindow end
Getting Message...
以WndProc开头的是在窗口过程中收到的消息,在调用CreateWindowEx的时候,窗口过程就开始接收消息,里面有重要的WM_CREATE,然后在ShowWindow的时候,Window向窗口过程发送了很多的消息,而UpdateWindow只给窗口过程发送了一条WM_PAINT消息,接下来就进入了循环。
可以看到,GetMessage函数是程序主动上交空闲时间的办法之一,因为显示Getting Message以后,程序就等着那里了,这表示程序的空闲时间并不浪费在消息循环中,而是在GetMessage函数的内部由Windows自动分配了。
接下来把鼠标移过MsgWindow窗口,在记事本上看到了什么?用户一个小小的动作就够窗口过程忙的了。我们看到了多次重复的下列内容:
WndProc: [0084] WM_NCHITTEST 00000000 0125024a
WndProc: [0020] WM_SETCURSOR 001305e2 02000001
[0200]Message gotten
Dispatching Message...
WndProc: [0200] WM_MOUSEMOVE 00000000 00a801e0
DispatchMessage end
Getting Message...
首先,Windows在GetMessage没有返回的时候调用了两次窗口过程,分别是处理WM_NCHITTEST和WM_SETCURSOR,它们并不经过消息循环;然后,GetMessage取到[0200]消息并返回,0200是WM_MOUSEMOVE消息的编号;接下来,DispatchMessage函数开始工作,在这个函数的内部,消息被Windows发送给窗口过程处理,最后DispatchMessage返回,然后开始新的GetMessage。
最后在MsgWindow上单击“关闭”按钮,看发生了什么:
[00a1]Message gotten
Dispatching Message...
WndProc: [00a1] WM_NCLBUTTONDOWN 00000014 007302b5
WndProc: [0215] WM_CAPTURECHANGED 00000000 00000000
WndProc: [0112] WM_SYSCOMMAND 0000f060 007302b5
WndProc: [0010] WM_CLOSE 00000000 00000000
WndProc: [0046] WM_WINDOWPOSCHANGING 00000000 0012f984
WndProc: [0047] WM_WINDOWPOSCHANGED 00000000 0012f984
WndProc: [0086] WM_NCACTIVATE 00000000 00000000
WndProc: [0006] WM_ACTIVATE 00000000 00000000
WndProc: [001c] WM_ACTIVATEAPP 00000000 000006d4
WndProc: [0008] WM_KILLFOCUS 00000000 00000000
WndProc: [0002] WM_DESTROY 00000000 00000000
WndProc: [0082] WM_NCDESTROY 00000000 00000000
DispatchMessage end
Getting Message...
[0012]Message gotten
GetMessage收到的是按下鼠标的WM_NCLBUTTONDOWN消息,由DispatchMessage转给窗口过程处理后,窗口过程将它转手给了DefWindowProc,DefWindowProc根据鼠标的位置得出结论:用户按的是“关闭”按钮,放到鼠标后,它就给窗口过程发送WM_CLOSE消息,当窗口过程调用DestroyWindow后,窗口被摧毁,窗口过程最后收到的是WM_DESTROY消息和WM_NCDESTROY消息,而消息循球中GetMessage最后收到的是0012号WM_QUIT消息,消息循环结束。
实验二:全部消息都经过消息循环吗?
上一实验中可以看到GetMessage返回的次数明显地比调用窗口过程的次数次,这意味着窗口过程有很多次是由Windows直接调用的。
这次,我们用极端的方式来验证,先把消息循环中的DispatchMessage去掉,这样,GetMessage得到的消息将不会再被送到窗口过程了,窗口过程收到的就是由Windows直接调用的了。
WndProc: [0084] WM_NCHITTEST 00000000 00c20076
WndProc: [0020] WM_SETCURSOR 000c03e6 02000001
由于没有了DispatchMessage,大部分消息被忽略了,窗口就停在了屏幕上,不能进行移动、缩放或关闭等操作,但还是有一部分消息直接由Windows发送给窗口过程,它们是鼠标位置测试的WM_NCHITTEST消息的要求设置光标的WM_SETCURSOR消息,所以在鼠标移动边框的时候,鼠标光标还是会变成双箭头的样子。
另外,尝试着单击别的窗口来切换焦点,然后再单击标题栏来重新激活窗口,可以发现WM_MOUSEACTIVATE,WM_ACTIVATE和WM_KILLFOCUS等消息也是不经过消息循环的。接下来,把一个窗口移动到MsgWindow窗口覆盖它的位置,再移开,可以发现WM_SYNCPAINT和WM_ERASEBKGND等消息也是由Windows直接发给窗口过程的。
最后,关闭窗口,当然这个窗口只能在任务管理器中以结束进程的方法关闭了!
实验三:TranslateMessage有什么用
在窗口上敲几个键,每次敲一个键,得到的消息是:WM_KEYDOWN,WM_CHAR和WM_KEYUP。如果按下键盘不放,则首先得到一个WM_KEYDOWN,接下来就是重复的WM_CHAR和WM_KEYUP消息,直到放开键盘为止,最后才会看到一个WM_KEYUP。显示如下:
WndProc: [0100] WM_KEYDOWN 00000041 001e0001
WndProc: [0102] WM_CHAR 00000061 001e0001
WndProc: [0101] WM_KEYUP 00000041 c01e0001
在WM_KEYDOWN和WM_KEYUP消息中,wParam中是按键的扫描码,上面的数据是按下了键“A”得到的,00000041h是“A”的扫描码,到了WM_CHAR消息中,wParam中就是已经转换过的ASCII码61了,代表输入的是小写的字母“a”。
好!现在从程序中去掉“TranslateMessage”语句,然后看这个程序的运行结果,同样,按几次键以及按下键盘不放,我们发现:这中间的区别就是少了WM_CHAR,所以只有在处理键盘输入要用到转换后的ASCII码的时候,TranslareMessage函数才是有用的,在别的时候完全可以省略这个语句。这个函数的功能就是看到WM_KEYDOWN的时候把消息检查一下,然后根据键值将一条新的WM_CHAR和WM_SYSCHAR的消息放入消息循环中。
实验四:DefWindowProc做了什么工作
现在把DefWindowProc语句去掉,然后再以同样的方法运行,窗口根本就没有出现!看记事本中出现了什么:
Creating Window...
WndProc: [0024] WM_GETMINMAXINFO 00000000 0012f900
WndProc: [0081] WM_NCCREATE 00000000 0012f8e8
WndProc: [0083] WM_NCCALCSIZE 00000000 0012f920
WndProc: [0001] WM_CREATE 00000000 0012f8e8
CreateWindow end
Showing Window...
WndProc: [0018] WM_SHOWWINDOW 00000001 00000000
WndProc: [0046] WM_WINDOWPOSCHANGING 00000000 0012febc
WndProc: [0046] WM_WINDOWPOSCHANGING 00000000 0012febc
WndProc: [001c] WM_ACTIVATEAPP 00000001 000006d4
WndProc: [0086] WM_NCACTIVATE 00000001 00000000
WndProc: [0006] WM_ACTIVATE 00000001 00000000
WndProc: [0007] WM_SETFOCUS 00000000 00000000
WndProc: [0085] WM_NCPAINT 00000001 00000000
WndProc: [0014] WM_ERASEBKGND 01010055 00000000
WndProc: [0047] WM_WINDOWPOSCHANGED 00000000 0012febc
WndProc: [0005] WM_SIZE 00000000 01900258
WndProc: [0003] WM_MOVE 00000000 00640064
ShowWindow end
Updating Window...
WndProc: [000f] WM_PAINT 00000000 00000000
UpdateWindow end
Getting Message...
[c0aa]Message gotten
Dispatching Message...
DispatchMessage end
Getting Message...
[000f]Message gotten
Dispatching Message...
WndProc: [000f] WM_PAINT 00000000 00000000
DispatchMessage end
Getting Message...
[000f]Message gotten
Dispatching Message...
WndProc: [000f] WM_PAINT 00000000 00000000
DispatchMessage end
Getting Message...
[000f]Message gotten
Dispatching Message...
WndProc: [000f] WM_PAINT 00000000 00000000
DispatchMessage end
Getting Message...
[000f]Message gotten
Dispatching Message...
WndProc: [000f] WM_PAINT 00000000 00000000
似乎陷入了死循环,因为记事本上不停地有消息冒出来,而且只是冒出WM_PAINT消息来,为什么呢?原来WM_PAINT消息是不能不处理的,也不能丢弃,只要Windows认为窗口的客户区需要绘画(或者说是无效的),它就会不停地向窗口发送WM_PAINT消息,一般WM_PAINT消息的处理中用BeginPaint和EndPaint会隐含地让客户区有效,如果不用BeginPaint/EndPaint,程序必须显式地把客户区设置为有效,Windows才不会再发送WM_PAINT消息。这个函数是ValidateRect,现在在分支中再加上处理WM_PAINT的代码:
.elseif eax == WM_PAINT
invoke ValidateRect, hWnd, NULL
再编译执行,现在记事本出现的信息显示程序停留在了GetMessage处,一切正常。但是,窗口在哪里呢,屏幕上什么都没有,隐身了?把鼠标移动窗口原来应该出现的地方,记事本中熟悉的WM_NCHITTEST和WM_SETCURSOR消息出现了,原来窗口还在那里,只不过没有了DefWindowProc的处理,窗口的绘画等所有工作都没有做,窗口的边框与与客户区等所有的东西连画都没有画上去,所以窗口是存在的,但我们看不到它!
是不是再加上WM_NCPAINT消息自己画边框呢,这就不是这个实验的内容了,我们已经知道,DefWindowProc做的工作太多了,缺了它我们要补上的代码可不是一两个分支的问题,而是上百个分支了!
实验的程序的完整代码如下:
.386
.model flat,stdcall
option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data?
hInstance dd ?
hWinMain dd ?
.const
szClassName db 'MyClass',0
szCaptionMain db 'Message Tester',0
;******************************************************************
; 消息ID列表
;******************************************************************
dwMsgTable dd WM_NULL
dd WM_CREATE
dd WM_DESTROY
dd WM_MOVE
dd WM_SIZE
dd WM_ACTIVATE
dd WM_SETFOCUS
dd WM_KILLFOCUS
dd WM_ENABLE
dd WM_SETREDRAW
dd WM_SETTEXT
dd WM_GETTEXT
dd WM_GETTEXTLENGTH
dd WM_PAINT
dd WM_CLOSE
dd WM_QUERYENDSESSION
dd WM_QUIT
dd WM_QUERYOPEN
dd WM_ERASEBKGND
dd WM_SYSCOLORCHANGE
dd WM_ENDSESSION
dd WM_SHOWWINDOW
dd WM_WININICHANGE
dd WM_DEVMODECHANGE
dd WM_ACTIVATEAPP
dd WM_FONTCHANGE
dd WM_TIMECHANGE
dd WM_CANCELMODE
dd WM_SETCURSOR
dd WM_MOUSEACTIVATE
dd WM_CHILDACTIVATE
dd WM_QUEUESYNC
dd WM_GETMINMAXINFO
dd WM_PAINTICON
dd WM_ICONERASEBKGND
dd WM_NEXTDLGCTL
dd WM_SPOOLERSTATUS
dd WM_DRAWITEM
dd WM_MEASUREITEM
dd WM_DELETEITEM
dd WM_VKEYTOITEM
dd WM_CHARTOITEM
dd WM_SETFONT
dd WM_GETFONT
dd WM_SETHOTKEY
dd WM_GETHOTKEY
dd WM_QUERYDRAGICON
dd WM_COMPAREITEM
dd WM_GETOBJECT
dd WM_COMPACTING
dd WM_OTHERWINDOWCREATED
dd WM_OTHERWINDOWDESTROYED
dd WM_COMMNOTIFY
dd WM_WINDOWPOSCHANGING
dd WM_WINDOWPOSCHANGED
dd WM_POWER
dd WM_COPYDATA
dd WM_CANCELJOURNAL
dd WM_NOTIFY
dd WM_INPUTLANGCHANGEREQUEST
dd WM_INPUTLANGCHANGE
dd WM_TCARD
dd WM_HELP
dd WM_USERCHANGED
dd WM_NOTIFYFORMAT
dd WM_CONTEXTMENU
dd WM_STYLECHANGING
dd WM_STYLECHANGED
dd WM_DISPLAYCHANGE
dd WM_GETICON
dd WM_SETICON
dd WM_NCCREATE
dd WM_NCDESTROY
dd WM_NCCALCSIZE
dd WM_NCHITTEST
dd WM_NCPAINT
dd WM_NCACTIVATE
dd WM_GETDLGCODE
dd WM_SYNCPAINT
dd WM_NCMOUSEMOVE
dd WM_NCLBUTTONDOWN
dd WM_NCLBUTTONUP
dd WM_NCLBUTTONDBLCLK
dd WM_NCRBUTTONDOWN
dd WM_NCRBUTTONUP
dd WM_NCRBUTTONDBLCLK
dd WM_NCMBUTTONDOWN
dd WM_NCMBUTTONUP
dd WM_NCMBUTTONDBLCLK
dd WM_KEYDOWN
dd WM_KEYUP
dd WM_CHAR
dd WM_DEADCHAR
dd WM_SYSKEYDOWN
dd WM_SYSKEYUP
dd WM_SYSCHAR
dd WM_SYSDEADCHAR
dd WM_KEYLAST
dd WM_INITDIALOG
dd WM_COMMAND
dd WM_SYSCOMMAND
dd WM_TIMER
dd WM_HSCROLL
dd WM_VSCROLL
dd WM_INITMENU
dd WM_INITMENUPOPUP
dd WM_MENUSELECT
dd WM_MENUCHAR
dd WM_ENTERIDLE
dd WM_CTLCOLORMSGBOX
dd WM_CTLCOLOREDIT
dd WM_CTLCOLORLISTBOX
dd WM_CTLCOLORBTN
dd WM_CTLCOLORDLG
dd WM_CTLCOLORSCROLLBAR
dd WM_CTLCOLORSTATIC
dd WM_MOUSEMOVE
dd WM_LBUTTONDOWN
dd WM_LBUTTONUP
dd WM_LBUTTONDBLCLK
dd WM_RBUTTONDOWN
dd WM_RBUTTONUP
dd WM_RBUTTONDBLCLK
dd WM_MBUTTONDOWN
dd WM_MBUTTONUP
dd WM_MBUTTONDBLCLK
dd WM_MOUSELAST
dd WM_PARENTNOTIFY
dd WM_ENTERMENULOOP
dd WM_EXITMENULOOP
dd WM_MDICREATE
dd WM_MDIDESTROY
dd WM_MDIACTIVATE
dd WM_MDIRESTORE
dd WM_MDINEXT
dd WM_MDIMAXIMIZE
dd WM_MDITILE
dd WM_MDICASCADE
dd WM_MDIICONARRANGE
dd WM_MDIGETACTIVE
dd WM_MDISETMENU
dd WM_DROPFILES
dd WM_MDIREFRESHMENU
dd WM_CUT
dd WM_COPY
dd WM_PASTE
dd WM_CLEAR
dd WM_UNDO
dd WM_RENDERFORMAT
dd WM_RENDERALLFORMATS
dd WM_DESTROYCLIPBOARD
dd WM_DRAWCLIPBOARD
dd WM_PAINTCLIPBOARD
dd WM_VSCROLLCLIPBOARD
dd WM_SIZECLIPBOARD
dd WM_ASKCBFORMATNAME
dd WM_CHANGECBCHAIN
dd WM_HSCROLLCLIPBOARD
dd WM_QUERYNEWPALETTE
dd WM_PALETTEISCHANGING
dd WM_PALETTECHANGED
dd WM_HOTKEY
dd WM_PRINT
dd WM_PRINTCLIENT
dd WM_PENWINFIRST
dd WM_PENWINLAST
dd WM_MENURBUTTONUP
dd WM_MENUDRAG
dd WM_MENUGETOBJECT
dd WM_UNINITMENUPOPUP
dd WM_MENUCOMMAND
dd WM_NEXTMENU
dd WM_SIZING
dd WM_CAPTURECHANGED
dd WM_MOVING
dd WM_POWERBROADCAST
dd WM_DEVICECHANGE
dd WM_ENTERSIZEMOVE
dd WM_EXITSIZEMOVE
MSG_TABLE_LEN equ ($ - dwMsgTable)/sizeof dword
;******************************************************************
; 消息名称字符串列表
;******************************************************************
MSG_STRING_LEN equ sizeof szStringTable
szStringTable db 'WM_NULL ',0
db 'WM_CREATE ',0
db 'WM_DESTROY ',0
db 'WM_MOVE ',0
db 'WM_SIZE ',0
db 'WM_ACTIVATE ',0
db 'WM_SETFOCUS ',0
db 'WM_KILLFOCUS ',0
db 'WM_ENABLE ',0
db 'WM_SETREDRAW ',0
db 'WM_SETTEXT ',0
db 'WM_GETTEXT ',0
db 'WM_GETTEXTLENGTH ',0
db 'WM_PAINT ',0
db 'WM_CLOSE ',0
db 'WM_QUERYENDSESSION ',0
db 'WM_QUIT ',0
db 'WM_QUERYOPEN ',0
db 'WM_ERASEBKGND ',0
db 'WM_SYSCOLORCHANGE ',0
db 'WM_ENDSESSION ',0
db 'WM_SHOWWINDOW ',0
db 'WM_WININICHANGE ',0
db 'WM_DEVMODECHANGE ',0
db 'WM_ACTIVATEAPP ',0
db 'WM_FONTCHANGE ',0
db 'WM_TIMECHANGE ',0
db 'WM_CANCELMODE ',0
db 'WM_SETCURSOR ',0
db 'WM_MOUSEACTIVATE ',0
db 'WM_CHILDACTIVATE ',0
db 'WM_QUEUESYNC ',0
db 'WM_GETMINMAXINFO ',0
db 'WM_PAINTICON ',0
db 'WM_ICONERASEBKGND ',0
db 'WM_NEXTDLGCTL ',0
db 'WM_SPOOLERSTATUS ',0
db 'WM_DRAWITEM ',0
db 'WM_MEASUREITEM ',0
db 'WM_DELETEITEM ',0
db 'WM_VKEYTOITEM ',0
db 'WM_CHARTOITEM ',0
db 'WM_SETFONT ',0
db 'WM_GETFONT ',0
db 'WM_SETHOTKEY ',0
db 'WM_GETHOTKEY ',0
db 'WM_QUERYDRAGICON ',0
db 'WM_COMPAREITEM ',0
db 'WM_GETOBJECT ',0
db 'WM_COMPACTING ',0
db 'WM_OTHERWINDOWCREATED ',0
db 'WM_OTHERWINDOWDESTROYED ',0
db 'WM_COMMNOTIFY ',0
db 'WM_WINDOWPOSCHANGING ',0
db 'WM_WINDOWPOSCHANGED ',0
db 'WM_POWER ',0
db 'WM_COPYDATA ',0
db 'WM_CANCELJOURNAL ',0
db 'WM_NOTIFY ',0
db 'WM_INPUTLANGCHANGEREQUEST',0
db 'WM_INPUTLANGCHANGE ',0
db 'WM_TCARD ',0
db 'WM_HELP ',0
db 'WM_USERCHANGED ',0
db 'WM_NOTIFYFORMAT ',0
db 'WM_CONTEXTMENU ',0
db 'WM_STYLECHANGING ',0
db 'WM_STYLECHANGED ',0
db 'WM_DISPLAYCHANGE ',0
db 'WM_GETICON ',0
db 'WM_SETICON ',0
db 'WM_NCCREATE ',0
db 'WM_NCDESTROY ',0
db 'WM_NCCALCSIZE ',0
db 'WM_NCHITTEST ',0
db 'WM_NCPAINT ',0
db 'WM_NCACTIVATE ',0
db 'WM_GETDLGCODE ',0
db 'WM_SYNCPAINT ',0
db 'WM_NCMOUSEMOVE ',0
db 'WM_NCLBUTTONDOWN ',0
db 'WM_NCLBUTTONUP ',0
db 'WM_NCLBUTTONDBLCLK ',0
db 'WM_NCRBUTTONDOWN ',0
db 'WM_NCRBUTTONUP ',0
db 'WM_NCRBUTTONDBLCLK ',0
db 'WM_NCMBUTTONDOWN ',0
db 'WM_NCMBUTTONUP ',0
db 'WM_NCMBUTTONDBLCLK ',0
db 'WM_KEYDOWN ',0
db 'WM_KEYUP ',0
db 'WM_CHAR ',0
db 'WM_DEADCHAR ',0
db 'WM_SYSKEYDOWN ',0
db 'WM_SYSKEYUP ',0
db 'WM_SYSCHAR ',0
db 'WM_SYSDEADCHAR ',0
db 'WM_KEYLAST ',0
db 'WM_INITDIALOG ',0
db 'WM_COMMAND ',0
db 'WM_SYSCOMMAND ',0
db 'WM_TIMER ',0
db 'WM_HSCROLL ',0
db 'WM_VSCROLL ',0
db 'WM_INITMENU ',0
db 'WM_INITMENUPOPUP ',0
db 'WM_MENUSELECT ',0
db 'WM_MENUCHAR ',0
db 'WM_ENTERIDLE ',0
db 'WM_CTLCOLORMSGBOX ',0
db 'WM_CTLCOLOREDIT ',0
db 'WM_CTLCOLORLISTBOX ',0
db 'WM_CTLCOLORBTN ',0
db 'WM_CTLCOLORDLG ',0
db 'WM_CTLCOLORSCROLLBAR ',0
db 'WM_CTLCOLORSTATIC ',0
db 'WM_MOUSEMOVE ',0
db 'WM_LBUTTONDOWN ',0
db 'WM_LBUTTONUP ',0
db 'WM_LBUTTONDBLCLK ',0
db 'WM_RBUTTONDOWN ',0
db 'WM_RBUTTONUP ',0
db 'WM_RBUTTONDBLCLK ',0
db 'WM_MBUTTONDOWN ',0
db 'WM_MBUTTONUP ',0
db 'WM_MBUTTONDBLCLK ',0
db 'WM_MOUSELAST ',0
db 'WM_PARENTNOTIFY ',0
db 'WM_ENTERMENULOOP ',0
db 'WM_EXITMENULOOP ',0
db 'WM_MDICREATE ',0
db 'WM_MDIDESTROY ',0
db 'WM_MDIACTIVATE ',0
db 'WM_MDIRESTORE ',0
db 'WM_MDINEXT ',0
db 'WM_MDIMAXIMIZE ',0
db 'WM_MDITILE ',0
db 'WM_MDICASCADE ',0
db 'WM_MDIICONARRANGE ',0
db 'WM_MDIGETACTIVE ',0
db 'WM_MDISETMENU ',0
db 'WM_DROPFILES ',0
db 'WM_MDIREFRESHMENU ',0
db 'WM_CUT ',0
db 'WM_COPY ',0
db 'WM_PASTE ',0
db 'WM_CLEAR ',0
db 'WM_UNDO ',0
db 'WM_RENDERFORMAT ',0
db 'WM_RENDERALLFORMATS ',0
db 'WM_DESTROYCLIPBOARD ',0
db 'WM_DRAWCLIPBOARD ',0
db 'WM_PAINTCLIPBOARD ',0
db 'WM_VSCROLLCLIPBOARD ',0
db 'WM_SIZECLIPBOARD ',0
db 'WM_ASKCBFORMATNAME ',0
db 'WM_CHANGECBCHAIN ',0
db 'WM_HSCROLLCLIPBOARD ',0
db 'WM_QUERYNEWPALETTE ',0
db 'WM_PALETTEISCHANGING ',0
db 'WM_PALETTECHANGED ',0
db 'WM_HOTKEY ',0
db 'WM_PRINT ',0
db 'WM_PRINTCLIENT ',0
db 'WM_PENWINFIRST ',0
db 'WM_PENWINLAST ',0
db 'WM_MENURBUTTONUP ',0
db 'WM_MENUDRAG ',0
db 'WM_MENUGETOBJECT ',0
db 'WM_UNINITMENUPOPUP ',0
db 'WM_MENUCOMMAND ',0
db 'WM_NEXTMENU ',0
db 'WM_SIZING ',0
db 'WM_CAPTURECHANGED ',0
db 'WM_MOVING ',0
db 'WM_POWERBROADCAST ',0
db 'WM_DEVICECHANGE ',0
db 'WM_ENTERSIZEMOVE ',0
db 'WM_EXITSIZEMOVE ',0
;*************************************************************************
szDestClass db 'Notepad',0
szFormat db 'WndProc: [%04x] %s %08x %08x',0dh,0
szCreateWindow1 db 'Creating Window...',0dh,0
szCreateWindow2 db 'CreateWindow end',0dh,0
szShowWindow1 db 'Showing Window...',0dh,0
szShowWindow2 db 'ShowWindow end',0dh,0
szUpdateWindow1 db 'Updating Window...',0dh,0
szUpdateWindow2 db 'UpdateWindow end',0dh,0
szGetMsg1 db 'Getting Message...',0dh,0
szGetMsg2 db '[%04x]Message gotten',0dh,0
szDispatchMsg1 db 'Dispatching Message...',0dh,0
szDispatchMsg2 db 'DispatchMessage end',0dh,0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_SendtoNotepad proc _lpsz
local @hWinNotepad
pushad
invoke FindWindow, addr szDestClass, NULL
.if eax
mov ecx, eax
invoke ChildWindowFromPoint,ecx,20,20
.endif
.if eax
mov @hWinNotepad, eax
mov esi, _lpsz
@@:
lodsb
or al, al
jz @F
movzx eax,al
invoke PostMessage, @hWinNotepad, WM_CHAR, eax, 1
jmp @B
@@:
.endif
popad
ret
_SendtoNotepad endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ShowMessage proc _uMsg, _wParam, _lParam
local @szBuffer[128]:byte
pushad
;*****************************************************************
; 查找消息的说明字符串
;*****************************************************************
mov eax, _uMsg
mov edi, offset dwMsgTable
mov ecx, MSG_TABLE_LEN
cld
repnz scasd
.if ZERO?
sub edi, offset dwMsgTable + sizeof dword
shr edi, 2
mov eax, edi
mov ecx, MSG_STRING_LEN
mul ecx
add eax, offset szStringTable
;*****************************************************************
; 翻译格式并发送到Notepad窗口
;*****************************************************************
invoke wsprintf, addr @szBuffer, addr szFormat, _uMsg, eax, _wParam, _lParam
invoke _SendtoNotepad, addr @szBuffer
.endif
popad
ret
_ShowMessage endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口过程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain proc uses ebx edi esi, hWnd, uMsg, wParam, lParam
invoke _ShowMessage, uMsg, wParam, lParam
mov eax,uMsg
;*************************************************************************
.if eax == WM_CLOSE
invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
.elseif eax == WM_NCCREATE
mov eax,1
ret
.elseif eax == WM_PAINT
invoke ValidateRect, hWnd, NULL
;***************************************************************************
.else
;invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
;***************************************************************************
xor eax,eax
ret
_ProcWinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain proc
local @szBuffer[128]:byte
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;**************************************************************************
; 注册窗口类
;**************************************************************************
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize, sizeof WNDCLASSEX
mov @stWndClass.style, CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc, offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov @stWndClass.lpszClassName, offset szClassName
invoke RegisterClassEx, addr @stWndClass
;***************************************************************************
; 建立并显示窗口
;***************************************************************************
invoke _SendtoNotepad, addr szCreateWindow1
invoke CreateWindowEx, WS_EX_CLIENTEDGE, \
offset szClassName, offset szCaptionMain, \
WS_OVERLAPPEDWINDOW, \
100, 100, 600, 400, \
NULL, NULL, hInstance, NULL
mov hWinMain,eax
invoke _SendtoNotepad, addr szCreateWindow2
invoke _SendtoNotepad, addr szShowWindow1
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke _SendtoNotepad, addr szShowWindow2
invoke _SendtoNotepad, addr szUpdateWindow1
invoke UpdateWindow,hWinMain
invoke _SendtoNotepad, addr szUpdateWindow2
;**************************************************************************
; 消息循环
;**************************************************************************
.while TRUE
invoke _SendtoNotepad, addr szGetMsg1
invoke GetMessage, addr @stMsg, NULL, 0, 0
push eax
invoke wsprintf, addr @szBuffer, addr szGetMsg2, @stMsg.message
invoke _SendtoNotepad, addr @szBuffer
pop eax
.break .if eax == 0
invoke TranslateMessage, addr @stMsg
invoke _SendtoNotepad, addr szDispatchMsg1
invoke DispatchMessage, addr @stMsg
invoke _SendtoNotepad, addr szDispatchMsg2
.endw
ret
_WinMain endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
call _WinMain
invoke ExitProcess, NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
end start