日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Win32汇编笔记-消息基础

發(fā)布時(shí)間:2025/4/14 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Win32汇编笔记-消息基础 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

WIN32的消息機(jī)制


windows系統(tǒng)是一個(gè)消息驅(qū)動(dòng)的OS,操作通過處理各種消息來響應(yīng)用戶的操作。

對于每一個(gè)帶有窗口的線程,系統(tǒng)都會(huì)給他分配一個(gè)自己的消息隊(duì)列,用于處理消息派送(Dispatch)。每個(gè)線程都用自己的消息循環(huán)來接受消息。每個(gè)線程列隊(duì)默認(rèn)管理最大10000個(gè)消息,修改注冊表下面的鍵值可以修改列隊(duì)中的消息數(shù)。建議的最小值是4000
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERPostMessageLimit.

線程列隊(duì)不是一個(gè)公開的數(shù)據(jù)結(jié)構(gòu)(THREADINFO),其中包括登記消息隊(duì)列(Posted-message queue),消息發(fā)送隊(duì)列(Send-message queue),消息應(yīng)答隊(duì)列(reply-message queue),虛擬輸入隊(duì)列(virtualized-input queue),喚醒標(biāo)志(wake flag),以及用來掃描線程局部輸入狀態(tài)的若干變量。(參考Windows核心編程)

消息列隊(duì)提取優(yōu)先級

1.檢查QS_SENDMESSAGE 標(biāo)志 GetMessage 不處理Send消息,如果隊(duì)列中沒有其他send消息,關(guān)閉QS_SENDMESSAGE標(biāo)志,GetMessage()不返回檢查其他消息。

2.檢查QS_POSTMESSAGE 標(biāo)志 GetMessage 從此列隊(duì)取出消息處理并由DisPatch分發(fā)到指定窗口回調(diào)函數(shù)處理。GetMessage返回True,沒有其他post消息關(guān)閉標(biāo)志。

3.檢查QS_QUIT 標(biāo)志? 如果被PostQuitMessage()打開,則GetMessage返回False退出消息循環(huán),并且關(guān)閉QS_QUIT標(biāo)志

4.檢查QS_INPUT 標(biāo)志 GetMessage 從此列隊(duì)取出消息處理由TranslateMessage()處理鍵盤鼠標(biāo)消息,然后由DisPatch分發(fā)到指定窗口回調(diào)函數(shù)處理沒有其他消息關(guān)閉標(biāo)志.

5.檢查QS_PAINT 標(biāo)志 處理同2

6.檢查QS_TIME? 標(biāo)志 首先復(fù)位計(jì)時(shí)器,GetMessage返回True,如果沒有計(jì)數(shù)器,關(guān)閉QS_TIME標(biāo)志。

優(yōu)先級很清楚,send優(yōu)先級最高,最低的是time。

Windows定義了很多消息都以WM_開頭,都是用#DEFINE 定義的常量,用戶可以定義自己的消息,windows規(guī)定用戶的消息從WM_USER 0x0400開始。

?

BOOL PostMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);

往進(jìn)程的消息列隊(duì)發(fā)送消息PostMessage,這個(gè)函數(shù)往指定進(jìn)程的消息列隊(duì)發(fā)送一個(gè)消息,發(fā)送完畢立即返回。調(diào)用函數(shù)無法知道發(fā)送的消息是否能被處理。如果這個(gè)指定窗口未處理完自己消息列隊(duì)的所有消息前就推出了,就會(huì)處理不到post的消息。

PostMessage發(fā)送的消息參數(shù)不能包含指針參數(shù),MSDN的說明是:
“如果發(fā)送一個(gè)低于WM_USER范圍的消息給異步消息函數(shù)(PostMessage.SendNotifyMessage,SendMesssgeCallback),消息參數(shù)不能包含指針。否則,操作將會(huì)失敗。函數(shù)將再接收線程處理消息之前返回,發(fā)送者將在內(nèi)存被使用之前釋放。”

我的理解是,就算目標(biāo)進(jìn)程知道你發(fā)來的是個(gè)指針地址,但是2個(gè)進(jìn)程之間的尋址空間是獨(dú)立的,互相不可訪問,怎么能獲取發(fā)送進(jìn)程內(nèi)存空間里的數(shù)據(jù)呢??

關(guān)于WM_QUIT消息


窗口回調(diào)函數(shù)里不可能接到WM_QUIT消息。因?yàn)閺南⒘嘘?duì)里GetMessage()收到WM_QUIT消息就返回0,消息循環(huán)就會(huì)結(jié)束,所以DispatchMessage()也不可能再把這個(gè)消息分發(fā)到窗口的回調(diào)函數(shù)。這就是為什么,書里一再強(qiáng)調(diào)要在WM_DESTORY的消息事件里加上PostQuitMessage()的原因。如果不加,程序只是銷毀窗口,但是進(jìn)程任然存在。消息循環(huán)還在運(yùn)行,但是因?yàn)榇翱谝呀?jīng)銷毀,所以他不可能再從消息列隊(duì)里取得任何消息。

