Tải bản đầy đủ (.pdf) (20 trang)

Tài liệu Tìm hiểu về cửa sổ ứng dụng docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (408.07 KB, 20 trang )

Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
Tìm hiểu về cửa sổ ứng dụng

Trong bài viết này, chúng ta sẽ xây dựng một chương trình với đầy đủ chức năng
của một cửa sổ ứng dụng trên desktop.

Tìm hiểu về cửa sổ, lớp cửa sổ

Trong một ứng dụng đồ họa 32-bit, cửa sổ (window) là một vùng hình chữ nhật
trên màn hình, nơi mà ứng dụng có thể hiện thị thông tin và nhận thông tin vào từ người
dùng (users). Do vậy, nhiệm vụ đầu tiên của một ứng dụng đồ họa 32-bit là tạo một cửa
sổ.
Một cửa sổ sẽ chia sẻ màn hình với các cửa sổ khác trong cùng một ứng dụng hay
với các ứng dụng khá
c. Chỉ một cửa sổ trong một thời điểm nhận được thông tin nhập từ
người dùng thông qua bàn phím, thiết bị chuột hay các thiết bị nhập liệu khác để tương
tác với cửa sổ và ứng dụng.

Tất cả các cửa sổ đều đựơc tạo từ một cấu trúc được cung cấp sẵn gọi là lớp cửa sổ
(window class). Cấu trúc này là một tập hợp các mô tả các thuộc tính m
à hệ thống dùng
như khuôn mẫu để tạo nên các cửa sổ. Mỗi một cửa sổ phải là thành viên của một lớp cửa
sổ. Tất cả các lớp cửa sổ này đều được xử lý riêng biệt.

Người dịch: Benina (REA TEAM) Trang 1
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)

Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
Các chương trình trên Windows thường sử dụng các API để thiết kế giao diện
(GUI) cho chúng. Bằng cách này, nó có lợi cho cả người sử dụng và cả lập trình viên. Đối
với người sử dụng, họ không cần phải tìm hiểu làm thế nào để sử dụng giao diện đồ họa


cho mỗi chương trình mới, giao diện của các chương trình là tương tự nhau. Đối với các
lập trình viên, mã lệnh để tạo nên giao diện chương trình đã sẵn, đư
ợc test và sẵn sàng
cho việc sử dụng. Nhưng mặt khác, việc giao diện được thiết kế sẵn theo khuôn mẫu của
HĐH sẽ làm tăng thêm sự tạp trong việc các lập trình muốn cài đặt hay thao tác trên các
đối tượng bất kỳ của giao diện như là hộp thoại , menus, icons, … vì họ phải tuân theo
một nguyên tắc chính xác, nhưng điều này có thể khắc phục bằng cách lập trình theo đơn
thể hoặc theo hướng đối tượng (
OOP).

Tôi sẽ phát thảo những bước cần thiết để xây dựng cửa sổ ứng dụng trên desktop:
1. Lấy handle của thể hiện – chính là bản thân ứng dụng

 Handle là một số nguyên không dấu 32bit do HĐH tạo ra để làm định danh cho mỗi đối
tượng.
 Còn lý do tại sao phải xin HĐH cấp một handle và vì sao gọi là instance handle
(handle của thể hiện). Vì Windows là HĐH đa nhiệm, có thể có nhiều bản của cùng một
chương trình cùng chạy vào cùng một thời điểm nên phải cần phải có 1 handle của chương
trình ngay lúc chúng đang được active (nghĩa là đang được thao tác). Ngoài ra, để quản lý
chặt chẽ hơn, HĐH còn có
khái niệm hPrevinst là chỉ số của bản đã được khởi động trước đó
và chúng luôn có giá trị NULL

2. Lấy địa chỉ của xâu ký tự các đối số dòng lệnh (command line), không cần thiết
trừ khi chương trình thực hiện bằng dòng lệnh)

3. Định nghĩa lớp cửa sổ và đăng ký nó với HĐH (trừ khi bạn dùng loại cửa sổ đã
được HĐH định nghĩa trước như MessageBox hay Dialogbox).

4. Tạo lập cửa sổ làm việc cho ứng dụng.


5. Hiển thị cửa sổ trên desktop (trừ khi bạn không muốn hiển thị cửa sổ nga
y)

6. Luôn luôn vẽ (paint) và vẽ lại (repaint) vùng client của cửa sổ

Hiểu như thế nào là vẽ và vẽ lại? Có nghĩa là người dùng có thể di chuyển cửaa sổ của một hay nhiều
chương trình khác trên màn hình và sự di chuyển sẽ che một phần cửa sổ của ứng dụng. HĐH sẽ
không lưu phần cửa sổ mà bị chương trình khác che. Khi cửa sổ bị/được di chuyển, HĐH sẽ gửi yêu
cầu vẽ lại vùng client của cửa sổ.









