Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text
Xuất chữ ra vùng client của ứng dụng
Một trong các đặc tính nổi bật nhất của Windows là giao diện giao tiếp với người
dùng. Nhờ đặc tính này, nhiều dạng dữ liệu thông tin khác nhau được máy tính hỗ trợ khi
xuất ra màn hình, máy in, … Trong đó xuất văn bản ra vùng làm việc của các ứng dụng
Win32 là hình thức phổ biến nhất trong giao diện đồ họa.
Ở các bài viết trước, các thao tác xuất thông tin chỉ được thực hiện thông qua các
cửa sổ thông báo, hộp thoại và các phần tử điều khiển. Chương nà
y sẽ trình bày cách thể
hiện nội dung văn bản trên vùng làm việc của cửa sổ thông qua các hàm Win32 API.
Trong bài viết này, chúng ta học cách làm thế nào để xuất chữ ra vùng client của
ứng dụng, tìm hiểu sâu hơn về việc xử lý thông điệp WM_PAINT, các khái niệm về thiết
bị ngữ cảnh (device context) và các thông tin metrics.
Tìm hiểu về Device Context
Văn bản là
một trong những đối tượng đồ họa của hệ thống. Mỗi ký tự là tập hợp
của nhiều điểm ảnh (pixels) được xếp thành những mẫu riêng biệt. Đó là lý do tại sao
người ta dùng thuật ngữ là vẽ painting chứ không phải là writing. Thông thường, bạn
“vẽ” chữ trong vùng client của ứng dụng (thực tế, bạn cũng có thể vẽ bê
n ngoài vùng
client, nhưng đó là vấn đề khác). Việc xuất chữ ra màn hình trong Windows hoàn toàn
khác trong Dos. Trong Dos, ở chế độ văn bản màn hình có kích thước 80 x 25 (80 cột x
25 dòng) và chỉ sử dụng bởi một ứng dụng. Nhưng trong Windows thì khác, màn hình
được chia sẽ cho những ứng khác nhau. Một vài bắt buộc phải tuân thủ để tránh tình trạng
màn hình của ứng dụng này chồng lên màn hình của ứng dụng khác .Vì thế, HĐH sẽ giới
hạn vùng painting của mỗi cửa sổ là vùng client của chính nó.
Một số giá
trị trong DC là các thuộc tính đồ họa như colors, font… Bạn có thể thay đổi
những giá trị mặc định nà
y bằng cách gọi các hàm GDI khác nhau. Các hàm GDI cho
phép bạn nhận các giá trị hiện hành của các thuộc tính này. Dĩ nhiên, còn có các hàm GDI
khác cho phép bạn thực sự vẽ vùng client của cửa sổ. Có thể xem DC như là môi trường
được thiết lập mặc định được HĐH chuẩn bị sẳn cho bạn. Bạn có thể thay đổi thiết lập
mặc định nếu bạn muốn.
Khi một chương trình cần vẽ, đầu tiên nó cần nhận một handl
e của một DC. Thông
thường, có 3 cách để nhận handle của DC:
Sử dụng hàm BeginPaint và EndPaint để xử lý thông điệp WM_PAINT
Bạn sử dụng phương pháp này khi xử lý các thông điệp WM_PAINT. Hai hàm được yêu
cầu: BeginPaint và EndPaint. Hai hàm này yêu cầu handle cửa sổ, đưa vào thủ tục
window như một đối số và địa chỉ của biến cấu trúc này là ps và định nghĩa nó trong thủ
tục window như sau:
PAINTSTRUCT ps;
Cấu trúc PAINTSTRUCT được định nghĩa như sau:
typedef struct tagPAINTSTRUCT { // ps
HDC hdc; //handle c•a DC
BOOL fErase; //Cho bi•t background có b• xóa hay không
RECT rcPaint; //Cho bi•t t•a •• HCN tô v• l•i
BOOL fRestore; //dành riêng cho Windows s• d•ng
BOOL fIncUpdate; //dành riêng cho Windows s• d•ng
BYTE rgbReserved[32]; //dành riêng cho Windows s• d•ng
} PAINTSTRUCT;
EndPaint(hwnd, &ps);
return 0;
Thủ tục Window cần gọi BeginPaint và EndPaint như một cặp khi xử lý thông điệp
WM_PAINT. Nếu thủ tục Window không xử lý các thông điệp WM_PAINT, nó cần đưa
thông điệp WM_PAINT cho DefWindowProc, là thủ tục Window mặc định nằm trong
HĐH. DefWindowProc xử lý thông điệp WM_PAINT như sau:
case WM_PAINT:
BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
Việc gọi liên tiếp BeginPaint và EndPaint sẽ làm hợp lệ vùng không hợp lệ trước đó.
Windows đặ một thông điệp WM_PAINT trong hàng đợi thông điệp vì một phần của
client không hợp lệ. Bạn không gọi BeginPaint và EndPaint (hay ValidateRect), HĐH sẽ
không hợp lệ vùng đó và sẽ gửi tiếp cho bạn một thông điệp WM_PAINT khác. Sự việc
sẽ làm treo chương trình vì không thể thoát.
Sử dụng hàm GetDC để nhận ha
ndle và ReleaseDC để giải phóng handle của DC
khi xử lý các thông điệp khác WM_PAINT Mặc dù tốt nhất là vẽ lại toàn bộ vùng client trong khi xử lý thông điệp WM_PAINT,
nhưng có thể bạn sẽ thấy hữu dụng hơn khi vẽ một phần vùng client trong quá trình xử
lý các thông điệp khác. Hoặc bạn có thể cần một handle của DC cho mục đích khác
như nhận thông tin về Device Context.
Người dịch: Benina (REA TEAM) Trang 3
ReleaseDC(hWnd, hDC);
Lưu ý
: Các hàm GetDC và BeginPaint trả về device context cho vùng client của cửa sổ,
riêng hàm GetWindowDC trả về device context của toàn bộ cửa sổ kể cả thanh tiêu đề,
menu, thanh cuộn… và tất nhiên là cả vùng client. Để vẽ ra ngoài vùng làm việc
(client area), phải chặn thông điệp WM_NCPAINT( NC: None Client).
Một điều mà bạn phải nhớ là, sau khi bạn nhận được handle của DC, bạn chỉ sử dụng DC
cho việc xử lý một thông điệp duy nhất, tức là không được nhận handle ở t
hông điệp này
mà giải phóng nó ở một thông điệp khác.
HĐH sẽ gởi thông điệp WM
_PAINT đến cửa sổ để vẽ lại vùng client của nó. HĐH không
lưu nội dung của vùng client của một cửa số. Thay vào đó, khi có một sự kiện xảy ra, thì
nó cho phép vẽ lại vùng client (như trường hợp vùng client của cửa sổ này bị che bởi
vùng client của một cửa sổ khác
), HĐH sẽ đặt thông điệp WM_PAINT trong hàng đợi
thông điệp. Nó chịu trách nhiệm vẽ lại vùng client của cửa sổ. Bạn phải cung cấp các
thông tin để HĐH biết cửa sổ muốn vẽ lại vùng client như thế nào trong phần
WM_PAINT của thủ tục window, vì vậy thủ tục window sẽ vẽ lại vùng client khi thông
điệp WM_PAINT được tiếp nhận.
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 4 : Painting with Text
Khái niệm khác mà bạn phải tìm hiểu đến là thuật ngữ hình chữ nhật không hợp lệ
(invalid rectangle). HĐH định nghĩa một hình chữ nhật không hợp lệ là một vùng hình
chữ nhật nhỏ nhất trong vùng client cần được vẽ lại. Khi HĐH phát hiện ra một hình chữ
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
Người dịch: Benina (REA TEAM) Trang 5
Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)