使用PostQuitMessage()與PostMessage()發(fā)送消息的不同
前者把消息列隊(duì)里的QS_QUIT標(biāo)志打開,并且等待程序處理完消息列隊(duì)里的所有消息后才結(jié)束消息循環(huán)。
后者是把WM_QUIT直接放到消息列隊(duì),消息循環(huán)取到得下一個(gè)消息是WM_QUIT就立即退出。MSDN里不建議使用PostMessage發(fā)送WM_QUIT消息,因?yàn)檫@樣會(huì)造成程序的收尾工作無法進(jìn)行,正常退出后所需要的資源釋放等操作就沒法執(zhí)行了。

用SendMessage無法發(fā)送WM_QUIT消息,因?yàn)镾endMessage并不是吧消息放入消息列隊(duì),所以,GetMessage根本無法得到SendMessage發(fā)送的消息。

BOOL PostThreadMessage(DWORD dwThreadId,UINT uMsg,WPARAM wParam,LPARAM lParam);
這個(gè)函數(shù)和PostMessage類似,都是發(fā)送完消息立即返回,不同的是這個(gè)函數(shù)向指定的ThreadId發(fā)送一條消息。這個(gè)函數(shù)發(fā)送的消息不回被分配到目標(biāo)進(jìn)程窗口的回調(diào)函數(shù),因?yàn)楫?dāng)消息放入列隊(duì)時(shí),MSG的hwnd被設(shè)置為NULL,沒有窗口句柄,DispatchMessage能把消息分配給誰呢?PostThreadMessage也可以發(fā)送WM_QUIT消息,消息會(huì)放到隊(duì)列的尾端。在qs_input之前處理該消息。
PostMessage 和PostThreadMessage發(fā)送WM_QUIT消息都會(huì)造成窗口的首尾代碼無法執(zhí)行。用的時(shí)候需要注意下。

LRESULT SendMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
SendMessage同步發(fā)送消息,發(fā)送進(jìn)程要等待目標(biāo)進(jìn)程窗口的回調(diào)函數(shù)處理完成消息后才能恢復(fù)運(yùn)行,調(diào)用點(diǎn)線程在等待SendMessage返回的過程中是掛起狀態(tài),本身也無法響應(yīng)任何操作。
發(fā)送進(jìn)程再等待的過程中,如果系統(tǒng)中其他的進(jìn)程向等待進(jìn)程發(fā)送消息,則發(fā)送進(jìn)程立即處理消息。

Windows提供了其他的4個(gè)API來進(jìn)行進(jìn)程間的消息發(fā)送。

LRESULT SendMessageTimeout(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT fuFlags,UINT fuTimeout,PDWORD_PTR pdwResult);

fuFlags參數(shù)由下列標(biāo)志組成
SMTO_ABORTIFHUNG??? 如果目標(biāo)進(jìn)程處于掛起狀態(tài)立即返回。
SMTO_BLOCK????? 發(fā)送進(jìn)程在SendMessageTimeout返回之前不處理任何消息
SMTO_NORMAL?????? 0值,如果不適用其他標(biāo)志,就是用這個(gè)標(biāo)志?
SMTO_NOTIMEOUTIFNOTHUNG? 如果目標(biāo)進(jìn)程未處于掛起狀態(tài)不考慮fuTimeout限定等待值

fuTimeout參數(shù)指定等待時(shí)間單位毫秒
pdwResult 指向一段內(nèi)存區(qū)域,保存返回結(jié)果。如果用SendMessageTimeout本身線程的窗口則直接調(diào)用窗口的回調(diào)函數(shù),并且將結(jié)果保存在pdwResult中。

BOOL SendMessageCallback(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,SENDASYNCPROC lpCallback,ULONG_PTR dwData);
lpCallback 參數(shù) 指向一個(gè)CALLBACK函數(shù),定義如下
VOID CALLBACK ResultBack(HWND hwnd,UINT uMsg,ULONG_PTR dwData,LRESULT lResult);

發(fā)送線程使用SendMessageCallback發(fā)送消息到接受線程的發(fā)送消息列隊(duì),并理解返回。當(dāng)接收線程處理完消息后,用一個(gè)消息登記到發(fā)送線程的應(yīng)答消息隊(duì)列,然后系統(tǒng)調(diào)用ResultBack函數(shù)通知發(fā)送進(jìn)程。前2個(gè)參數(shù)是接受線程窗口的句柄,消息值,第三個(gè)參數(shù)dwData就是SendMessageCallback中最后一個(gè)參數(shù),lResult參數(shù)是接受消息窗口回調(diào)函數(shù)的返回值。
接收進(jìn)程處理完SendMessageCallback函數(shù)后先在發(fā)送進(jìn)程消息列隊(duì)登記應(yīng)答消息,發(fā)送進(jìn)程在下一次調(diào)用GetMessage,PeekMessage時(shí),執(zhí)行ResultBack函數(shù)

Bool SendNotifyMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
SendNotifyMessage將消息放到接收線程的發(fā)送消息列隊(duì)(QS_SENDMESSAGE)中,并且立即返回。和PostMessage類似,但不同的是。
發(fā)送消息列隊(duì)的優(yōu)先級比登記列隊(duì)(QS_POSTMESSAGE)的優(yōu)先級高。所以SendNotifyMessage發(fā)送的消息比PostMessage發(fā)送的消息處理的早。
向進(jìn)程發(fā)送窗口消息時(shí),SendNotifyMessage效果和SendMessage完全一樣,等待消息處理完之后才返回。