Người dịch: Benina (REA TEAM) Trang 2
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
7. Nhận và xử lý thông điệp thông qua vòng lặp thông điệp.
Chúng ta xét qua lược đồ quản lý sự kiện và thông điệp của HĐH :

Trình tự xử lý thông điệp được mô tả như sau:
 Nhận một thông điệp thông qua hàm API GetMessage() từ hàng đợi thông điệp của ứng dụng.
Ngoại trừ thông điệp WM_QUIT, mỗi thông điệp sẽ trả về cho hàm GetMessage() một thông số
khác 0 (TRUE). Khi nhận thông điệp WM_QUIT thì chương trình sẽ thoát khỏi vòng lặp và
chấm dứt hoạt động chương trình. Hàm GetMessage() này nhận vào 4 tham số đầu vào tro

ng đó
tham số thứ 1 quan trọng là con trỏ trỏ về một cấu trúc kiểu MSG.

 Sau đó, giải mã thông điệp này thông qua hàm API TranslateMessage(). Hàm này sẽ gọi trình
điều khiển bàn phím (Keyboard driver) để chuyển đổi những thông điệp từ bàn phím do người
dùng gõ (ví dụ WM_KEYDOWN, WM_KEYUP, …) thành những ký tự để đưa các trị này vào
hàng đợi thông điệp của ứng dụng dưới dạng thông điệp WM_CHAR. Nghĩa là chương trình của
Người dịch: Benina (REA TEAM) Trang 3
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
bạn có đủ khả năng phân biệt chữ A (a hoa) và a (a thường) mà không cần biết có phím SHIFT
đang hoạt động.
 Rồi sau đó, phân công giải quyết bởi một hàm thích hợp thông qua hàm API DispatchMessage()
để xem dữ kiện chứa trong cấu trúc MSG để gọi cho đúng hàm giải quyết thông điệp. Hàm này sẽ
push thông điệp vào Window Procedure để bắt đầu xử lý thông điệp.
Vòng lặp While tiếp tục chạy cho đến khi nào GetMessage() trả về giá trị 0
cho biết là không còn cái
thông điệp nào trong hàng đợi thông điệp và lúc này thông điệp chấm dứt.
Hệ điều hành Windows sẽ phân công giải quyết các thông điệp thông qua một thủ tục gọi là Windows
Procedure () và thường được lập trình viên đặt tên là WinProc(). Đoạn code trong thủ tục WndProc() này
là phần mà bạn phải nhọc công lập trình.
Các thông số bên trong các lớp Window (Window Class - WNDCLASS), các hàm API như
GetMessage(), ShowWindow(), … bạn có thể tự tì
m hiểu thông qua MSDN hoặc ebook Microsoft®
Win32® Programmer's Reference.
8. Nếu thông điệp được gởi tới cửa sổ, chúng sẽ được xử lý bởi một hàm đặc biệt
(được gọi là Window Procedure) của cửa sổ.
Trong mỗi chương trình trên nền Windows, bộ phận trung tâm là 1 hoặc nhiều hàm nhận và xử lý
các thông điệp. Các hàm này được gọi là Window Procedure() viết tắt là WndProc(). Đứng về mặt lập
trình, thì cấu trúc của WndProc thường là lệnh switch() với những trường hợp cho những thông điệp khác

nhau. Trong trường hợp của chúng ta, chỉ có hai case là xử lý thông điệp WM_PAINT và thông điệp
WM_DESTROY. Trong thực tế các ứng dụng thường chứa một switch khổng
lồ với số lượng trường hợp
nhiều kinh khủng.
Giá trị trả về của WndProc() tùy thuộc vào loại thông điệp. Mỗi WndProc() đều cần 4 tham số đầu vào :
 hwnd : handle của thông điệp msg và 2 thông số khác là wparam và lparam. Tham số hwnd này
rất quan trọng vì một WndProc() có thể hỗ trợ nhiều cửa sổ khác nhau trên màn hình cùng một
lúc. Do đó, tham số hwnd sẽ nhận diện cửa sổ nào đã gọi WndProc() của chúng ta.
 msg : cho số thứ tự của thô
ng điệp. Nếu hwnd cho biết ai gọi thì msg cho biết nó muốn gì. Có thể.
Có thể nó muốn nói là cửa sổ mới tạo xong (WM_CREATE) hoặc di chuyển đến vị trí mới
(WM_MOVE) hoặc thay đổi kích thước (WM_SIZE) hoặc vẽ lại ứng dụng (WM_PAINT) hoặc
khi người dùng nhấn nút Close đóng cửa sổ ứng dụng lại (WM_DESTROY).
9. Thoát chương trình nếu người dùng đóng cửa sổ


