子窗口控件
本節課程中,我們將討論控件。這些控件作爲是我們輸入輸出的設備。
理論:
Windows提供了幾個預定義的窗口類供我們窗口使用。大多數時間內我們把它們用在對話框上,所以我們把它們叫做子窗口控件。子窗口控件會自己處理消息,並在自己狀態發生改變時通知父窗口。這樣大大減輕了我們編程的工作,我們應儘可能的利用它們。本課中我們把這些控件放在窗口中簡化程序。但是大多數情況下子窗口控件都是放在對話框中的。我們示例中演示的子窗口控件包括: 按鈕、下拉菜單、檢查框、單選按鈕、編輯框等。使用子窗口控件時,先調用CreateWindow函數或CreateWindowEx函數。這裏由於windows已經註冊了這些子控件,所以我們無需在註冊。當然我們也不能改變它們的類名稱。譬如你如果想產生一個按鈕控件,則必須指令類名爲“Button”。其他必須制定的參數還有父窗口的句柄和將要產生子窗口的控件ID。子窗口的控件ID號是用來標示子控件的,故也必須是唯一的。子窗口控件產生後,當狀態發生改變時會像其父窗口發送消息。一般我們應在WM_CREATE消息中產生子控件。子控件向父窗口發送的消息是WM_COMMAND消息,並傳遞的wparam的低二位字節是包括控件的ID,消息號在wparam高兩位字節,lparam中則是子控件的窗口句柄。各類控件有不同的消息代碼集,詳情參見Win32 API參考手冊。父窗口也可以通過調用函數SendMessage向子控件發送消息,其中第一個參數是子控件的窗口句柄,第二個參數是要發送的消息號。附加的參數可以在wparam和lparam中傳遞,其實只要知道,其實只要知道某個窗口的句柄就可以用該函數發送消息相關消息。所以產生子窗口後必須處理WM_COMMAND消息以便可以接受到子控件的消息。
例子:
我們將產生一個窗口,在該窗口中有一個編輯框和一個按鈕。當你按下按鈕時,會彈出一個消息框顯示你在編輯框中輸入的內容。另外該應用程序還有四個菜單項。
1. say hello 把一段字符串輸入到編輯框中。
2. Clear Edit Box 清除編輯框中的字符串。
3. Get Text 彈出對話框顯示編輯空中的內容。
4. Exit 退出程序
代碼:
format PE GUI 4.0
include 'win32ax.inc'
macro memmov [dst, src]
{
common
push [src]
pop [dst]
}
.data
;**************數據********************
szClassName db 'first Window',0
szWndName db 'My first program',0
szMenuName db 'MyMenu',0
szTestString db 'Wow! i am in an edit box now', 0
EditName db 'edit',0
ButtonName db 'button',0
ButtonText db 'My first button',0
szBuffer db 50 dup (?)
hInstanse rd 1
hIcon rd 1
hCursor rd 1
hWndow rd 1
lpCommand rd 1
hButton rd 1
hEdit rd 1
IDM_TEXT equ 1
IDM_HELLO equ 2
IDM_CLEAR equ 3
IDM_EXIT equ 4
BUTTONID = 5
EDITID = 6
.text
entry $
xor edi, edi
invoke GetModuleHandle, edi
or eax, eax
jz .fail
mov [hInstanse], eax
invoke GetCommandLine,edi
mov [lpCommand], eax
stdcall _WndMain, hInstanse, edi, [lpCommand],SW_SHOWDEFAULT
.fail:
invoke ExitProcess,NULL
proc _WndMain hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
local @wc:WNDCLASSEX
local @Msg:MSG
local @hMenu:DWORD
invoke RtlZeroMemory, addr @wc, sizeof.WNDCLASSEX
invoke LoadIcon, NULL,IDI_ASTERISK
mov [hIcon], eax
invoke LoadCursor, NULL,IDC_ARROW
mov [hCursor], eax
mov [@wc.cbSize], sizeof.WNDCLASSEX
mov [@wc.style], CS_HREDRAW or CS_VREDRAW
mov [@wc.lpfnWndProc], _WndProc
mov [@wc.cbClsExtra], NULL
mov [@wc.cbWndExtra], NULL
memmov @wc.hInstance, hInstance
memmov @wc.hIcon, hIcon
memmov @wc.hCursor, hCursor
mov [@wc.hCursor], hCursor
mov [@wc.hbrBackground], COLOR_WINDOW + 1
mov [@wc.lpszMenuName], NULL
mov [@wc.lpszClassName], szClassName
invoke RegisterClassEx, addr @wc
invoke LoadMenu,[hInstanse], szMenuName
mov [@hMenu], eax
invoke CreateWindowEx, WS_EX_CLIENTEDGE, addr szClassName, szWndName, WS_OVERLAPPEDWINDOW,/
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,/
NULL, [@hMenu] , [hInstanse] , NULL
mov [hWndow], eax
invoke ShowWindow,[hWndow],SW_SHOWNORMAL
invoke UpdateWindow,[hWndow]
.GetMsg:
invoke GetMessage,addr @Msg,NULL, 0, 0
or eax, eax
je .QUIT
invoke TranslateMessage,addr @Msg
invoke DispatchMessage,addr @Msg
jmp .GetMsg
.QUIT:
mov eax, [@Msg.wParam]
ret
endp
proc _WndProc uses ebx esi edi, hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
cmp [uMsg], WM_DESTROY
je finish
cmp [uMsg], WM_COMMAND
je .command
cmp [uMsg], WM_CREATE
je .create
invoke DefWindowProc,[hWnd],[uMsg],[wParam], [lParam]
ret
.create:
invoke CreateWindowEx,NULL, ButtonName, ButtonText, /
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, /
75, 70, 140, 25, [hWnd] , BUTTONID, [hInstanse], NULL
or eax, eax
je faild
mov [hButton], eax
invoke CreateWindowEx,WS_EX_CLIENTEDGE, EditName, NULL,/
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, /
50, 35, 200, 25, [hWnd], EDITID, [hInstanse], NULL
mov [hEdit], eax
invoke SetFocus, [hEdit]
jmp Myend
.command:
mov eax ,[wParam]
and eax, 0FFFFh
cmp eax, IDM_TEXT
je _text
cmp eax, IDM_HELLO
je _hello
cmp eax, IDM_CLEAR
je _clear
cmp eax, IDM_EXIT
je _exit
jmp Myend
_text:
invoke GetWindowText,[hEdit], szBuffer, 50
invoke MessageBox, NULL, szBuffer, 'test', MB_OK
jmp Myend
_hello:
invoke SetWindowText, [hEdit], szTestString
jmp Myend
_clear:
invoke SetWindowText, [hEdit], NULL
jmp Myend
_exit:
invoke DestroyWindow, [hWnd]
jmp Myend
faild:
invoke MessageBox,NULL, 'faild', 'test', MB_OK
jmp Myend
finish:
invoke PostQuitMessage, NULL
Myend:
xor eax, eax
ret
endp
.import
library kernel32, 'kernel32.dll',/
user32, 'user32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'
section '.rsrc' data readable resource from 'resourcename.RES'
分析:
.create:
invoke CreateWindowEx,NULL, ButtonName, ButtonText, /
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, /
75, 70, 140, 25, [hWnd] , BUTTONID, [hInstanse], NULL
or eax, eax
je faild
mov [hButton], eax
invoke CreateWindowEx,WS_EX_CLIENTEDGE, EditName, NULL,/
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, /
50, 35, 200, 25, [hWnd], EDITID, [hInstanse], NULL
mov [hEdit], eax
invoke SetFocus, [hEdit]
jmp Myend
我們在WM_CREATE消息中建立子窗口控件,在上面我也以及說了,我們一般都是在WM_CREATE消息中創建子窗口控件的。 我們通過CreatWindowEx函數來創建子窗口控件,因爲這些子窗口控件都是windows預定義的類,這些類都是註冊過的,且它們內部自己處理消息,當子窗口控件發生改變時向我們的父窗口發送WM_COMMADN消息。 創建子窗口控件沒什麼好講的,我們在建立編輯框的時候給它附加了一個WS_EX_CLIENTEDGE外觀,這使得看起來編輯下凹,具有立體感。每一個子控件的id都是預定義的,例如按鈕的預定義類名是“button”,編輯框的預定義類名是“edit”。接下來的參數是窗口風格,除了通常的風格外,每一個控件也有自己的擴展風格,譬如按鈕類的擴展風格前面加BS_,編輯框的類則是ES_。Win32 API參考中所有的風格描述,注意:您在CreateWindowsEx函數中本來要傳遞菜單句柄的地方傳入子窗口空間的ID號不會有什麼副作用,因爲子窗口控件本身不能有菜單。產生控件後,我們保存它們的句柄,然後調用SetFocus把焦點設到編輯控件上以便用戶立即可以輸入。接下來的是如何處理控件發送的通知消息WM_COMMAND。
.command:
mov eax ,[wParam]
and eax, 0FFFFh
cmp eax, IDM_TEXT
je _text
cmp eax, IDM_HELLO
je _hello
cmp eax, IDM_CLEAR
je _clear
cmp eax, IDM_EXIT
je _exit
jmp Myend
_text:
invoke GetWindowText,[hEdit], szBuffer, 50
invoke MessageBox, NULL, szBuffer, 'test', MB_OK
jmp Myend
_hello:
invoke SetWindowText, [hEdit], szTestString
jmp Myend
_clear:
invoke SetWindowText, [hEdit], NULL
jmp Myend
_exit:
invoke DestroyWindow, [hWnd]
jmp Myend
faild:
invoke MessageBox,NULL, 'faild', 'test', MB_OK
jmp Myend
這裏我們通過判斷菜單項的ID號來進行處理。如果是選擇say hello菜單,則通過調用SetWindowText設置編輯框一段字符串。 如果選擇 Get Text 菜單則通過 GetWindowText函數獲得編輯框中的內容並保存到緩衝區中。然後通過MessageBox顯示緩衝區中的內容。其次如果選擇的是Clear Edit Box消息,則調用SetWindowText設置編輯框的內容爲NULL,(則此時編輯框的內容爲空)。 如果選擇exit菜單項,則調用DestroyWindow函數銷燬窗口,從而結束程序。
cmp ax, BUTTONID
je _Button
_Button:
shr eax, 16
cmp eax, BN_CLICKED
jne Myend
invoke SendMessage,[hWnd] , WM_COMMAND, IDM_TEXT, NULL
jmp Myend
上面的片段是處理用戶按鈕事件的。他首先檢查wParam的低字節看是否是按鈕的ID 號,若是則檢查高字節看發送的消息號是否BN_CLICKED,該消息是在按鈕按下時發送的,如果一切都對,則轉入處理該消息,我們可以從處理消息IDM_GETTEXT處複製全部的代碼,但是更專業的辦法是在發送一條IDM_GETTEXT消息讓主窗口過程處理,這隻要把傳送的消息設置爲WM_COMMAND,再把wParam的低字節中設置爲IDM_GETTEXT即可。這樣一來您的代碼就簡潔了許多,所以儘可能利用該技巧。最後,當然不是或有或無,必須在消息循環中調用函數TranslateMessage,因爲您的應用程序需要在編輯框中輸入可讀的文字。如果省略了該函數,就不能在編輯框中輸入任何東西。