BOOL ReplyMessage(LRESULT lResult);
這個(gè)函數(shù)是用于接收線程窗口的回調(diào)函數(shù)中,調(diào)用ReplyMessage后,發(fā)送線程恢復(fù)運(yùn)行。

判斷消息類型
BOOL InSendMessage();
如果當(dāng)前消息是進(jìn)程間消息,返回TRUE,如果是進(jìn)程內(nèi)消息返回FALSE;

DWORD InSendMessageEx(PVOID pvReserved);
這個(gè)函數(shù)返回正在執(zhí)行的消息類型。返回值如下:
ISMEX_NOSEND??? 消息是線程內(nèi)部消息
ISMEX_SEND??? 消息是用SendMessage或SendMessageTimeout發(fā)送的進(jìn)程間消息
ISMEX_NOTIFY??? 消息是SendNotifyMessage發(fā)送的進(jìn)程間消息
ISMEX_CALLBACK? 消息是SendMessageCallBack發(fā)送的進(jìn)程間消息
ISMEX_REPLIED??? 消息是是進(jìn)程間消息,并且已經(jīng)調(diào)用ReplyMessage

消息隊(duì)列的狀態(tài)標(biāo)志
DWORD GetQueueStatus(UINT fuFlags);

參數(shù)fuFlags是由一組標(biāo)志聯(lián)合起來的值,用來查看特定的喚醒隊(duì)列標(biāo)志。
QS_KEY????? WM_KEYUP、WM_KEYDOWN、WM_SYSKEYUP或WM_SYSKEYDOWN
QS_MOUSE??? MOVEWM_MOUSEMOVE
QS_MOUSEBUTTON? WM_?BUTTON*(其中?代表L、M或R、*代表DOWN、UP或DBLCLK)
QS_MOUSE??? 同QS_MOUSEMOVE|QS_MOUSEBUTTON
QS_INPUT??? 同QS_MOUSE|QS_KEY
QS_PAINT??? WM_PAINT
QS_TIMER??? WM_TIMER
QS_HOTKEY??? WM_HOTKEY
QS_POSTMESSAGE? 登記的消息(不同于硬件輸入事件)。當(dāng)隊(duì)列在期望的消息過濾器范圍內(nèi)沒有登記
????? 的消息時(shí),這個(gè)標(biāo)志要消除。除此之外,這個(gè)標(biāo)志與QS_ALLPOSTMESSAGE相同

QS_ALLPOSTMESSAGE? 登記的消息(不同于硬件輸入事件)。當(dāng)隊(duì)列完全沒有登記的消息時(shí)(在任何消息
????? 過濾器范圍),該標(biāo)志被清除。除此之外,該標(biāo)志與QS_POSTMESSAGE相同

QS_ALLEVENTS??? 同QS_INPUT|QS_POSTMESSAGE|QS_TIMER|QS_PAINT|QS_HOTKEY
QS_QUIT??? 已調(diào)用PostQuitMessage。注意這個(gè)標(biāo)志沒有公開,所以在WinUser.h文件中沒有。它由系統(tǒng)在內(nèi)部使用
QS_SENDMESSAGE? 由另一個(gè)線程發(fā)送的消息
QS_ALLINPUT??? 同QS_ALLEVENTS|QS_SENDMESSAGE

消息類型存放在回值的高字節(jié)中(2個(gè)字節(jié)),低字節(jié)儲(chǔ)存還沒有處理的消息類型。

上面幾個(gè)函數(shù)都是用來發(fā)送消息,很多函數(shù)不是常用的,但多了解幾個(gè)函數(shù)沒有壞處,了解的東西越多,遇到問題解決的辦法也就越多。

鍵盤,鼠標(biāo)消息

windows程序與用戶的互交都是通過鼠標(biāo)鍵盤實(shí)現(xiàn)的,所以必須要了解windows是如何處理鍵盤鼠標(biāo)消息的。

首先,發(fā)生的鍵盤鼠標(biāo)消息是先報(bào)錯(cuò)在系統(tǒng)消息列隊(duì)的(不是直接發(fā)放到應(yīng)用程序列隊(duì)),當(dāng)應(yīng)用程序處理上一個(gè)輸入消息后,系統(tǒng)消息隊(duì)列才把下一個(gè)輸入消息投放到應(yīng)用程序列隊(duì)。因?yàn)槿绻存I的輸入速度比應(yīng)用程序處理速度快,后來的鍵如果還是發(fā)往當(dāng)前的焦點(diǎn)窗口句柄,那么切換到新窗口后后來輸入的鍵還是會(huì)發(fā)送到先前的窗口,直到上一個(gè)窗口處理完所有的未處理的按鍵消息,按鍵才會(huì)改變發(fā)送的窗口句柄到新窗口。

其次,每一個(gè)按鍵產(chǎn)生2類消息,按鍵消息和字符消息。很顯然,有的按鍵只有按鍵消息沒有字符消息,比如Capslk,Shift等。