Như bạn thấy, cấu trúc của một chương trình chạy trên nền Windows khá phức tạp so với chương
trình chạy trên nền Dos. Nhưng trong thế giới Windows khác hoàn toàn so với thế giới Dos. Những
chương trình Winodws có thể “chung sống” êm thắm với nhau. Chúng phải tuân thủ theo các nguyên tắc.
Bạn - một lập trình viên - càng phải nghiêm ngặt hơn trong thói quen và phong cách lập trình của mình.








Người dịch: Benina (REA TEAM) Trang 4
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)

Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
Xây dựng một ứng dụng Win32 ASM đơn giản

Dưới đây là source code xây dựng một cửa sổ ứng dụng đơn giản. Trước khi đi
vào các chi tiết phức tạp trong việc lập trình Win32 ASM , tôi sẽ điểm qua một số điểm
quan trọng để cho bạn lập trình được dễ dàng.

 Bạn nên sẽ đặt tất cả các hằng số, cấu trúc và các khai báo hàm (prototypes) trong
một file include và include nó ngay lúc bắt đầu code của file .asm của bạn. Nó sẽ
giúp bạn tránh đư
ợc lỗi do gõ sai. Hiện thời, file include hòan chỉnh nhất cho
MASM là windows.inc. Bạn cũng có thể định nghĩa những hằng số cho riêng bạn
và những định nghĩa cấu trúc ,nhưng bạn sẽ đặt chúng vào trong một file include
riêng biệt với windows.inc.

 Dùng chỉ thị includelib để chỉ rõ thư viện nhập nào được sử dụng trong chương
trình của bạn. Ví dụ, nếu chương trình của bạn gọi Me
ssageBox, bạn sẽ gọi chỉ thị
includelib với cú pháp như sau: includelib user32.lib tại vị trí bắt đầu trong file
.asm của bạn. Chỉ thị này nói cho MASM biết rằng chương trình của bạn có sử
dụng những hàm trong thư viện nhập có tên là user32.lib. Nếu chương trình gọi
những hàm mà những hàm này nằm trong nhiều hơn một thư viện nhập, chỉ cần
thêm một includelib cho mỗi thư viện bạn dùng. Bằng các
h sử dụng chỉ thị
includelib, bạn sẽ không phải lo lắng về các thư viện nhập này trong lúc liên kết
chương trình để tạo ra file thực thi (file .EXE). Bạn có thể dùng /LIBPATH để nói
cho trình liên kết biết tất cả các thư viên đó ở đâu.

 Khi khai báo prototypes các hàm API, cấu trúc hay hằng số trong file include của
bạn, hãy cố bám sát các tên gốc được dùng trong file include của HĐH. Điều này

giúp bạn đỡ phải tra cứu một vài mục Win32 API reference.

 Sử dụng makefile để trình biê
n dịch tự động dịch chương trình của bạn. Điều này
sẽ giúp bạn tiết kiệm thời gian thay vì phải gõ lệnh biên dịch.

.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
includelib user32.lib ; Goi cac ham trong user32.lib
; va kernel32.lib
include kernel32.inc
includelib kernel32.lib

; Khai bao prototype ham WinMain
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.DATA ; Nhung du lieu duoc khoi tao truoc

ClassName db "SimpleWinClass",0 ; Ten cua lop cua so
AppName db "Our First Window",0 ; Ten cua cua so


Người dịch: Benina (REA TEAM) Trang 5
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
.DATA? ; Nhung du lieu khong duoc khoi tao

hInstance HINSTANCE ? ; handle cua the hien cua ung dung
CommandLine LPSTR ?

.CODE ; Here begins our code
start:
invoke GetModuleHandle, NULL ; Lay handle cua the hien cua ung dung

hInstance,eax
mov hInstance,eax
invoke GetCommandLine ; Lay dia chi cua xau ky tu chua
; cac doi so dong lenh.
; Ban khong can goi ham nay neu nhu
; chuong trinh cua ban khong thuc hien
; bang dong lenh
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT ; Goi ham
; WinMain
invoke ExitProcess, eax ; Thoat khoi ung dung.
; Gia tri tra ve cua ExitProcess duoc
; luu trong eax cua WinMain.

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,
CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX ; Tao cac bien cuc bo trong ngan xep
LOCAL msg:MSG
LOCAL hwnd:HWND

mov wc.cbSize,SIZEOF WNDCLASSEX ; Dien day du gia tri cho
; cac bien cua lop cua so
mov wc.style, CS_HREDRAW or CS_VREDRAW

mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc ; Dang ky lop cua so voi HDH
invoke CreateWindowEx,NULL,\
ADDR ClassName,\
ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,\
NULL
mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow ; Hien thi cua so ra desktop
invoke UpdateWindow, hwnd ; Ve va ve lai vung client cua cua so




