窗口程序實驗--行為與消息之間的關系
在這一節中,將通過不同的實驗,進一步介紹窗口的運行。首先構造一個程序,在程序中將收到的消息查表翻譯成文本以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