按鍵又分為系統(tǒng)按鍵和非系統(tǒng)按鍵,對于系統(tǒng)按鍵,當(dāng)按下一個(gè)鍵發(fā)生WM_SYSKEYDOWN消息,放開這個(gè)鍵發(fā)生WM_SYSKEYUP消息,對于非系統(tǒng)間,按下和放開發(fā)生WM_KEYDOWN和WM_KEYUP消息。
很顯然這些消息都成成對出現(xiàn)的。一個(gè)KEYDOWN,接著就是一個(gè)KEYUP,

對于系統(tǒng)按鍵,通常是windows系統(tǒng)本身比較關(guān)心的消息,系統(tǒng)按鍵通常由ALT快捷鍵產(chǎn)生,Alt tab Alt F4 Alt esc 等等。應(yīng)用程序不處理ALT消息,而是交給DefWindowProc來處理,這就說明應(yīng)用程序的菜單快捷鍵也是由系統(tǒng)處理。系統(tǒng)將Ctrl+s這類的快捷鍵,轉(zhuǎn)換成菜單命令消息,不用自己去處理。

對于所有的4類按鍵消息WM_SYSKEYDOWN WM_SYSKEYUP WM_KEYDOWN WM_KEYUP,wParam參數(shù)保存虛擬鍵代碼,LParam參數(shù)包含按鍵的其他數(shù)據(jù)。

產(chǎn)生虛鍵代碼的原因是因?yàn)樵缙诘逆I碼是由真實(shí)鍵盤產(chǎn)生,叫做"掃描碼",掃描嗎是按照鍵盤的排列順序產(chǎn)生的,比如16 是Q,17是W(數(shù)數(shù)看,呵呵)很明顯這種鍵碼會(huì)因?yàn)殒I盤布局的變化而變化,太過于設(shè)備話,于是通過定義虛擬鍵代碼。
虛擬代碼是一些列VK開頭的定義在winuser.h里的值。例如VK_TAB,VK_RETURN(回車鍵)等等,鍵盤數(shù)字0-9和字母a-z,A-Z就是ASCII的值。小鍵盤上的數(shù)字是VK_NUMPAD0-VK_NUMPAD9,其他的功能鍵都是VK_+鍵的英文含義組成。

lParam參數(shù)的32位分成6個(gè)字段,用于表示不同的消息
00-15位,? 包含按鍵的從重復(fù)次數(shù)。
16-23位,? 包含按鍵的OEM掃描嗎,上面說過掃描嗎。
24?? 位,? 包含按鍵的擴(kuò)充標(biāo)志,這個(gè)標(biāo)準(zhǔn)被windows忽略不用
29?? 位,? 包含按鍵的內(nèi)容代碼,對于系統(tǒng)鍵盤此位是1,對于非系統(tǒng)鍵此位為0
30?? 位,? 包含按鍵的先前狀態(tài),如果鍵是先前釋放的,為0,否則為1.
31?? 位,???? 包含按鍵的轉(zhuǎn)換狀態(tài),如果鍵盤按按下,為0,否則為1。
25-28位未知。

short GetKeyState(int vKey)函數(shù)用來獲得某個(gè)鍵到目前為止的狀態(tài),比如判斷shift是否按下GetKeyState(VK_SHIFT),按下高位時(shí)1,否則是0,GetKeyState(VK_CAPITAL)(Capslk鍵)如果打開低位返回1,注意這個(gè)GetKeyState返回short類型的值16位,不是上面說的LParam的值。GetKeyState不是實(shí)時(shí)檢查狀態(tài)的,指檢查到目前為止的鍵盤狀態(tài)。

short GetAsyncKeyState(int vKey)函數(shù)用來獲取當(dāng)前的某個(gè)鍵的當(dāng)前狀態(tài)。高位為1則當(dāng)前判斷的建被按下,低位返回1則,則按鍵在上次調(diào)用GetasyncKeystate以來狀態(tài)是被按下的。

GetKeyState判斷組合鍵比較合適,因?yàn)榭梢耘袛嗄硞€(gè)鍵到目前為止的狀態(tài),按下了Ctrl再按下S,那么可以在S鍵的處理消息上判斷GetKeyState(VK_LCTRL)是否按下。

GetAsyncKeyState可以用來做個(gè)循環(huán),當(dāng)某個(gè)鍵現(xiàn)在按下,處理某些事情。

字符消息
每當(dāng)一個(gè)鍵被按下,就產(chǎn)生一個(gè)按鍵消息和字符消息,通常我們只關(guān)心字符消息,因?yàn)橥瑯拥陌存I產(chǎn)生的字符有可能是不同的,比如,打開搜狗輸入法按Shit + 4打出的字符是¥,關(guān)閉輸入法打出的是$。
字符消息的wParem參數(shù)是按鍵的ASCII值,所以在回調(diào)函數(shù)中可以if (wParam == 'a')這樣判斷輸入的字符。

鼠標(biāo)消息