Người dịch: Benina (REA TEAM) Trang 6
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
.WHILE TRUE ; Vong lap thong diep
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam ; Tra gia tri trong thanh ghi eax
ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY ; Neu nguoi dung dong cua so dong cua so
invoke PostQuitMessage,NULL ; thi thoat ung dung
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
; Nguoc lai thi xu ly thong diep theo mac dinh cua HDH
ret
.ENDIF
xor eax,eax
ret
WndProc endp

end start

Phân tích mã lệnh


Bạn đã thấy một chương trình tạo một cửa sổ ứng dụng đơn giản cần phải code rất
nhiều mã lệnh như trên. Nhưng phần lớn những đoạn code đó chỉ là một template code
(mã nguồn mẫu) mà bạn có thể copy từ mã lệnh của file này đến mã lệnh của một file
khác. Hay nếu bạn thích, bạn có thể dịch một vài đoạn mã nguồn đó vào trong một thư
viện để dùng như một đọan
mở đầu hay đọan code cuối. Bạn có thể chỉ viết code trong
hàm WinMain. Thật vậy, đây là những gì mà trình biên dịch C đã làm sẵn. Chúng cho
phép bạn viết code hàm WinMain không cần phải lo làm những công việc “vặt vảnh”
khác. Khác với trình biên dịch C là bạn phải có một hàm có tên là WinMain, nếu không
sẽ không thể kết hợp code của bạn với đoạn đầu hay đọan cuối của code. Bạn không bị
hạn chế vấn đề nà
y trong ngôn ngữ ASM. Bạn có thể dùng bất kỳ tên hàm nào không nhất
thiết là WinMain hoặc không có hàm nào cũng không sao.

Tiếp theo là những giải thích về đoạn code đã viết ở trên. Nó rất dài, chúng ta hãy cùng
nhau phân tích chương trình này để làm rõ vấn đề xây dựng một ứng dụng Win32 ASM
không đến nổi quá phức tạp như bạn nghĩ.

.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
Người dịch: Benina (REA TEAM) Trang 7
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)

Iczelion’s Tutorial Win32 ASM Tutorial 3 : A Simple Window
Ba dòng trước tiên rất cần thiết vì:

.386: nói cho MASM biết rằng chúng ta có ý định sử dụng cấu trúc bộ lệnh 80386 trong
chương trình.

.model flat,stdcall: nói cho MASM rằng kiểu bộ nhớ đang sử dụng cho chương trình là
kiểu flat memory. Cũng như bạn sẽ dùng tham số stdcall để quy ướ cách truyền tham số
cho hàm như thế nào.

option casemap:none nói cho MASM biết rằng tên các nhãn trong chương trình phân
biệt chữ hoa, chữ thường, vì vậy ExitProcess sẽ khác với exitprocess.

Kế đến là khai báo prot
otype của hàm WinMain. Sau này, khi chúng ta gọi hàm
WinMain, chúng ta ta phải định nghĩa lại prototype của hàm để có thể sử dụng được hàm
invoke.

Chúng ta phải include file windows.inc phần bắt đầu trong mã nguồn. Nó chứa các cấu
trúc quan trọng, các hằng số được dùng trong chương trình của chúng ta. File include
windows.inc chỉ là một file text. Bạn có thể mở nó với bất kỳ trình sọan thảo vănbản nào.
Xin chú ý rằng file windows.inc không chứa tất cả các cấu trúc và các hằng số. Bạn c
ó
thể thêm các đối tượng mới (cấu trúc, hằng số, …) mới nếu không có trong file.

Chương trình ứng dụng gọi các hàm API chứa trong user32.dll (CreatWindowEx,
RegisterWindowClassEx và gọi hàm API chứa trong kernal32.dll (ExitProcess), vì thế
chúng ta phải liên kết chương trình với các thư viện nhập này. Câu hỏi kế tiếp là: “Làm
thế nào tôi biết được thư viện nhập nào được liên kết với chương trình của chúng ta?


Bạn phải biết API nằm ở đâu để có thể gọi các hàm API. Ví dụ: nếu bạn gọi một hàm API
trong gdi32.dll , bạn phải liên kết với với gdi32.lib.

Đây là cách mà MASM giải quyết vấn đề. Còn đối với TASM, cách liên kết với các thư
viện nhập thì đơn giản hơn, chỉ cần liên kết đến một và chỉ một file mà thôi:
import32.lib.
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
Kế đến là section “DATA”

Trong section .DATA, chúng ta khai báo những chuỗi kết thúc bằng zero.
 Classname là tên của lớp cửa sổ (Window class)
 Appname là tên cửa sổ ứng dụng của chúng ta.


Người dịch: Benina (REA TEAM) Trang 8
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)

×