• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            luqingfei@C++

            為中華之崛起而崛起!
            兼聽(tīng)則明,偏聽(tīng)則暗。

            Win32匯編--分析窗口程序

             

            分析窗口程序

             

            了解了消息驅(qū)動(dòng)體系的工作流程以后,讓我們來(lái)分析如何用Win32匯編實(shí)現(xiàn)這一切。

             

            模塊和句柄

            模塊的概念

            一個(gè)模塊代表的是一個(gè)運(yùn)行中的exe文件或dll文件,用來(lái)代表這個(gè)文件中所有的代碼和資源,磁盤(pán)上的文件不是模塊,裝入內(nèi)存后運(yùn)行時(shí)就叫做模塊。

             

            一個(gè)應(yīng)用程序調(diào)用其他DLL中的API時(shí),這些DLL文件被裝入內(nèi)存,就產(chǎn)生了不同的模塊,為了區(qū)分地址空間中的不同模塊,每個(gè)模塊都有一個(gè)唯一的模塊句柄來(lái)標(biāo)識(shí)。

             

            很多API函數(shù)中都要用到程序的模塊句柄,以便利用程序中的各種資源,所以在程序中一開(kāi)始就先取得模塊句柄并存放到一個(gè)全局變量中可以省去很多的麻煩,在Win32中,模塊句柄在數(shù)值上等于程序在內(nèi)存中裝入的起始地址。

             

            取模塊句柄使用的API函數(shù)是GetModuleHandle,它的使用方法是:

                   invoke GetModuleHandle, lpModuleName

            lpModuleName參數(shù)是一個(gè)指向含有模塊名稱字符串的指針,可以用這個(gè)函數(shù)取得程序地址空間中各個(gè)模塊的句柄,例如,如果想得到User32.dll的句柄以便使用其中包含的圖標(biāo)資源,那么可以如下使用:

                   szUserDll        db    ‘User32.dll’,0

                   invoke           GetModuleHandle, addr szUserDll

                   .if                  eax

                                        mov hUserDllHandle,eax

                   .endif

            如果使用參數(shù)NULL,調(diào)用GetModuleHandle,那么得到的是調(diào)用者本模塊的句柄,如下所示:

                   invoke            GetModuleHandl,NULL

                   mov               hInstance,eax

                   可以注意到,把返回的句柄放到了hInstance變量里而并不是放在hModule中,為什么是hInstance呢?Instance是“實(shí)例”,它的概念來(lái)自于Win16Win16中不同運(yùn)行程序的地址空間并不是完全隔離的,一個(gè)可執(zhí)行文件運(yùn)行后形成“模塊”,多次加載同一個(gè)可執(zhí)行文件時(shí),這個(gè)“模塊”是公用的,為了區(qū)分多次加載的“拷貝”,就把每個(gè)“拷貝”叫做實(shí)例,每個(gè)實(shí)例均用不同的“實(shí)例句柄”(hInstance)值來(lái)標(biāo)識(shí)它們。

             

            但在Win32中,程序運(yùn)行時(shí)是隔離的,每個(gè)實(shí)例都使用自己私有的4GB空間,都認(rèn)為自己是唯一的,不存在一個(gè)模塊的多個(gè)實(shí)例的問(wèn)題,實(shí)際上在Win32中,實(shí)例句柄就是模塊句柄,但很多API原型中用到模塊句柄的時(shí)候使用的名稱還是沿用hInstance,所以我們還是把變量名稱取為hInstance

             

            C++語(yǔ)言的編程中,hInstance通過(guò)WinMain由系統(tǒng)傳入,WinMain的原型是:

                   WinMain(hInstance, hPrevInstance, lpszCmdParam, nCmdShow)

            程序不用自己去獲得hInstance,但在Win32匯編中必須自己獲取,如果不了解hModule就是hInstance的話,就無(wú)法得知如何得到hInstance,因?yàn)椴](méi)有一個(gè)類似于GetInstanceHandle之類的API函數(shù)。

             

             

            句柄是什么

            隨著分析的深入,句柄(handle)一詞也出現(xiàn)得頻繁了起來(lái),“句柄”是什么呢?句柄只是一個(gè)數(shù)值而已,它的值對(duì)程序來(lái)說(shuō)是沒(méi)有意義的,它只是Windows用來(lái)表示各種資源的編號(hào)而已,所以只有Windows才知道怎么使用它來(lái)引用各種資源。

             

            舉例說(shuō)明,屏幕上已經(jīng)有10窗口,Windows把它們從110編號(hào),應(yīng)用程序又建立了一個(gè)窗口,現(xiàn)在Windows把它編號(hào)為11,然后把11當(dāng)做窗口句柄返回給應(yīng)用程序,應(yīng)用程序并不知道11代表的是什么,但在操作窗口的時(shí)候,把11當(dāng)做句柄傳給WindowsWindows自然可以根據(jù)這個(gè)數(shù)值查出是哪個(gè)窗口。當(dāng)該窗口關(guān)閉的時(shí)候,11這個(gè)編號(hào)作廢。第二次運(yùn)行的時(shí)候,如果屏幕上現(xiàn)有5個(gè)窗口,那么現(xiàn)在句柄可能就是6了,所以,應(yīng)用程序并不用關(guān)心句柄的具體數(shù)值是多少。打個(gè)比方,可以把句柄當(dāng)做是商場(chǎng)中寄放書(shū)包時(shí)營(yíng)業(yè)員給的紙條,紙條上的標(biāo)記用戶并不知道是什么意思,但把它交還給營(yíng)業(yè)員的時(shí)候,她自然會(huì)找到正確的書(shū)包。

             

            Windows中幾乎所有的東西都是用句柄來(lái)標(biāo)識(shí)的,文件句柄、窗口句柄、線程句柄和模塊句柄等,同樣道理,不必關(guān)心它們的值究竟是多少,拿來(lái)用就是了!

             

             

            創(chuàng)建窗口

            在創(chuàng)建窗口之前,先要談到“類”。“類”的概念讀者都不陌生,主要是為了把一組物體的相同屬性歸納整理起來(lái)封裝在一起,以便重復(fù)使用,在“類”已定義的屬性基礎(chǔ)上加上其他個(gè)性化的屬性,就形成了各式各樣的個(gè)體。

             

            Windows中創(chuàng)建窗口同樣使用這樣的層次結(jié)構(gòu)。首先定義一個(gè)窗口類,然后在窗口類的基礎(chǔ)上添加其他的屬性建立窗口。不同一步到位的辦法是因?yàn)楹芏啻翱诘幕緦傩院托袨槎际且粯拥模绨粹o、文本輸入框和選擇框等,對(duì)這些東西Windows都預(yù)定義了對(duì)應(yīng)的類,使用時(shí)直接使用對(duì)應(yīng)的類名建立窗口就可以了。只有用戶自定義的窗口才需要先定義自己的類,再建立窗口。這樣可以節(jié)省資源。

             

            注冊(cè)窗口類

            建立窗口類的方法是在系統(tǒng)中注冊(cè),注冊(cè)窗口類的API函數(shù)是RegisterClassEx,最后的Ex是擴(kuò)展的意思,因?yàn)樗?/span>Win16RegisterClass的擴(kuò)展。一個(gè)窗口類定義了窗口的一些主要屬性,如:圖標(biāo)、光標(biāo)、背景色、菜單和負(fù)責(zé)處理該窗口所屬消息的函數(shù)。這些屬性并不是分成多個(gè)參數(shù)傳遞過(guò)去的,而是定義在一個(gè)WNDCLASSEX結(jié)構(gòu)中,再把結(jié)構(gòu)的地址當(dāng)參數(shù)一次性傳遞給RegisterClassExWNDCLASSEXWNDCLASS結(jié)構(gòu)的擴(kuò)展。

             

            WNDCLASSEX的結(jié)構(gòu)定義為:

                   WNDCLASSEX     STRUCT

                          CbSize                  DWORD               ?             ;結(jié)構(gòu)的字節(jié)數(shù)

                          Style                     DWORD               ?             ;類風(fēng)格

                          LpfnWndProc        DWORD               ?             ;窗口過(guò)程的地址

                          CbClsExtra            DWORD               ?

                          CbWndExtra          DWORD               ?

                          HInstance              DWORD               ?             ;所屬的實(shí)例句柄

                          HIcon                   DWORD               ?             ;窗口圖標(biāo)

                          HCursor                DWORD               ?             ;窗口光標(biāo)

                          HbrBackground      DWORD               ?             ;背景色

                          LpszMenuName     DWORD               ?             ;窗口菜單

                          LpszClassName      DWORD               ?             ;類名字符串的地址

                          HIconSm               DWORD               ?             ;上圖標(biāo)

                   WNDCLASSEX    ENDS

             

             

            Win32匯編源程序中,注冊(cè)窗口類的代碼如下:

                   local        @stWndClass:WNDCLASSEX            ;定義一個(gè)WNDCLASSEX結(jié)構(gòu)

             

                   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

            程序定義了一個(gè)WNDCLASSEX結(jié)構(gòu)的變量@stWndClass,用RtlZeroMemory將它填為全零,再填寫(xiě)結(jié)構(gòu)的各個(gè)字段,這樣,沒(méi)有賦值的部分就保持為0,結(jié)構(gòu)各字段的含義如下:

            hIcon      圖標(biāo)句柄,指定顯示在窗口標(biāo)題欄左上角的圖標(biāo)。Windows已經(jīng)預(yù)定課外作業(yè)了一些圖標(biāo),同樣,程序也可以使用在資源文件中定義的圖標(biāo),這些圖標(biāo)的句柄可以用LoadIcon函數(shù)獲得。例子程序沒(méi)有用到圖標(biāo),所以Windows給窗口顯示了一個(gè)默認(rèn)的圖標(biāo)。

             

            hCursor   光標(biāo)句柄,指定了鼠標(biāo)在窗口中光標(biāo)形狀。同樣,Windows也預(yù)定義了一些光標(biāo),可以用LoadCursor獲取它們的句柄,IDC_ARROWWindows預(yù)定義的箭頭光標(biāo),如果想使用自定義的光標(biāo),也可以自己在資源文件中定義。

             

            lpszMenuName      指定窗口上顯示的默認(rèn)菜單,它指向一個(gè)字符串,描述資源文件中菜單的名稱,如果資源文件中菜單是用數(shù)值定義的,那么這里使用菜單資源的數(shù)值。窗口中的菜單也可以在建立窗口函數(shù)CreateWindowsEx的參數(shù)中指定。如果在兩個(gè)地方都沒(méi)有指定,那么建立的窗口上就沒(méi)有菜單。

             

            hInstance              指定要注冊(cè)的窗口類屬于哪個(gè)模塊,模塊句柄在程序開(kāi)始的地方已經(jīng)用GetModuleHandle函數(shù)獲得。

             

            cbSize            指定WNDCLASSEX結(jié)構(gòu)的長(zhǎng)度,用sizeof偽操作符來(lái)獲取。很多Win32 API參數(shù)中的結(jié)構(gòu)都有cbSize字段,它主要是用來(lái)區(qū)分結(jié)構(gòu)的版本,當(dāng)以后新增了一個(gè)字段時(shí),cbSize就相應(yīng)增大,如果調(diào)用的時(shí)候cbSize還是老的長(zhǎng)度,表示運(yùn)行的是基于舊結(jié)構(gòu)的程序,這樣可以防止使用無(wú)效的字段。

             

            style        窗口風(fēng)格。CS_HREDRAWCS_VREDRAW表示窗口的寬度或高度改變時(shí)是否重畫(huà)窗口。比較重要的是CS_DBLCLKS風(fēng)格,指定了它,Windows才會(huì)把在窗口中快速兩次單擊鼠標(biāo)的行為翻譯為雙擊消息WM_LBUTTONDBLCLK發(fā)給窗口過(guò)程。

             

            hbrBackground      窗口客戶區(qū)的背景色。前面的hbr表示它是一個(gè)刷子(Brush)的句柄,刷子一詞形象地表示了填充一個(gè)區(qū)域的著色模式。Windows預(yù)定義了一些刷子,如BLACK_BRUSHWHITE_BRUSH等,可以用下列語(yǔ)句來(lái)得到它們的句柄:

                   invoke     GetStockObject, WHITE_BRUSH

            但在這里也可以使用顏色值,Windows已經(jīng)預(yù)定義了一些顏色值,分別對(duì)應(yīng)窗口各部分的顏色,如COLOR_BACKGROUNDCOLOR_HIGHLIGHTCOLOR_MENUCOLOR_WINDOWS等,使用顏色值的時(shí)候,Windows規(guī)定必須在顏色值上加1,所以在程序中的指令是:

                   mov        @stWndClass.hbrBackground, COLOR_WINDOWS + 1

             

            lpszClassName指定程序員要建立的類命名,以便以后用這個(gè)名稱來(lái)引用它。這個(gè)字段是一個(gè)字符串指針,在程序里,它指向MyClass字符串中。

             

            cbWndExtracbClsExtra分別是在Windows內(nèi)部保存的窗口結(jié)構(gòu)和類結(jié)構(gòu)中給程序員預(yù)留的空間大小,用來(lái)存放自定義的數(shù)據(jù),它們的單位是字節(jié)。不使用自定義數(shù)據(jù)的話,這兩個(gè)字段就是0

             

            lpfnWndProc         是最重要的參數(shù),它指定了基于這個(gè)類建立的窗口的窗口過(guò)程地址。通過(guò)這個(gè)參數(shù),Windows就知道了在DispatchMessage函數(shù)中把窗口消息發(fā)到哪里去,一個(gè)窗口過(guò)程可以為多個(gè)窗口服務(wù),只要這些窗口是基于同一個(gè)窗口類建立的。Windows中不同應(yīng)用程序中的按鈕和文本框的行為都是一樣的,就是因?yàn)樗鼈兪腔谙嗤?/span>Windows預(yù)定義類建立的,它們背后的窗口過(guò)程其實(shí)是同一段代碼。

             

            結(jié)構(gòu)中的style表示窗口的風(fēng)格,Windows已經(jīng)有一些預(yù)定義的值,它們是以CS_(Class Style的縮寫(xiě))開(kāi)始的標(biāo)識(shí)符,如下所示:

            CS_VREDRAW                    00000001H

            CS_HREDRAW                    00000002H

            CS_KEYCVTWINDOWS      00000004H

            CS_DBLCLKS                     00000008H

            CS_OWNDC                       00000020H

            CS_CLASSDC                     00000040H

             

            可以看到,這些預(yù)定義值實(shí)際上在使用不重復(fù)的數(shù)據(jù)位,所以可以組合起來(lái)使用,同時(shí)使用不同的預(yù)定義值并不會(huì)引起混淆。

             

            注意:對(duì)于不同二進(jìn)制位組合的計(jì)算,“加”和“或”的結(jié)果是一樣的,在FirstWindows程序中用CS_HREDRAW or CS_VREDRAW來(lái)代表兩個(gè)組合,若用CS_HREDRAW + CS_VREDRAW也并沒(méi)有什么不同,但強(qiáng)烈建議使用or,因?yàn)槿绻恍⌒闹付藘蓚€(gè)同樣的風(fēng)格時(shí):CS_HREDRAW or CS_VREDRAW or CS_VREDRAW和原來(lái)的數(shù)值是一樣的,而CS_HREDRAW + CS_VREDRAW + CS_VREDRAW就不對(duì)了,因?yàn)?/span>1 or 1 = 1,而1 + 1就等于2了。

             

             

            建立窗口

            接下來(lái)的步驟是在已經(jīng)注冊(cè)的窗口類的基礎(chǔ)上建立窗口,使用“類”的原因是定義窗口的“共性”,建立窗口時(shí)肯定還要指定窗口的很多“個(gè)性化”的參數(shù),如WNDCLASSEX結(jié)構(gòu)中沒(méi)有定義的外觀、標(biāo)題、位置、大小和邊框類型等屬性,這些屬性是在建立窗口時(shí)才指定的。

             

            和注冊(cè)窗口類時(shí)用一個(gè)結(jié)構(gòu)傳遞所有參數(shù)不同,建立窗口時(shí)所有的屬性都是用單個(gè)參數(shù)的方式傳遞的,建立窗口的函數(shù)是CreateWindowEx(注意不要寫(xiě)成CreateWindowsEx),同樣,它是Win16中的CreateWindow函數(shù)的擴(kuò)展,主要表現(xiàn)在多了一個(gè)dwExStyle(擴(kuò)展風(fēng)格)參數(shù),原因是Win32Win16中多了很多種窗口風(fēng)格,原來(lái)的一個(gè)風(fēng)格參數(shù)已經(jīng)不夠用了。

            CreateWindowEx函數(shù)的使用方法是:

                   invoke     CreateWindowEx, dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam

            12個(gè)參數(shù)很好理解:

            lpClassName          建立窗口使用的類名字符串指針,在FirstWindow程序中指向MyClass字符串,表示使用MyClass類建立窗口,這正是我們自己注冊(cè)的類,這樣一來(lái),這個(gè)窗口就有“MyClass”的所有屬性,并且消息將被發(fā)到“MyClass”中指定的窗口過(guò)程中去,當(dāng)然,這里也可以是Window預(yù)定義的類名。

             

            lpWindowName      指向表示窗口名稱的字符串,該名稱會(huì)顯示在標(biāo)題欄上。如果該參數(shù)空白,則標(biāo)題欄上什么都沒(méi)有。

             

            hMenu    窗口上要出現(xiàn)的菜單的句柄。在注冊(cè)窗口類的時(shí)候也定義了一個(gè)菜單,那是窗口的默認(rèn)菜單,意思是如果這里沒(méi)有定義菜單(用參數(shù)NULL)而注冊(cè)窗口類時(shí)定義了菜單,則使用窗口類中定義的菜單;如果這里指定了菜單句柄,則不管窗口類中有沒(méi)有定義都將使用這里定義的菜單;兩個(gè)地方都沒(méi)有定義菜單句柄,則窗口上沒(méi)有菜單。另外,當(dāng)建立的窗口是子窗口時(shí)(dwStyle中指定了WS_CHILD),這個(gè)參數(shù)是另一個(gè)含義,這時(shí)hMenu參數(shù)指定的是子窗口的ID號(hào),這樣可以節(jié)省一個(gè)參數(shù)的位置,因?yàn)榉凑哟翱诓粫?huì)有菜單。

             

            hpParam         這是一個(gè)指針,指向一個(gè)欲傳給窗口的參數(shù),這個(gè)參數(shù)在WM_CREATE消息中可以被獲取,一般情況下用不到這個(gè)字段。

             

            hInstance              模塊句柄,和注冊(cè)窗口類時(shí)一樣,指定了窗口所屬的程序模塊。

             

            hWndParent           窗口所屬的父窗口,對(duì)于普通窗口(相對(duì)于子窗口),這里的“父子”關(guān)系只是從屬關(guān)系,主要用來(lái)在父窗口銷毀時(shí)一同將其“子”窗口銷毀,并不會(huì)把窗口位置限制在父窗口的客戶區(qū)范圍內(nèi),但如果要建立的是真正的子窗口(dwStyle中指定了WS_CHILD的時(shí)候),這時(shí)窗口位置會(huì)被限制在父窗口的客戶區(qū)范圍內(nèi),同時(shí)窗口的坐標(biāo)(xy)也是以父窗口的左上角為基準(zhǔn)的。

             

            xy       指定窗口左上角位置,單位是像素。默認(rèn)時(shí)可指定為CW_USEDEFAULT,這樣Windows會(huì)自動(dòng)為窗口指定最合適的位置,當(dāng)建立子窗口時(shí),位置是以父窗口的左上角為基準(zhǔn)的,否則,以屏幕左上角為基準(zhǔn)。

             

            nWidthhHeight           窗口的寬度和高度,也就是窗口的大小,同樣是以像素為單位的。默認(rèn)時(shí)可指定為CW_USEDEFAULT,這樣Windows會(huì)自動(dòng)為窗口指定最合適的大小。

             

            窗口的兩個(gè)參數(shù)dwStyledwExStyle決定了窗口的外形和行為,dwStyle是從Win16開(kāi)始就有的屬性,下表列出了一些常見(jiàn)的dwStyle定義,它們是一些以WSWindows Style的縮寫(xiě))為開(kāi)頭的預(yù)定義值。

            預(yù)定義值

            16進(jìn)制值

            含義

            WS_OVERLAPPED

            00000000h

            普通的重疊式窗口

            WS_POPUP

            80000000h

            彈出式窗口(沒(méi)有標(biāo)題欄)

            WS_CHILD

            40000000h

            子窗口

            WS_MINIMIZE

            20000000h

            初始狀態(tài)是最小化的

            WS_VISIBLE

            10000000h

            初始狀態(tài)是可見(jiàn)的

            WS_DISABLED

            08000000h

            初始狀態(tài)是被禁止的

            WS_MAXIMIZE

            01000000h

            初始狀態(tài)是最大化的

            WS_BORDER

            00800000h

            單線條邊框

            WS_DLGFRAME

            00400000h

            對(duì)話框類型的邊框

            WS_VSCROLL

            00200000h

            帶垂直滾動(dòng)條

            WS_HSCROLL

            00100000h

            帶水平滾動(dòng)條

            WS_SYSMENU

            00080000h

            帶系統(tǒng)菜單(即帶標(biāo)題欄左上角的圖標(biāo))

            WS_THICKFRAME

            00040000h

            可以拖動(dòng)調(diào)整大小的邊框

             

            為了容易理解,Windows也為一些定義取了一些別名,同時(shí),由于窗口的風(fēng)格往往是幾種風(fēng)格的組合,所以Windows也預(yù)定義了一些組合值,如下表所示:

            預(yù)定義值

            等效值

            WS_CHILDWINDOW

            WS_CHILD

            WS_TILED

            WS_OVERLAPPED

            WS_ICONIC

            WS_MINIMIZE

            WS_SIZEBOX

            WS_THICKFRAME

            WS_OVERLAPPEDWINDOW

            WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or WS_THICKFRAME or WS_MINIMIZEBOX or WS_MAXINIZEBOX

            WS_TILEDWINDOW

            WS_OVERLAPPEDWINDOW

            WS_POPUPWINDOW

            WS_POPUP or WS_BORDER or WS_SYSMENU

             

            dwExStyleWin32中擴(kuò)展的,它們是一些以WS_EX_開(kāi)頭的預(yù)定義值,主要定義了一些特殊的風(fēng)格,如下表所示:

            預(yù)定義值

            16進(jìn)制值

            含義

            WS_EX_POPMOST

            00000008h

            總在頂層的窗口

            WS_EX_ACCEPTFILES

            00000010h

            允許窗口進(jìn)行鼠標(biāo)拖放操作

            WS_EX_TOOLWINDOW

            00000080h

            工具窗口(很窄的標(biāo)題欄)

            WS_EX_WINDOWEDGE

            00000100h

            立體體的邊框

            WS_EX_CLIENTEDGE

            00000200h

            客戶區(qū)立體邊框

            WS_EX_OVERLAPPDWINDOW

             

            WS_EX_WINDOWEDGE or WS_EX_CLIENTEDGE

            WS_EX_PALETTEWINDOW

             

            WS_EX_POPMOST

             

            用預(yù)定義的組合值WS_EX_PALETTEWINDOW可以很方便地構(gòu)成浮在其他窗口前面的工具欄。

             

            建立窗口的相關(guān)代碼如下:

                   invoke            CreateWindowEx, WS_EX_CLIENTEDGE, \

                                        offset szClassName, offset szCaptionMain, \

                                        WS_OVERLAPPPEDWINDOW, \

                                        100, 100, 600, 400, \

                                        NULL, NULL, hInstance, NULL

                   mov               hWinMain,eax

                   invoke            ShowWindow, hWinMain, SW_SHWONORMAL

                   invoke            UpdateWindow, hWinMain

             

            建立窗口以后,eax中傳回來(lái)的是窗口句柄,要把它保存起來(lái),這時(shí)候,窗口雖已建立,但還沒(méi)有在屏幕上顯示出來(lái),要用ShowWindow把它顯示出來(lái),ShowWindow也可以用在另的地方,主要用來(lái)控制窗口的顯示狀態(tài)(顯示或隱藏),大小控制(最大化、最小化或原始大小)和是否激活(當(dāng)前窗口還是背后的窗口),它用窗口句柄第一個(gè)參數(shù),第二個(gè)參數(shù)則是顯示的方式。顯示方式有如下的預(yù)定義值:

            預(yù)定義值

            等效值

            SW_HIDE

            隱藏窗口,大小不變,激活狀態(tài)不變

            SW_MAXIMIZE

            最大化窗口,顯示狀態(tài)不變,激活狀態(tài)不變

            SW_MINIMIZE

            最小化窗口,顯示狀態(tài)不變,激活狀態(tài)不變

            SW_RESTORE

            從最大化或最小化恢復(fù)正常大小,顯示狀態(tài)不變,激活狀態(tài)不變

            SW_SHOW

            顯示并激活窗口,大小狀態(tài)不變

            SW_SHOWMAXIMIZED

            顯示并激活窗口,以最大化顯示

            SW_SHOWMINIMIZED

            顯示并激活窗口,以最小化顯示

            SW_SHOWMINOACTIVE

            顯示窗口并最小化,激活狀態(tài)不變

            SW_SHOWNA

            顯示窗口,大小狀態(tài)不變,激活狀態(tài)不變

            SW_SHOWNOACTIVATE

            顯示并從最大化或最小化恢復(fù)正常大小,激活狀態(tài)不變

            SW_SHOWNORMAL

            顯示并激活窗口,恢復(fù)正常大小(初始化時(shí)用這個(gè)參數(shù))

             

            窗口顯示以后,用UpdateWindow繪制客戶區(qū),它實(shí)際上就是向窗口發(fā)送了一條WM_PAINT消息。到此為止,一個(gè)頂層窗口就正常建立并顯示了。

             

            CreateWindowEx也可以用來(lái)建立子窗口,Windows中有很多預(yù)定義的子窗口類,如按鈕和文本框的類名分別是ButtonEdit。要建立一個(gè)按鈕,只要把lpClassName指向Button字符串就可以了。例如:

                                               .data

                   szButton                db           ‘button’,0

                   szButtonText          db           ‘&OK’,0

             

                          invoke     CreateWindowEx, NULL, \

                                        offset szButton, offset szButtonText, \

                                        WS_CHILD or WS_VISIBLE, \   

                                        10, 10, 65, 22, \

                                        hWnd, 1, hInstance, NULL

             

            FirstWindow的源程序中加入按鈕類的定義字符串“szButton”和按鈕文字字符串“szButtonText”,然后在窗口過(guò)程的WM_CREATE消息中加入建立按鈕的代碼,執(zhí)行一下,窗口中就出現(xiàn)了一個(gè)按鈕。建立按鈕的時(shí)候,lpWindowName參數(shù)就是按鈕上的文字,風(fēng)格則一定要指定WS_CHILD,建立的按鈕才會(huì)在我們的主窗口上,WS_VISIBLE也要同時(shí)指定,否則按鈕不會(huì)顯示出來(lái),hMenu參數(shù)在這里用做表示子窗口ID,將它設(shè)置為1,在建立多個(gè)子窗口的時(shí)候,ID應(yīng)該有所區(qū)別。

             

             

            消息循環(huán)

            消息循環(huán)的一般形式:

                   .while      TRUE

                                 invoke     GetMessage, addr @stMsg, NULL, 0, 0

                                 .break     .if eax == 0

                                 invoke     TranslateMessage, addr @stMsg

                                 invoke     DispatchMessage, addr @stMsg

                   .endw

             

            消息循環(huán)中的幾個(gè)函數(shù)要用到一個(gè)MSG結(jié)構(gòu),用來(lái)做消息傳遞:

                   MSG       STRUCT

                          Hwnd      DWORD        ?

                          Message DWORD        ?

                          WParam DWORD        ?

                          LParam   DWORD        ?

                          Time       DWORD        ?

                          Pt           POINT           <>

                   MSG       ENDS

            各個(gè)字段的含義是:

            hwnd      消息要發(fā)向的窗口句柄。

            message 消息標(biāo)識(shí)符,在頭文件中以WM_開(kāi)頭的預(yù)定義值(意思為Windows Message)。

            wParam 消息的參數(shù)之一。

            lParam    消息的參數(shù)之二。

            time        消息放入消息隊(duì)列的時(shí)間。

            pt           這是一個(gè)POINT的數(shù)據(jù)結(jié)構(gòu),表示消息放入消息隊(duì)列時(shí)的鼠標(biāo)坐標(biāo)。

            這個(gè)結(jié)構(gòu)定義了消息的所有屬性,GetMessage函數(shù)就是從消息隊(duì)列中取出這樣一條消息來(lái)的:

                          invoke     GetMessage, lpMsg, hWnd, wMsgPilterMin, wMsgFilterMax

            函數(shù)的lpMsg指向一個(gè)MSG結(jié)構(gòu),函數(shù)會(huì)在這里返回取到的消息,hWnd參數(shù)指定要獲取哪個(gè)窗口的消息,例子中指定為NULL表示獲取的是所有本程序所屬窗口的消息wMsgFilterMinwMsgFilterMax0表示獲取所有編號(hào)的消息。

             

            GetMessage函數(shù)從消息隊(duì)列里取得消息,填寫(xiě)好MSG結(jié)構(gòu)并返回,如果獲取的消息是WM_QUIT消息,那么eax中的返回值是0,否則eax返回非零值,所以用.break .if eax == 0來(lái)檢查返回值,如果消息隊(duì)列中有WM_QUIT則退出消息循環(huán)。

             

            TranslateMessageMSG結(jié)構(gòu)傳給Windows進(jìn)行一些鍵盤(pán)消息的轉(zhuǎn)換,當(dāng)有鍵盤(pán)按下和放開(kāi)時(shí),Windows產(chǎn)生WM_KEYDOWNWM_KEYUPWM_SYSKEYDOWNWM_SYSKEYUP消息,但這些消息的參數(shù)中包含的是按鍵的掃描碼,轉(zhuǎn)換成常用的ASCII碼要經(jīng)過(guò)查表,很不方便,TranslateMessage遇到鍵盤(pán)消息則將掃描碼轉(zhuǎn)換成ASCII碼并在消息隊(duì)列中插入WM_CHARWM_SYSCHAR消息,參數(shù)就是轉(zhuǎn)換好的ASCII碼,如此一來(lái),要處理鍵盤(pán)消息的話只要處理WM_CHAR消息就好了。遇到別的消息則TranslateMessage不做處理。

             

            最后,由DispatchMessage將消息發(fā)送到窗口對(duì)應(yīng)的窗口過(guò)程去處理。窗口過(guò)程返回后DispatchMessage函數(shù)才返回,然后開(kāi)始新一輪消息循環(huán)。

             

             

            其它形式的消息循環(huán):

            GetMessage函數(shù)是程序空閑的時(shí)候主動(dòng)將控制權(quán)交還給Windows的一種方式Windows是一個(gè)搶占式的多任務(wù)系統(tǒng),任務(wù)之間每20ms切換一次,試想一下,如果窗口程序在主窗口中采用死循環(huán)等待,消息由Windows直接發(fā)送到窗口過(guò)程,那么程序會(huì)是下列這種樣子:

                   invoke     CreateWindow,

                   invoke     ShowWindow,

                   invoke     UpdateWindow,

                   .while      dwQuitFlag == 0           ;要退出時(shí)在窗口過(guò)程中設(shè)置dwQuitFlag

                   .endw

                   invoke     ExitProcess,

             

            但這樣一來(lái),即使程序在空閑狀態(tài),輪到自己的20ms時(shí)間片的時(shí)候,CPU時(shí)間就會(huì)全部消耗在.while循環(huán)中,使用GetMessage的時(shí)候,輪到應(yīng)用程序時(shí)間片的時(shí)候,如果消息隊(duì)列里還沒(méi)有消息,那么程序還是停留在GetMessage內(nèi)部,這時(shí)就可以由Windows當(dāng)家作主沒(méi)收這20ms的時(shí)間片,如此保證了CPU資源的合理應(yīng)用。

             

            如果應(yīng)用程序想把所有時(shí)間充分用回來(lái),消息隊(duì)列里沒(méi)有消息的時(shí)候不讓GetMessageWindows內(nèi)部等待,拱手交出屬于自己的CPU時(shí)間,那么消息循環(huán)可以是下列這種樣子:

                   .while      TRUE

                          invoke     PeekMessage, addr @stMsg, NULL, 0, 0, PM)REMOVE

                          .if           eax

                                        .break     .if @stMsg.message == WM_QUIT

                                        invoke     TranslateMessage, addr @stMsg

                                        invoke     DispatchMessage, addr @stMsg

                          .else

                                        <做其他工作>

                          .endif

                   .endw

            PeekMessage是一個(gè)類似于GetMessage的函數(shù),區(qū)別在于當(dāng)消息隊(duì)列里有消息的時(shí)候,PeekMessage取回消息,并在eax中返回非零值,沒(méi)有消息的時(shí)候它會(huì)直接返回,并在eax中返回零。所以在返回非零值的時(shí)候,程序檢查消息是否是WM_QUIT,是則結(jié)束消息循環(huán),不是則用標(biāo)準(zhǔn)流程處理消息;返回零的時(shí)候,表示是空閑時(shí)間,程序就可以做其他工作了,但插入做其他工作的代碼執(zhí)行時(shí)間不能過(guò)長(zhǎng),以不越過(guò)10ms為好,否則會(huì)影響正常的消息處理,使窗口的反應(yīng)看起來(lái)很遲鈍。如果必須處理很長(zhǎng)時(shí)間的工作,那么應(yīng)該將它分成很多小部分處理,以便有足夠的頻率來(lái)用PeekMessage來(lái)檢查消息。PeekMessage的前面4個(gè)參數(shù)和GetMessage是相同的,增加了最后一個(gè)參數(shù),PM_REMOVE表示取回消息的同時(shí)從消息隊(duì)列里刪除,否則用PM_NOREMOVE

             

             

            窗口過(guò)程

            窗口過(guò)程是給Windows回調(diào)用的,它必須遵循規(guī)定的格式。對(duì)窗口過(guò)程的子程序名并沒(méi)有規(guī)定,對(duì)Windows來(lái)說(shuō),窗口過(guò)程的地址才是唯一需要的,例子程序中的子程序名是_ProcWinMain,讀者可以改用任何名稱。窗口過(guò)程子程序的參數(shù)格式為:

                   WindowProc   proc        hwnd, uMsg, wParam, lParam

            第一個(gè)參數(shù)是窗口句柄,一個(gè)窗口過(guò)程可能為多個(gè)基于同一個(gè)窗口類的窗口服務(wù),所以Windows回調(diào)的時(shí)候必須指出要操作的窗口,否則窗口過(guò)程不知道要去處理哪個(gè)窗口,FirstWindow程序只建立了一個(gè)窗口,所以每次傳遞過(guò)來(lái)的hwnd和用CreateWindowEx函數(shù)返回的窗口句柄是一樣的;

            第二個(gè)參數(shù)是消息標(biāo)識(shí),后面兩個(gè)參數(shù)是消息的兩個(gè)參數(shù)。這4個(gè)參數(shù)和消息循環(huán)中MSG結(jié)構(gòu)中的前4個(gè)字段是一樣的。

             

            窗口過(guò)程的結(jié)構(gòu)

            窗口過(guò)程一般有如下結(jié)構(gòu):

            WindowsProc        proc        uses ebx edi esi, hWnd, uMsg, wParam, lParam

             

                                        mov        eax,uMsg

                                        .if           eax == WM_XXX

                                                      <處理WM_XXX消息>

                                        .elseif      eax == WM_YYY

                                                      <處理WM_YYY消息>

                                        .elseif      eax == WM_CLOSE

                                                      invoke     DestroyWindow, hWinMain

                                                      invoke     PostQuitMessage, NULL

                                        .else

                                                      invoke     DefWindowProc, hWnd, uMsg, wParam, lParam

                                                      ret

                                        .endif

             

                                        mov        eax,eax

                                        ret

            WindowProc          endp

             

            該過(guò)程主要是對(duì)uMsg參數(shù)中的消息編號(hào)構(gòu)成一個(gè)分支結(jié)構(gòu),對(duì)于需要處理的消息分別處理。不感興趣的消息則交給DefWindowProc來(lái)處理。

             

            要注意的是窗口過(guò)程中要注意保存ebxediesiebp寄存器,高級(jí)語(yǔ)言中不用自己操心這一點(diǎn),匯編中就要注意了,Windows內(nèi)部將這4個(gè)寄存器當(dāng)指針使用,如果返回時(shí)改變了它們的值,程序會(huì)馬上崩潰。proc后面的uses偽操作在子程序進(jìn)入和退出時(shí)自動(dòng)按插上pushpop寄存器指令,來(lái)保護(hù)這些寄存器的值。其實(shí)不僅是在窗口過(guò)程中是這樣,所有由應(yīng)用程序提供給Windows的回調(diào)函數(shù)都必須遵循這個(gè)規(guī)定,如定時(shí)器回調(diào)函數(shù)等,所有Win32 API也遵循這個(gè)規(guī)定,所以調(diào)用API后,ebxediesiebp寄存器的值總是不會(huì)被改變的,但ecxedx的值就不一定了。

             

            uMsg參數(shù)指定的消息有一定的范圍,Windows標(biāo)準(zhǔn)窗口中已經(jīng)預(yù)定義的值在0~03ffh之間(1024個(gè)),用戶可以自定義一些消息,通過(guò)SendMessage等函數(shù)傳給窗口過(guò)程做自定義的處理工作,這時(shí)可以使用的值是從0400h開(kāi)始的,WM_USER就定義為00000400h,當(dāng)程序員定義多個(gè)用戶消息的時(shí)候,一般使用WM_USER+1WM_USER+2,之類的定義方法。

             

            wParamlParam參數(shù)是消息所附帶的參數(shù),它隨消息的不同而不同,對(duì)于不同的消息,它們的含義必須分別從手冊(cè)中查明:如WM_MOUSEMOVE消息中,wParam是標(biāo)志,lParam是鼠標(biāo)位置;而在WM_GETTEXT消息中,wParam是要獲取的字符數(shù),lParam是緩沖地址;而對(duì)于WM_COPY消息來(lái)說(shuō),它不需要額外的信息,所以兩個(gè)參數(shù)都沒(méi)有定義。

             

            處理了不同的消息,必須返回規(guī)定的值給Windows,返回值也需要分別從手冊(cè)中查明,比如處理WM_CREATE消息的時(shí)候,如果返回0表示成功;如果程序無(wú)法初始化,如申請(qǐng)內(nèi)存失敗,那么可以返回-1Windows就不會(huì)繼續(xù)窗口的創(chuàng)建過(guò)程。一些消息的返回值則沒(méi)有定義,但大部分的消息處理以后都以返回0表示成功,所以程序中把默認(rèn)的返回語(yǔ)句放在最后,將eax清零后返回,如果在處理某個(gè)消息的時(shí)候需要返回不同的值,可以以分支中將eax賦值后直接用ret指令返回。對(duì)于DefWindowProc的返回值,我們不對(duì)它進(jìn)行干涉,所以直接將eax不做修改地用ret返回。

             

            WM_CLOSE消息是按下了窗口右上角的“關(guān)閉”按鈕后收到的,程序可以在這里處理和關(guān)閉窗口相關(guān)的事情,一般是相關(guān)資源的釋放工作,如釋放內(nèi)存、保存工作和提示用戶是否保存工作等,如記事本程序在未保存的時(shí)候單擊“關(guān)閉”按鈕,會(huì)有提示框提示是否先保存文件,單擊“取消”按鈕的話,記事本不會(huì)關(guān)閉,這個(gè)步驟就是在WM_CLOSE消息處理中完成的。如果處理WM_CLOSE消息時(shí)直接返回,那么窗口不會(huì)關(guān)閉,因?yàn)檫@個(gè)消息只是Windows通知窗口用戶剛才單擊了“關(guān)閉”按鈕而已,窗口采用什么樣的行為是窗口的事。當(dāng)窗口決定關(guān)閉的時(shí)候,需要程序自己調(diào)用DestroyWindow來(lái)摧毀窗口,并用PostQuitMessage向消息循環(huán)發(fā)送WM_QUIT消息來(lái)退出消息循環(huán)。調(diào)用PostQuitMessage時(shí)的參數(shù)是退出碼,就是GetMessage收到的WM_QUITMSG結(jié)構(gòu)wParam字段中的東西,在這里使用NULL

             

            PostQuitMessage是初學(xué)者容易遺漏的函數(shù),如果沒(méi)有這條語(yǔ)句,外觀上窗口是從屏幕上消失了,但主程序中的消息循環(huán)卻沒(méi)有收到WM_QUIT,結(jié)果還在那里打轉(zhuǎn)。常有人調(diào)試的時(shí)候丟了這條語(yǔ)句,結(jié)果再一次編譯的時(shí)候就收到錯(cuò)誤:LINK fatal error LNK1104:cannot open file “xxx.exe”,表示exe文件現(xiàn)在不可寫(xiě)。

             

            Windows為什么不在窗口摧毀的時(shí)候自動(dòng)發(fā)送一個(gè)WM_QUIT消息,而必須由用戶程序自己通過(guò)PostQuitMessage函數(shù)發(fā)送呢?其實(shí)很好理解:因?yàn)槠聊簧峡赡懿恢挂粋€(gè)窗口,Windows無(wú)法確定哪個(gè)窗口的關(guān)閉代表著程序結(jié)束。試想一下,用戶打開(kāi)了一個(gè)輸入?yún)?shù)的小窗口,單擊“確定”按鈕后關(guān)閉并回到主窗口,Windows卻不分三七二十一自動(dòng)發(fā)送了一個(gè)WM_QUIT,程序就會(huì)莫名其妙地退出了。

             

             

            收到消息的順序

            窗口過(guò)程收到消息是有一定順序的,收到第一條消息并不是從消息循環(huán)開(kāi)始以后,而是在CreateWindowEx中就開(kāi)始了,顯示和刷新窗口的函數(shù)ShowWindowUpdateWindow也向窗口過(guò)程發(fā)送消息,這一點(diǎn)并不奇怪,因?yàn)?/span>WindowsCreateWindowEx前調(diào)用RegisterClassEx的時(shí)候就已經(jīng)得到窗口過(guò)程的地址了。并且在建立窗口的過(guò)程中需要窗口過(guò)程的配合。

            下面分別列出調(diào)用CreateWindowExShowWindow的時(shí)候窗口過(guò)程收到的消息。

             

            調(diào)用CreateWindowEx時(shí)窗口過(guò)程收到的消息

            消息發(fā)生

            說(shuō)明

            WM_GETMINMAXINFO

            獲取窗口大小,以便初始化

            WM_NCCREATE

            非客戶區(qū)開(kāi)始建立

            WM_NCCALCSIZE

            計(jì)算客戶區(qū)大小

            WM_CREATE

            窗口建立

             

            調(diào)用ShowWindow時(shí)窗口過(guò)程收到的消息

            消息發(fā)生

            說(shuō)明

            WM_SHOWWINDOW

            顯示窗口

            WM_WINDOWPOSCHANGING

            窗口位置準(zhǔn)備改變

            WM_ACTIVATEAPP

            窗口準(zhǔn)備激活

            WM_NCACTIVATE

            激活狀態(tài)改變

            WM_GETTEXT

            取窗口名稱(顯示標(biāo)題欄用)

            WM_ACTIVATE

            窗口準(zhǔn)備激活

            WM_SETFOCUS

            窗口獲得焦點(diǎn)

            WM_NCPAINT

            需要繪畫(huà)窗口邊框

            WM_ERASEBKGND

            需要擦除背景

            WM_WINDOWPOSCHANGED

            窗口益已經(jīng)改變

            WM_SIZE

            窗口大小已經(jīng)改變

            WM_MOVE

            窗口位置已經(jīng)移動(dòng)

             

            然后程序執(zhí)行UpdateWindow,這個(gè)函數(shù)向窗口過(guò)程發(fā)送一條WM_PAINT消息,接著,主程序開(kāi)始進(jìn)入消息循環(huán),Windows根據(jù)各種因素給窗口過(guò)程發(fā)送相應(yīng)的消息,一直到調(diào)用DestroyWindow為止。

            調(diào)用DestroyWindow時(shí)窗口過(guò)程收到的消息

            消息發(fā)生

            說(shuō)明

            WM_NCACTIVATE

            窗口激活狀態(tài)改變

            WM_ACTIVATE

            窗口準(zhǔn)備非激活

            WM_ACTIVATEAPP

            窗口準(zhǔn)備非激活

            WM_KILLFOCUS

            失去焦點(diǎn)

            WM_DESTROY

            窗口即將被摧毀

            WM_NCDESTROY

            窗口的非客戶區(qū)及所有子窗口已經(jīng)被摧毀

             

            在所有這些階段的消息中,大部分的消息都不需要程序自己關(guān)心,Windows只是盡義務(wù)通知窗口過(guò)程而已,窗口過(guò)程轉(zhuǎn)手就交給DefWindowProc去處理了。程序需要關(guān)心的消息有下面這些,可以根據(jù)需要選擇使用:

            WM_CREATE 放置窗口初始化代碼,如建立各種子窗口(狀態(tài)欄和工具欄等)。

            WM_SIZE             放置位置安排的代碼,因?yàn)榻⒌淖哟翱诳赡苄枰S窗口大小的改變而移動(dòng)位置。

            WM_PAINT          如果需要自己繪制客戶區(qū),則在這里安排代碼。

            WM_CLOSE         向用戶確認(rèn)是否退出,如果退出則摧毀窗口并發(fā)送WM_QUIT消息。

            WM_DESTROY    窗口摧毀,在這里放置釋放資源等掃尾代碼。

             

            在例子程序中,我們處理了WM_PAINT消息來(lái)繪制客戶區(qū),功能就是在窗口中的中間寫(xiě)上一行字:Win32 Assembly, Simple and powerful! 過(guò)程是先通過(guò)BeginPaint獲取窗口客戶區(qū)的“設(shè)備環(huán)境”句柄,然后通過(guò)GetClientRect獲取客戶區(qū)的大小,最后通過(guò)DrawText函數(shù)將字符串按照取得的屏幕大小居中寫(xiě)到“設(shè)備環(huán)境”中,也就是窗口上。如果不需要顯示這個(gè)字符串,則連WM_PAINT消息也不用處理。

             

             

            消息的默認(rèn)處理:DefWindowProc

            Windows預(yù)定義的消息范圍是0~03ffh,共1024個(gè)消息,查看一下頭文件Windows.inc,可以發(fā)現(xiàn)實(shí)際已定義的消息數(shù)目有幾百個(gè),這些消息中的大部分對(duì)于窗口的運(yùn)行來(lái)說(shuō)都是必需的,如果窗口過(guò)程要處理每一種消息,那么窗口過(guò)程中的elseif語(yǔ)句就會(huì)綿延數(shù)千行,但是窗口的行為就是由處理這些消息的方法來(lái)表現(xiàn)的,不處理又不行,怎么辦呢?

             

            實(shí)際上大部分窗口的行為都是差不多的,這意味著如果要窗口過(guò)程處理全部的消息,不同窗口的窗口過(guò)程代碼應(yīng)該是大同上異的,那么可以用一個(gè)模塊來(lái)以默認(rèn)的方式處理消息,Win32中的DefWindowProc函數(shù)實(shí)現(xiàn)的就是這個(gè)功能。

             

            不要小看了這個(gè)DefWindowProc,正是它用默認(rèn)的方式處理了幾百種消息,才使用戶能用區(qū)區(qū)百來(lái)行代碼寫(xiě)出一個(gè)全功能的窗口。也正是所有的窗口都用DefWindowProc默認(rèn)處理程序自己不處理的消息,才使它們的行為看上去大同小異,因?yàn)樗鼈儽澈髮?shí)際上是同一塊代碼在處理。

             

            在窗口過(guò)程的分支語(yǔ)句中,用戶處理所有需要個(gè)性化處理的消息,對(duì)于表現(xiàn)行為是默認(rèn)行為的消息,則在else分支中用DefWindowProc來(lái)處理,對(duì)于Windows來(lái)說(shuō),它并不關(guān)心消息在窗口過(guò)程中是程序用自己的代碼處理的還是用DefWindowProc處理的,它只看eax中的返回值來(lái)了解處理結(jié)果,所以不管消息是誰(shuí)處理的,都必須在eax中返回正確的值。DefWindowProc返回時(shí)eax中就是它對(duì)消息的處理結(jié)果,程序只要直接把eax傳回給Windows就行了,所以在例子程序中,DefWindowProc后面直接用一句ret指令返回。

             

            DefWindowProc中對(duì)一些消息的處理方法,如果和用戶期望的不同,就必須在窗口過(guò)程中自己處理。

             

            DefWindowProc對(duì)一些消息的默認(rèn)處理方式

            消息

            DefWindowProc的處理方式

            WM_PAINT

            發(fā)送WM_ERASEBKGND消息來(lái)擦除背景

            WM_ERASEBKGND

            用窗口類結(jié)構(gòu)中的hbrBackground刷子來(lái)繪畫(huà)窗口背景

            WM_CLOSE

            調(diào)用DestroyWindow來(lái)摧毀窗口

            WM_NCLBUTTONDBLCLK

            這是非客戶區(qū)(如標(biāo)題欄)鼠標(biāo)雙擊消息,DefWindowProc測(cè)試鼠標(biāo)的位置,然后再采取相應(yīng)的措施,如標(biāo)題欄雙擊將最大化和恢復(fù)窗口

            WM_NCLBUTTONUP

            這非客戶區(qū)鼠標(biāo)標(biāo)題釋放消息,同樣,DefWindowProc測(cè)試鼠標(biāo)的位置然后再采取相應(yīng)的措施,如鼠標(biāo)在“關(guān)閉”按鈕的位置釋放將導(dǎo)致發(fā)送WM_CLOSE消息

            WM_NCPAINT

            非客戶區(qū)繪制消息,DefWindowProc將繪制邊框和客戶區(qū)

             

            從這些默認(rèn)的處理方法可以看出,想要一個(gè)窗口和別的窗口看起來(lái)不一樣,比如想要窗口看起來(lái)像蘋(píng)果機(jī)的窗口一樣,并且把關(guān)閉按鈕移到標(biāo)題欄最左邊去,那么可以自己處理WM_NCPAINT消息,把非客戶區(qū)畫(huà)成蘋(píng)果機(jī)窗口的樣子,并把關(guān)閉按鈕畫(huà)到標(biāo)題欄左邊去,并且自己處理WM_NCLBUTTONUP消息,當(dāng)檢測(cè)到鼠標(biāo)按下的位置在自己的關(guān)閉按鈕上的時(shí)候,則發(fā)送WM_CLOSE消息。對(duì)別的消息的處理思路也可以按這種方法類推。

             

            另外,可以發(fā)現(xiàn)DefWindowProc對(duì)WM_CLOSE的默認(rèn)處理是調(diào)用DestroyWindow摧毀窗口,DestroyWindow會(huì)引發(fā)一個(gè)WM_DESTROY消息,WM_CLOSEWM_DESTROY的不同之處是:WM_CLOSE代表用戶有關(guān)閉的意向,窗口過(guò)程有權(quán)不“服從”,但收到WM_DESTROY的時(shí)候窗口已經(jīng)在關(guān)閉過(guò)程中了,不管窗口過(guò)程愿不愿意,窗口的關(guān)閉已經(jīng)是不可挽回的事了。

             

            對(duì)于這兩個(gè)消息,窗口過(guò)程必須處理其中的一個(gè),因?yàn)楸仨氂袀€(gè)地方發(fā)送WM_QUIT消息來(lái)結(jié)束消息循環(huán),例子程序中處理WM_CLOSE消息,在其中用DestroyWindow摧毀窗口,再調(diào)用PostQuitMessage結(jié)束消息循環(huán);程序也可以不處理WM_CLOSE消息,讓DefWindowProc以默認(rèn)處理的方式摧毀窗口,但這時(shí)候必須處理WM_DESTROY消息,在其中調(diào)用PostQuitMessage發(fā)送WM_QUIT以結(jié)束消息循環(huán)。

             

             

            posted on 2010-08-17 11:49 luqingfei 閱讀(2972) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Win32匯編程語(yǔ)言序設(shè)計(jì)

            導(dǎo)航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計(jì)

            留言簿(6)

            隨筆分類(109)

            隨筆檔案(105)

            Blogers

            Game

            Life

            NodeJs

            Python

            Useful Webs

            大牛

            搜索

            積分與排名

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久久无码精品亚洲日韩蜜臀浪潮| 久久这里只有精品18| 亚洲中文字幕久久精品无码喷水| 精品熟女少妇av免费久久| 久久精品成人| 精品乱码久久久久久久| 手机看片久久高清国产日韩| A狠狠久久蜜臀婷色中文网| 亚洲国产日韩欧美久久| 久久精品一区二区三区不卡| 狠狠色丁香久久婷婷综合| 久久久精品国产亚洲成人满18免费网站| 麻豆成人久久精品二区三区免费| 久久久久亚洲爆乳少妇无| 久久综合88熟人妻| 久久人人爽人人爽人人片AV东京热 | 久久夜色撩人精品国产小说| 国产精品女同久久久久电影院| 国产精品久久婷婷六月丁香| 品成人欧美大片久久国产欧美| 精品无码久久久久久午夜| 久久人做人爽一区二区三区| 人人狠狠综合久久亚洲| 国产高潮国产高潮久久久91| av无码久久久久久不卡网站| 亚洲AV无码一区东京热久久| 午夜精品久久久久久影视riav | 成人国内精品久久久久一区| 精品综合久久久久久98| 性做久久久久久久久| 久久精品三级视频| 99久久精品九九亚洲精品| 国产99精品久久| 国产成人精品久久二区二区| 国产高潮国产高潮久久久| 精品国际久久久久999波多野| 色欲av伊人久久大香线蕉影院| 久久久久国产精品人妻| 久久精品一本到99热免费| 国产精品久久久久a影院| 国产欧美久久久精品影院|