鼠標(biāo)按鍵全使用消息,每個(gè)鍵有3個(gè)消息,BUTTONDOWN,BUTTONUP,BUTTONDBLCLK(雙擊),WM_L(左鍵) WM_M(中鍵) WM_R(右鍵)加上三個(gè)消息代表了鼠標(biāo)顯示區(qū)消息。
鼠標(biāo)的移動(dòng)消息是WM_MOUSEMOVE
此時(shí)wParam參數(shù)表示下列的按鍵是否被按下MK_CONTROL MK_LBUTTON MK_MBMTTON MK_RBUTTON MK_SHIFT
lParam低位代表鼠標(biāo)X坐標(biāo),高位代表鼠標(biāo)Y坐標(biāo)。

可以看出SendMessage()發(fā)送的WM_CHAR消息不會(huì)被目標(biāo)進(jìn)程的窗口回調(diào)函數(shù)處理,因?yàn)镾endMessage直接發(fā)送到回調(diào)函數(shù),沒有經(jīng)過TranslateMessage翻譯鍵盤消息。

線程間的數(shù)據(jù)共享

WM_SETTEXT消息
首先說明WM_SETTEXT消息不是一個(gè)用來做進(jìn)程間發(fā)送數(shù)據(jù)用的,這個(gè)消息是用來設(shè)置窗口標(biāo)題,或者按鈕文本,或者Edit控件內(nèi)容的。比如SetWindowText(HWND hwnd,LPCTSTR lpString)(設(shè)置窗口的標(biāo)題)調(diào)用這個(gè)函數(shù)實(shí)際上就是產(chǎn)生了一個(gè)WM_SETTEXT消息,通常由默認(rèn)回調(diào)函數(shù)DefWindowProc來處理。想想就行了,只能發(fā)送一個(gè)字符串有什么用?
但是WM_SETTEXT消息特殊的地方就是,系統(tǒng)為用這個(gè)消息發(fā)送的字符串開辟另外一塊共享內(nèi)存映射空間,使不同進(jìn)程接收消息的線程也能夠收到并且使用這個(gè)字符串。

對應(yīng)的還有一個(gè)WM_GETTEXT消息,這個(gè)消息是從目標(biāo)窗口句柄返回字符串信息,同樣GetWindowText(HWND hwnd,LPCTSTR lpString,int iMaxCount)函數(shù)也是產(chǎn)生一個(gè)WM_GETTEXT消息由DefWindowProc來處理,參數(shù)中多了一個(gè)iMaxCount用來表示字符串的長度。

WM_COPYDATA消息
WM_COPYDATA消息把自定義的一塊數(shù)據(jù)發(fā)送到目標(biāo)線程,目標(biāo)進(jìn)程的窗口回調(diào)函數(shù)中必須有這個(gè)消息的處理方法,否則發(fā)了也沒用。

WM_COPYDATA WMSETTEXT這兩個(gè)數(shù)據(jù)傳遞消息都只能使用SnedMessage()發(fā)送,SnedMessage返回了系統(tǒng)就會(huì)釋放開辟的內(nèi)存空間,用其他的方法發(fā)送,系統(tǒng)不知道目標(biāo)進(jìn)程什么時(shí)候處理消息,所以也無法釋放內(nèi)存映射空間。
可以在Send.asm里加入下面代碼看看如果用PostMessage返回什么錯(cuò)誤提示。
lpBuffer? db? 512? dup (?)?? ;先定義一個(gè)buffer

invoke? PostMessage,hWnd,WM_COPYDATA,0,addr stCopyData? ;試用PostMessage發(fā)送,根本就沒有發(fā)送消息
.if eax == 0
? invoke GetLastError
? invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS,NULL,
??????? eax,LANG_NEUTRAL,offset lpBuffer,sizeof lpBuffer,NULL
??????? invoke MessageBox,NULL,offset lpBuffer,offset szCaption,MB_OK???????
.endif
這個(gè)方法是使用GetLastError函數(shù)先獲得上一次調(diào)用函數(shù)失敗的代碼,然后通過FormatMessage找到錯(cuò)誤代碼的描述,參數(shù)里設(shè)置說明是中文。
大部分的winAPI在調(diào)用失敗后都可以通過GetLastError獲得調(diào)用失敗的錯(cuò)誤代碼。這個(gè)方法很好用,可以及時(shí)了解為什么出錯(cuò)。

?

鍵盤消息的使用

?

可以使用PostMessage給目標(biāo)窗口或者控件發(fā)送鍵盤消息,按鍵消息和字符消息,但是使用SendMessage只能發(fā)送字符消息,而不能發(fā)送按鍵消息,想想為什么?
開始練習(xí)按鍵消息前,必須要先了解2個(gè)函數(shù):
HWND FindWindow(LPCTSTR lpClassName,LPCTSTR lpWindowName);通過lpClassName窗口注冊類名(就是WNDCLASS里的lpszClassName名稱)或者lpWindowName窗口標(biāo)題名獲得窗口句柄。
2個(gè)參數(shù)隨便用一個(gè)就可以,不使用的給NULL。

HWND FindWindowEx(HWND,hwndParent,HWND hwndChildAfter,LPCTSTR lpszClass,LPSTSTR lpszWindow);這個(gè)函數(shù)可以通過窗口句柄和控件類名或者控件標(biāo)題名獲得這個(gè)控件的句柄。
先通過FindWindow得到主窗口句柄,然后通過FindWindowEx得到主窗口內(nèi)某個(gè)控件的句柄。

下面看看如何通過PostMessage給windows記事本發(fā)送按鍵消息
首先找到記事本
szClac? db? 'Notepad',0? 記事本主窗體的類名,可以通過Spy++獲取
szEdit? db? 'Edit',0?? 內(nèi)容用于寫內(nèi)容的Edit控件

hwndnote? db??? ?? 用于保存句柄

invoke FindWindow,offser szCalc,NULL??? ;找到記事本句柄
invoke FindWindowEx,eax,NULL,offset szEdit,0? ;找到edit控件的句柄
mov hwndnote,eax

下面就可以給記事本發(fā)送各種鍵盤消息,比如

invoke SendMessage,hwndnote,WM_KEYDOWN,VK_1,0??? ;發(fā)送一個(gè)按鍵消息1
invoke PostMessage,hwndnote,WM_KEYDOWN,VK_2,0? ;發(fā)送一個(gè)按鍵消息2

invoke SendMessage,hwndnote,WM_CHAR,VK_3,0? ;用SendMessage發(fā)送一個(gè)字符消息3

想象發(fā)送后記事本上的的字符順序是1,2,3么?

發(fā)送一個(gè)組合鍵Alt+E,就是打開記事本的編輯菜單
invoke PostMessage,hWndnd,WM_SYSKEYDOWN,VK_MENU,020000001h? ALT鍵按下
invoke PostMessage,hWndnd,WM_SYSKEYDOWN,VK_E,020000001h? E鍵按下必須要把第29位設(shè)置成1,代表alt鍵已經(jīng)下
invoke PostMessage,hWndnd,WM_SYSCHAR,VK_E,020000001h??? 發(fā)送一個(gè)系統(tǒng)字符E
invoke PostMessage,hWndnd,WM_SYSKEYUP,VK_E,080000001h??? E鍵放開,必須把31位設(shè)置成1,表示這個(gè)是系統(tǒng)鍵
invoke PostMessage,hWndnd,WM_KEYUP,VK_MENU,080000001h??? ALT鍵放開,31位系統(tǒng)鍵設(shè)置成1

這組消息可以通過SPY++監(jiān)視記事本的鍵盤輸入狀態(tài)得到,其實(shí)可以精簡,只用下面2條就可以。
invoke PostMessage,hWndnd,WM_SYSKEYDOWN,VK_E,020000001h? E鍵按下必須要把第29位設(shè)置成1,代表alt鍵已經(jīng)下
invoke PostMessage,hWndnd,WM_SYSKEYUP,VK_E,080000001h??? E鍵放開,必須把31位設(shè)置成1,表示這個(gè)是系統(tǒng)鍵

因?yàn)镋鍵的lParam參數(shù)的29位置1,已經(jīng)說明這個(gè)E在這里表示系統(tǒng)按鍵,29位置1表示ALT鍵已經(jīng)按下。
按鍵彈起的時(shí)候,必須把31位置1,表示這是個(gè)系統(tǒng)鍵彈起。否則會(huì)當(dāng)做普通鍵,并且在記事本里打印出字母e。

現(xiàn)在想出來這組消息后,記事本上會(huì)是什么字符么?答案是:321,前面說過SendMessage的優(yōu)先級高于PostMessage,所以是先打出3,然后是1,最后是2。

關(guān)于windows消息的操作還有很多,這里只舉出了最基本的發(fā)送鍵盤消息的方法。理解這些基本的操作是位日后學(xué)習(xí)使用其他消息操作打下一個(gè)好的基礎(chǔ)。

鼠標(biāo)消息的使用


鍵盤消息只發(fā)送給當(dāng)前擁有輸入焦點(diǎn)的窗口,鼠標(biāo)消息不同,只要鼠標(biāo)達(dá)到,窗口就會(huì)收到鼠標(biāo)消息。當(dāng)鼠標(biāo)在窗口顯示區(qū)域內(nèi),鼠標(biāo)消息的lParam參數(shù)是鼠標(biāo)所在窗口的X,Y坐標(biāo)值,當(dāng)鼠標(biāo)不在窗口顯示區(qū)域內(nèi),參數(shù)lParam是桌面的X,Y坐標(biāo)值。
顯示區(qū)域:是指用戶能夠輸出顯示信息結(jié)果的區(qū)域。非顯示區(qū)域是指:菜單,標(biāo)題欄,滾動(dòng)條

對于顯示區(qū)內(nèi)發(fā)送鼠按鍵消息,wParam參數(shù)指定鼠標(biāo)按鍵以及Shift和Ctrl按鍵的狀態(tài),鍵值如下:
MK_CONTROL 表示ctrl按下 MK_?BUTTON 表示鼠標(biāo)3個(gè)鍵按下 MK_SHIFT 表示shift按下
lParam參數(shù)指定鼠標(biāo)的坐標(biāo)值,高位Y坐標(biāo),低位X坐標(biāo)

下面的例子代碼是使用鍵盤的上下左右方向鍵移動(dòng)鼠標(biāo)光標(biāo),空格鍵發(fā)送鼠標(biāo)單擊消息。可以把SendMessage句柄改成“畫圖”程序句柄,這樣在當(dāng)前窗口按空格鍵,將會(huì)在畫圖程序的同樣位置畫出一個(gè)點(diǎn)。

_MoveMouse proc hwnd,wParam,lParam
? local @szPos [128]:byte
? local @stPoint:POINT
? local @stRect:RECT
? invoke GetCursorPos,addr @stPoint??????????? ;獲得當(dāng)前鼠標(biāo)屏幕坐標(biāo)位置
? invoke ScreenToClient,hwnd,addr @stPoint????????? ;將鼠標(biāo)的屏幕坐標(biāo)位位置轉(zhuǎn)換成當(dāng)前窗口內(nèi)的坐標(biāo)位置
? invoke wsprintf,addr @szPos,offset szMsg,@stPoint.x,@stPoint.y???????
? invoke SetWindowText,hwnd,addr @szPos
? mov eax,wParam
? .if eax == VK_LEFT
??? sub @stPoint.x,1
? .elseif eax == VK_RIGHT
??? add @stPoint.x,1
? .elseif eax == VK_UP
??? sub @stPoint.y,1
? .elseif eax == VK_DOWN
??? add @stPoint.y,1
? .elseif eax == VK_SPACE
??? mov eax,@stPoint.y
??? shl eax,16
??? add eax,@stPoint.x
??? invoke PostMessage,hwnd,WM_LBUTTONDOWN,MK_LBUTTON,eax
??? invoke PostMessage,hwnd,WM_LBUTTONUP,0,eax
? .endif
? invoke ClientToScreen,hwnd,addr @stPoint????????? ;將當(dāng)前窗口坐標(biāo)位置轉(zhuǎn)換成屏幕位置
? invoke SetCursorPos,@stPoint.x,@stPoint.y????????? ;設(shè)置光標(biāo)位置
? ret?
_MoveMouse endp

在窗口的回調(diào)函數(shù)中加入以下代碼:

.elseif eax == WM_KEYDOWN
? mov eax,wParam
? .if wParam == VK_LEFT || wParam || VK_RIGHT || wParam == VK_UP || wParam == VK_DOWN || wParam == VK_SPACE
??? invoke _MoveMouse,hWnd,wParam,lParam
? .endif

對于非顯示區(qū)鼠標(biāo)消息和顯示區(qū)鼠標(biāo)消息類似,消息后加"NC"代碼表示非顯示區(qū)消息,例如WM_NCLBUTTONCLICK
參數(shù)wParam是一些定義在winuser.h里以HT開頭的的非顯示區(qū)域代碼,比如HTCAPTION 代表標(biāo)題欄,HTCLOSE,代表窗口右上角的關(guān)閉按鈕等等。
參數(shù)lParam表示屏幕坐標(biāo),不是顯示區(qū)坐標(biāo),同樣低位是X坐標(biāo),高位是Y坐標(biāo)。

純C寫的FirstWindow和匯編FirstWindow的區(qū)別


同樣的FirstWindow程序,我用C寫了一個(gè),反匯編后比較,發(fā)現(xiàn)反匯編結(jié)果里多了很多編譯器添加的代碼。尺寸也大了不少,查了一些資料,發(fā)現(xiàn)原來這些編譯器添加的代碼就是傳說中的CRT,C語言運(yùn)行時(shí)環(huán)境。

用C寫windows程序,都知道程序從winMain開始執(zhí)行,實(shí)際上在這之前,是有其他的函數(shù)來調(diào)用WinMain的。這個(gè)函數(shù)就叫做入口函數(shù)。
入口函數(shù)對運(yùn)行時(shí)庫和程序運(yùn)行環(huán)境進(jìn)行初始化,包括堆,I/O,線程等等。入口函數(shù)執(zhí)行完后才回去調(diào)用main函數(shù)正式開始執(zhí)行程序,WinMain執(zhí)行完后,返回到入口函數(shù),由入口函數(shù)進(jìn)行清理工作。

這倒也好理解,winMain之前肯定有些東西執(zhí)行了什么,比如winMain的4個(gè)參數(shù),hInstance,szCmdLine,iCmdShow 都是從啟動(dòng)函數(shù)傳給winMain的。
對于我現(xiàn)在使用的vs2008的編譯器來說,入口函數(shù)的代碼位于srt\src\crt0.c文件里。函數(shù)的名稱是__tmainCRTStartup。現(xiàn)在看看里面都干了些什么關(guān)鍵:

首先定義了個(gè)STARTUPINFO StartupInfo結(jié)構(gòu),使用GetStartupInfo(&StartupInfo)初始化。STARTUPINFO結(jié)構(gòu)包含一些進(jìn)程的信息。具體細(xì)節(jié)可以查看msdn.
緊接著初始化堆??? _heap_init(1)
初始化堆是很重要的,否則不能使用C++的new 或c的malloc來分配內(nèi)存。
然后初始化多線程? _mtinit()
然后初始化I/O,_ioinit(),得到命令行參數(shù)GetCommandLineT();得到當(dāng)前進(jìn)程進(jìn)程版本信息

最后調(diào)用啟動(dòng)函數(shù)
WinMain((HINSTANCE) & __ImageBase,NULL,lpszCommandLine,StartupInfo.dwFlags & STARTF_USESHOWWINDOW? StartupInfo.wShowWindow: SW_SHOWDEFAULT);
到這里就可以看見,winMain的參數(shù)是怎么來的了,hInsteance 就是__ImageBase(載入基址),命令行參數(shù)也是傳進(jìn)來的,最后的iCmdShow,參數(shù)就是STRTUPINFO里的顯示方式。

就是因?yàn)榫幾g時(shí)加入了啟動(dòng)函數(shù)所以使C程序編譯出來的可執(zhí)行文件比匯編程的大了30多K。
其實(shí)啟動(dòng)函數(shù)不是必須的,可以自定義一個(gè)自己的啟動(dòng)函數(shù)代替默認(rèn)的啟動(dòng)函數(shù)。
比如定義一個(gè)
int WINAPI main()
{
??? HINSTANCE hInstance = GetModuleHandle(NULL);????? //得到當(dāng)前進(jìn)程的句柄,和匯編一樣
??? LPSTR lpszCmdLine = GetCommandLine();????????? //獲得命令行參數(shù)
??? int r = WinMain(hInstance, NULL, lpszCmdLine, SW_SHOWDEFAULT);? //調(diào)用WinMain函數(shù),就開始執(zhí)行
??? ExitProcess(r);??????????????? //最后結(jié)束進(jìn)程
??? return r; // this will never be reached.
}
需要在link.exe 后加/entry:main /nodefaultlib:msvcrt90.lib參數(shù),/entry指定入口點(diǎn)函數(shù), /nodefaultlib指定不連接運(yùn)行時(shí)庫。

這樣編譯連接后,可執(zhí)行文件尺寸和匯編后的大小一樣。反匯編后比較內(nèi)容也基本差不多。要不說C語言執(zhí)行速度快,編譯后的內(nèi)容和直接用匯編寫的程序基本上一樣。

轉(zhuǎn)載于:https://www.cnblogs.com/dubingsky/archive/2009/07/08/1519317.html

總結(jié)

以上是生活随笔為你收集整理的Win32汇编笔记-消息基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产一区二区小视频 | 99久久精品国产一区二区成人 | 精品成人一区二区三区久久精品 | 综合在线观看 | 无码人妻aⅴ一区二区三区玉蒲团 | 91色多多| 国产肥白大熟妇bbbb视频 | 国产在视频线精品视频 | 欧美亚洲一区二区三区 | 国产乱码精品一品二品 | 亚洲国产精品无码观看久久 | 国产精品久久久久久久蜜臀 | 欧美日本精品 | 国产精品香蕉在线 | 国产一级在线免费观看 | 精品人妻午夜一区二区三区四区 | 国产美女视频一区 | 午夜在线视频免费观看 | 色偷偷中文字幕 | 亚洲人成网站999久久久综合 | 久久亚洲精品视频 | www.4hu95.com四虎| 亚洲一区中文 | 美女露隐私网站 | 成人综合区一区 | 欧美伦理在线观看 | 欧洲一级视频 | 欧美三级久久久 | 亚洲成人一区在线观看 | 激情综 | 性生活网址| 亚洲91视频 | 在线视频免费观看你懂的 | 蜜臀麻豆| 美女精品一区 | 日本韩国免费观看 | 九色porn蝌蚪 | 91九色蝌蚪91por成人 | 调教撅屁股啪调教打臀缝av | 国产精品一区二区av日韩在线 | 91av高清| 做爰视频毛片视频 | 亚洲永久在线 | 台湾佬美性中文娱乐 | 福利久久久 | 91丨九色丨黑人外教 | wwwwww日本| 嫩草一二三 | 激情春色网 | 午夜免费高清视频 | 国产视频久久久久久 | 荒野求生21天去码版网站 | 中文字幕一区二区在线老色批影视 | 亚洲免费一级片 | 日韩欧美视频一区二区 | 男人av在线 | 怡红院国产 | 国产毛片在线看 | 亚洲偷拍一区 | 亚洲一区二区三区网站 | 欧美日韩在线视频一区二区三区 | 欧美另类高清videos的特点 | 国产精品天天操 | 天天尻 | 中文字幕一级二级三级 | 欧美三级黄色 | 亚洲欧美日韩专区 | 国产性生活片 | 亚洲国产精品久久AV | 日韩精品一区在线观看 | 性高潮久久久久久 | 高清无码视频直接看 | 国产日韩在线免费观看 | av有声小说一区二区三区 | 无码成人一区二区 | 久久一区二区三区四区五区 | 亚洲成人网在线播放 | 黄色的视频网站 | 天堂va欧美ⅴa亚洲va一国产 | 99中文字幕在线观看 | 国产av人人夜夜澡人人爽 | 色综合久久中文字幕无码 | 天堂中文在线最新 | 99嫩草| 性生交大全免费看 | 日韩免费高清一区二区 | 蜜桃免费av | 日韩av在线电影 | 日韩三级中文字幕 | 国产精品一线 | 欧美在线视频免费 | 日韩欧美精品一区二区三区 | 国产精品v日韩精品v在线观看 | 国产精品一区二区免费 | 亚洲国产成人精品激情在线 | 国产男人搡女人免费视频 | 免费看毛片的网站 | 边添小泬边狠狠躁视频 | 亚洲成a人v欧美综合天堂麻豆 |