Hệ ñiều hành Windows cũng gọi một hàm chính là WinMain khi bắt ñầu tiến trình ñể thực hiện việc tạo lập cửa sổ ứng dụng, ứng dụng này ñược hệ ñiều hành quản lý thông qua ñịa chỉ của tiến
Trang 2Bài 1
TỔNG QUAN VỀ LẬP TRÌNH WINDOWS
Không cần nói nhiều hơn, tất cả chúng ta ñều khá quen thuộc với việc sử dụng các ứng dụng trên
hệ ñiều hành Microsoft Windows Trong bài học ñầu tiên này, tôi xin giới thiệu các ñặc ñiểm chính của hệ ñiều hành này, ñồng thời cũng trình bày cách xây dựng ứng dụng cụ thể dạng Win32 Application trên công cụ Visual C++ 6.0
1.1 LẬP TRÌNH TRÊN MICROSOFT WINDOWS
Trong tất cả các môn học trước, hầu như chúng ta chỉ cài ñặt các chương trình, minh họa các
thuật toán,… dưới dạng các ứng dụng xử lý tuần tự theo lô (batch-oriented) hay giao tác (transaction-oriented) gắn liền với hệ ñiều hành MS-DOS Tuy nhiên, các ứng dụng trên
Windows thì hoàn toàn khác Vì thế, phần này sẽ tóm tắt giúp các bạn các ñặc ñiểm chính của hệ ñiều hành Windows, và cũng chính là các ñặc ñiểm của các ứng dụng khi lập trình trên môi trường này
1.1.1 Thông ñiệp và xử lý thông ñiệp
Khi chúng ta viết một chương trình dạng MS-DOS bằng ngôn ngữ C, yêu cầu duy nhất là ta phải
viết một hàm main Hệ ñiều hành gọi hàm main khi chương trình ñược thực thi, và thực hiện
theo các cấu trúc lệnh ñã ñược lập trình từ trên xuống dưới
Tuy nhiên, với Windows thì khác hẳn Hệ ñiều hành Windows cũng gọi một hàm chính là
WinMain khi bắt ñầu tiến trình ñể thực hiện việc tạo lập cửa sổ ứng dụng, ứng dụng này ñược hệ ñiều hành quản lý thông qua ñịa chỉ của tiến trình và vùng nhớ ñối tượng cửa sổ
Các thao tác của người dùng - hoặc do các ñối tượng khác trên Windows - tác ñộng lên ứng
dụng ñều ñược chuyển thành một dạng thông tin gọi là thông ñiệp (message) và ñược hệ ñiều
hành quản lý Hệ ñiều hành lần lượt chuyển các thông ñiệp ñến các cửa sổ nhận thao tác vừa thực hiện Khi lập trình, các thông ñiệp này ñều ñược gởi vào hàm xử lý của cửa sổ - thường ñặt tên là
WndProc ñối với cửa sổ chính của ứng dụng, và ta chỉ việc viết code ñể xử lý cho ứng dụng thông qua các giá trị thông ñiệp (tương ứng các thao tác) ñã ñược Windows ñịnh nghĩa sẵn
1.1.2 Giao diện ñồ họa người dùng GUI
Vấn ñề xử lý thông ñiệp là ñặc ñiểm quan trọng nhất, tuy nhiên khi ñề cập ñến lập trình trên
Windows người ta lại thường chú ý tới ñặc trưng giao diện (visual interface) của các ứng dụng
hơn
Trên môi trường Windows, các ứng dụng giao tiếp (trao ñổi thông tin) với người dùng thông
Trang 3qua các ñối tượng trực quan như cửa sổ (window), thanh trình ñơn (menu), hộp hội thoại (dialog
box ), các ñối tượng ñiều khiển (controls)…
Khi lập trình, chúng ta sử dụng các hàm API (Application Programming Interface), cơ chế liên kết ñộng DLL (Dynamic Link Library) và hệ thống tài nguyên (resource-based
programming ) của IDE (Intergrated Development Environment) ñể xây dựng giao diện các ứng
dụng theo các ñịnh dạng mà Windows hỗ trợ
1.1.3 Giao tiếp thiết bị ñồ họa GDI
Khi lập trình trên MS-DOS, ñể giao tiếp với các thiết bị - ñưa dữ liệu ra vùng nhớ màn hình, cổng máy in chẳng hạn - thì ta phải biết cơ chế của các thiết bị phần cứng tương ứng, rồi ghi dữ liệu ra thẳng thiết bị
Thế nhưng, trên Windows, với sự hỗ trợ của hệ ñiều hành, các ứng dụng không cần phải truy xuất thiết bị vật lý Dữ liệu ñược xuất ra một ñối tượng thiết bị ảo, gọi là thiết bị ngữ cảnh
(device context), sau ñó thông qua chương trình ñiều khiển thiết bị ñã cài ñặt trên hệ ñiều hành
(tức là các driver của các thiết bị phần cứng), dữ liệu sẽ ñược chuyển ñến các thiết bị vật lý khác nhau mà người lập trình ứng dụng trên Windows không cần phải biết về chúng
1.1.4 Quản lý vùng nhớ và tập tin
Với các phiên bản mới của hệ ñiều hành, việc quản lý các vùng nhớ và tập tin của các ứng dụng trên Windows ngày càng dễ dàng và hiệu quả hơn
Chúng ta không phải thật sự khó khăn khi cấp phát, truy xuất, giải phóng,… các khối nhớ, vì
ñã có hệ ñiều hành lo giúp Với cơ chế cấp phát và quản lý ñộng, quản lý vùng nhớ ảo và ánh xạ vùng nhớ lên hệ thống lưu trữ phụ, việc thao tác các ñối tượng vùng nhớ lớn, phức tạp giờ ñây chỉ còn là việc gọi các hàm Windows cung cấp và việc tổ chức các cấu trúc dữ liệu phú hợp cho ứng dụng mà thôi
1.1.5 Thư viện liên kết ñộng
Trên môi trường MS-DOS, các module của các ñối tượng trong mỗi chương trình chỉ ñược liên kết tĩnh trong quá trình tạo (build) ứng dụng Trong khi trên môi trường Windows, các hàm, thư viện còn có thể ñược gọi khi chương trình ñang thực thi (run-time), gọi là cơ chế liên kết ñộng Các tiến trình ñang thực thi có thể cùng chia sẻ các thư viện này, ñiều này giúp giảm thiểu vùng nhớ và kích thước các tập tin
Thật ra bản thân hệ ñiều hành Windows là tập hợp các thư viện liên kết ñộng, với ba ñơn vị
chính là Kernel, User, và GDI liên quan ñến các hàm, thủ tục thể hiện các chức năng quản lý các
tiến trình, vùng nhớ, tập tin,… (kernel), giao diện người dùng, các cửa sổ,… (user) và thiết bị ñồ họa (GDI) và các DLL gắn liền với các ứng dụng khác ñược cài ñặt trên máy tính
1.2 ỨNG DỤNG WIN32 APPLICATION TRÊN MS VISUAL C++ 6.0
Trang 4Sau khi nắm ñược các ñặc ñiểm chính của các ứng dụng trên môi trường hệ ñiều hành MS Windows, phần này sẽ giúp các bạn hiểu và xây dựng ứng dụng cụ thể dạng Win32 Application trên công cụ MS Visual C++ 6.0
1.2.1 Project ứng dụng Win32 Application
MS Visual C++ 6.0 hỗ trợ lập trình nhiều dạng ứng dụng khác nhau như Win32 Console Application, Win32 Application, MFC AppWizard, Win32 Dynamic Link Library,… trong ñó Win32 Application lập trình trực tiếp với các hàm API là dạng mà chúng ta sẽ tìm hiểu trong môn học này
Hình 1.1 Tạo lập project dạng Win32 Application
Sử dụng Wizard của Visual C++ 6.0, chúng ta tạo lập project ñầu tiên như sau:
1 Bước 1: Từ menu File, chọn menu item New… Ctrl+N
2 Bước 2: Trên hộp thoại New chọn dạng Projects Win32 Apllication, gõ tên project (Project
name) và ñường dẫn (Location) cần tạo – xem hình 1.1 Chọn OK
3 Bước 3: Visual C hỗ trợ cho phép tạo lập một số mẫu project Ta chọn dạng A typical
“Hello World!” application – xem hình 1.2 Chọn Finish Với cách chọn này, Visual C++
6.0 phát sinh sẵn một số file code, tài nguyên – kết quả thể hiện trên hình 1.3 Sau khi chọn
OK ñể kết thúc việc tạo project mới, chúng ta sẽ tìm hiểu giao diện của IDE MS Visual C++
6.0 và cách viết chương trình trên công cụ này
Trang 5Hình 1.2 Chọn project A typical “Hello World!” application
1.2.2 Các thành phần chính trên IDE MS Visual C++ 6.0
ðược xây dựng dựa theo công cụ Visual Workbench, IDE này gồm các cửa sổ, thanh công cụ, editor có thể cấu hình,… kết hợp hài hòa với nhau giúp các lập trình viên dễ dàng biên soạn code, tạo lập các tài nguyên, biên dịch, debug lỗi…
Trang 6Hình 1.3 Thông báo sau khi tạo project của Visual C++ 6.0
Nếu ñã từng sử dụng các IDE, có lẽ bạn sẽ dễ dàng ñể hiểu cách tổ chức và hoạt ñộng của Visual C++ 6.0 Tuy nhiên, nếu mới lập trình trên một IDE như thế này thì bạn cần phải hiểu
project là gì Một project là một tập hợp các file source có liên hệ với nhau mà trình biên dịch có thể biên dịch và liên kết ñể tạo các file khả thi dạng Windows hoặc các DLL Các file source trong mỗi project thường ñược lưu trữ trong một thư mục riêng biệt Ngoài ra, một project còn phụ thuộc vào một số file liên kết như file header, file thư viện… khác
1 Cửa sổ Workspace
Mặc ñịnh thì cửa sổ bên trái mà chúng ta thấy trên IDE Visual C++ 6.0 là một treeview cho phép chúng ta chọn các tập tin source code, resource,… ñể soạn thảo
Trang 7Hình 1.4 Giao diện của MS Visual C++ 6.0
FileView liệt kê các tập tin trong project, tổ chức như cây thư mục phân theo nhóm file chức
năng: các file source code (.c hoặc cpp), các file header (.h và hpp), các file resource như icon (.ico), bitmap (.bmp),…
ResourceView liệt kê các tài nguyên của ứng dụng Visual C++ 6.0 hỗ trợ các công cụ trực quan cho phép các lập trình viên thao tác các resource một cách hiệu quả Khi chọn menu, hộp thoại,… IDE sẽ thể hiện các editor tương ứng
ClassView giúp lập trình viên dễ dàng chuyển ñến các lớp ñối tượng (ñặc biệt cho project MFC), các hàm, các biến trong project
2 Cửa sổ soạn thảo source code
Visual C++ 6.0 cung cấp một cửa sổ soạn code khá tuyệt vời, hỗ trợ nhiều chức năng khi lập trình như thể hiện màu chữ theo dạng code, chế ñộ tự ñộng chuyển tab,… và nhất là chức năng
AutoComplete (tự ñộng thể hiện các tên hàm, biến,… ñã ñịnh nghĩa)
3 Cửa sổ thể hiện kết quả build (Output window)
Khi lập trình, việc biên dịch, sửa lỗi,… là thao tác rất quan trọng Trình biên dịch kiểm tra các lỗi
cú pháp, ngữ nghĩa,… và xuất các thông báo liên quan cho lập trình viên Căn cứ vào thông báo
lỗi, các lập trình viên có thể lần theo các vết thông báo ñể sửa lại chương trình
4 Các cửa sổ khác trên MS Visual C++ 6.0
Trang 8Ngoài các cửa sổ vừa ñề cập ở trên, Visual C++ 6.0 còn cung cấp các ñối tượng trực quan khác
ñể hỗ trợ các lập trình viên như thanh WizardBar, MenuBar, ToolsBar,… Các cửa sổ trên IDE
này ñều có thể kéo trôi nổi (docking) và sắp xếp theo ý thích của người sử dụng
1.2.3 Quy trình build ứng dụng
Quy trình build project tạo file khả thi dạng EXE hoặc DLL ñược Visual C++ thực hiện theo hai công ñoạn chính - xem hình 1.5 - như sau:
Hình 1.5 Quy trình build một project ứng dụng
- Biên dịch (compile) các file source code và resource sang dạng mã trung gian OBJ, RES,…
- Liên kết (link) các file OBJ, RES với các file thư viện Windows và run-time ñể tạo file khả thi Với project ñơn giản ñã tạo lập ở trên, chúng ta có thể thực hành xem kết quả build file
BaiTap.exe Bằng cách chọn chức năng “Build BaiTap.exe F7” từ menu Build, Visual C++
sẽ thực hiện biên dịch và liên kết các file có trong project và tạo các file OBJ, RES, EXE, tương ứng
Tuỳ theo dạng build ñược cấu hình cho project (Win32 Release hoặc Win32 Debug), các
file mã phát sinh trong quá trình build sẽ ñược tạo ra trong một thư mục con mặc ñịnh có tên là
Release hoặc Debug - nằm trong thư mục chứa các file nguồn của project
Nếu tạo lập như ở trên, chúng ta sẽ có các file mã phát sinh trong thư mục
D:\Study\BaiTap\Debug chẳng hạn ðể ý là khi copy code của một project ñem sang một máy tính khác ñể build, chúng ta không cần phải copy thư mục này (vì khá lớn)!!!
Hình 1.6 Thể hiện của ứng dụng BaiTap.exe
Trang 9Nếu quá trình build thành công – ñược thông báo trên cửa sổ Output, xem phần 1.2.2 -
chúng ta có thể xem kết quả thực hiện bằng cách chọn chức năng Execute Program (Ctrl+F5)
Kết quả project này thể hiện như ở hình 1.6
1.3 PHÂN TÍCH SOURCE CODE CỦA PROJECT
Trong project BaiTap dạng Win32 Application ñược tạo như ở trên, file BaiTap.cpp (workspace FileView) chính là file source chính cài ñặt các thao tác tạo lập và xử lý các chức năng của cửa
// ðịnh nghĩa trước các hàm sẽ ñược gọi trong module này ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{ // Các biến cục bộ trong hàm WinMain MSG msg; // Biến kiểu dữ liệu thông ñiệp HACCEL hAccelTable; // Bảng phím nóng
// Khởi tạo bằng cách nạp các chuỗi từ resource (tài nguyên) LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_BAITAP, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Thực hiện việc khởi tạo ứng dụng
if (!InitInstance (hInstance, nCmdShow)) {
return FALSE;
} Nạp bảng phím nóng từ resource hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_BAITAP);
// Vòng lặp xử lý thông ñiệp của ứng dụng – xem lại phần 1.1.1 while (GetMessage(&msg, NULL, 0, 0))
}
Trang 10// Hàm MyRegisterClass() - ðăng ký một lớp cửa sổ
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wcex; // Lớp cửa sổ wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
{ HWND hWnd; // Biến ñịnh danh cửa sổ hInst = hInstance; // Lưu ñịnh danh ứng dụng vào biến toàn cục ở trên // Tạo lập cửa sổ
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd) { return FALSE;
} // Hiển thị cửa sổ với dạng hiển thị là nCmdShow ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
} // Hàm WndProc(HWND, unsigned, WORD, LONG) // Hàm xử lý các thông ñiệp sau của cửa sổ ứng dụng:
// WM_COMMAND - Xử lý các thao tác liên quan ñến menu // WM_PAINT - Xử lý thao tác vẽ lại cửa sổ
// WM_DESTROY - Gởi thông ñiệp kết thúc ứng dụng khi ñóng cửa sổ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ // ðịnh nghĩa các biến cục bộ int wmId, wmEvent;
case WM_COMMAND: // Thông ñiệp liên quan tới menu và các control wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Chuyển thao tác ứng với menu item ñược kích hoạt switch (wmId)
{
Trang 11case WM_PAINT: // Thông ñiệp vẽ lại cửa sổ hdc = BeginPaint(hWnd, &ps);
} // Xử lý các thông ñiệp gởi cho hộp thoại IDD_ABOUTBOX LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ switch (message) {
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
} break;
} return FALSE;
} Chúng ta sẽ tìm hiểu hai hàm chính luôn phải có trong một chương trình dạng Win32
Application là WinMain và WndProc
1.3.1 Hàm WinMain
Như ñã giới thiệu ở phần 1.1.1, hệ ñiều hành khởi gọi thực thi một ứng dụng dạng Windows
(Windows-based) thông qua hàm WinMain có dạng:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow);
trong ñó:
1 hInstance là con trỏ ñược Windows quản lý xác ñịnh tiến trình ứng dụng ñang thực thi,
gọi là ñịnh danh ứng dụng (handle to instance)
Trang 122 hPrevInstance là ñịnh danh của tiến trình kế trước của ứng dụng này, thường bằng NULL
3 lpCmdLine trỏ ñến chuỗi ký tự zero xác ñịnh dòng command line gọi ứng dụng (không
chứa tên chương trình)
4 nCmdShow là tham số xác ñịnh cách thức hiển thị cửa sổ sẽ ñược tạo lập
Trong một ứng dụng thông thường, hàm WinMain thực hiện các chức năng chính sau
1 ðịnh nghĩa và ñăng ký một lớp cửa sổ
ðầu tiên của viêc xây dựng một ứng dụng Windows là phải ñịnh nghĩa một lớp cửa sổ cho ứng dụng Windows cung cấp một cấu trúc WNDCLASS (mở rộng là WNDCLASSEX) gọi là lớp cửa sổ, lớp này chứa những thuộc tính tạo thành một cửa sổ
typedef struct _WNDCLASSEX {
Sau khi khởi gán giá trị các tham số của ñối tượng lớp cửa sổ, ta dùng hàm RegisterClass
hoặc RegisterClassEx ñể ñăng ký lớp cửa sổ này với hệ thống
ATOM RegisterClassEx(CONST WNDCLASSEX *lpwcx );
Tuỳ theo cửa sổ ứng dụng muốn thể hiện, ta sẽ xác lập các giá trị khác nhau cho cấu trúc lớp của sổ ở trên Ở ñây chúng tôi không trình bày chi tiết các thuộc tính của lớp cửa sổ, sinh viên cần tham khảo thêm trong MSDN ñể tạo lập các dạng cửa sổ theo ý mình
Trong các tham số ở trên, cần lưu ý thuộc tính lpfnWndProc ðây chính là tên hàm xử lý
thông ñiệp của ứng dụng Trong ví dụ ở trên, tham số này có giá trị là WndProc, ñiều ñó có
nghĩa là với cách tạo lập này thì mọi thao tác xử lý của ứng dụng sẽ ñược viết trong một hàm có
tên là WndProc Chúng ta sẽ tìm hiểu kỹ hơn về hàm này trong phần 1.3.2
2 Tạo lập và hiện thị cửa sổ ứng dụng
Sau khi ñăng ký lớp cửa sổ, ở thao tác tiếp theo ta dùng hàm CreateWindow hoặc
CreateWindowEx ñể tạo lập cửa sổ ứng dụng theo cấu trúc cửa sổ ñã ñịnh nghĩa
Trang 13HWND CreateWindow(
LPCTSTR lpClassName, // Tên lớp cửa sổ ñã ñăng ký
LPCTSTR lpWindowName, // Tên cửa sổ sẽ hiển thị
DWORD dwStyle, // Các kiểu cửa sổ
int x, // Vị trí ngang ban ñầu
int y, // Vị trí dọc ban ñầu
int nWidth, // Chiều rộng ban ñầu
int nHeight, // Chiều cao ban ñầu
HWND hWndParent, // ðịnh danh của cửa sổ cha
HMENU hMenu, // ðịnh danh của menu
HINSTANCE hInstance, // ðịnh danh tiến trình ứng dụng
LPVOID lpParam // Dữ liệu gởi vào app khi tạo lập
);
Bằng cách thiết lập các thông số về lớp cửa sổ, tên cửa sổ, kích thước,… ta có thể tạo lập các cửa sổ khác nhau Nếu tạo lập thành công, hàm này trả về ñịnh danh của cửa sổ; nếu không, giá trị trả về bằng NULL
Các thao tác trên ứng dụng sau này thường tham chiếu ñến biến ñịnh danh cửa sổ vừa ñược tạo lập Trong các ứng dụng thông thường – như ở ví dụ trên – thao tác tiếp theo là hiển thị cửa
sổ vừa tạo lập
BOOL ShowWindow(
HWND hWnd, // ðịnh danh của cửa sổ
int nCmdShow // Dạng hiển thị
);
Trong ví dụ ở trên, nCmdShow chính là giá trị ñược gởi vào từ hàm WinMain Tuy nhiên, ta
có thể thay ñổi với các giá trị khác nhau ñể hiển thị cửa sổ dưới nhiều cách thức khác nhau Sau ñây là một số dạng hiển thị ñơn giản:
• SW_HIDE: Ẩn cửa sổ này và kích hoạt cửa sổ khác
• SW_MAXIMIZE: Phóng lớn cửa sổ ra ñầy màn hình
• SW_MINIMIZE: Thu nhỏ cửa sổ về task bar, ñồng thời kích hoạt cửa sổ ứng dụng kế tiếp theo thứ tự ñã chọn
• SW_RESTORE: Hiển thị theo ñúng kích thước ñã tạo lập
3 Vòng lặp xử lý thông ñiệp
Sau khi cửa sổ ñược hiển thị trên màn hình, chương trình nhận các thao tác của người dùng (từ
bàn phím, mouse,…) hoặc từ các ứng dụng khác, gọi là thông ñiệp Các thông ñiệp này ñược
Windows quản lý – dưới dạng một hàng ñợi – và chuyển cho ứng dụng qua hàm GetMessage
trong hàm WinMain này
BOOL GetMessage(
LPMSG lpMsg, // Cấu trúc nhận thông tin thông ñiệp
HWND hWnd, // ðịnh danh của cửa sổ
UINT wMsgFilterMin, // Giá trị thông ñiệp nhỏ nhất
UINT wMsgFilterMax // và lớn nhất cần nhận
);
Trang 14Với thông ñiệp nhận ñược, nếu là thông ñiệp phím nóng (accelerator) ta dùng hàm
TranslateAccelerator ñể chuyển các thông tin thành dạng thông ñiệp ứng các thao tác của menu, control (thông ñiệp này có tên là WM_COMMAND và WM_SYSCOMMAND sẽ ñược học trong bài nói về menu và control) Tương tự nếu là thông ñiệp về phím ảo (virtual key) ta dùng hàm TranslateMessage ñể chuyển thành các thông ñiệp ký tự (sẽ ñược học trong bài nói về mouse và keyboard)
Sau ñó các thông ñiệp nhận ñược này sẽ chuyển cho hàm xử lý thông ñiệp WndProc qua hàm DispatchMessage
Ý nghĩa của các trường trong cấu trúc MSG
1 hwnd : ðịnh danh của cửa sổ mà thông ñiệp phát sinh
2 message : ðịnh danh của thông ñiệp, ví dụ như thông ñiệp phát sinh khi bấm nút chuột trái là WM_LBUTTONDOWN có giá trị 0x0201
3 wParam : Tham số 32-bit chứa các thông tin phụ thuộc vào từng thông ñiệp cụ thể
4 lParam : Tham số 32-bit phụ thuộc vào thông ñiệp
5 time : Thời gian ñặt thông ñiệp trong hàng ñợi
6 pt : Tọa ñộ của chuột khi ñặt thông ñiệp vào hàng ñợi
Hàm GetMessage sẽ trả về 0 nếu msg chứa thông ñiệp có ñịnh danh WM_QUIT (0x0012), khi ñó vòng lặp thông ñiệp ngưng và ứng dụng kết thúc Ngược lại thì hàm sẽ trả về một giá trị khác 0 với các thông ñiệp khác
Trang 151.3.2 Hàm xử lý cửa sổ ứng dụng WndProc
Như ñã nói ở trên, mỗi lần ứng dụng nhận ñược một thông ñiệp trong hàm WinMain thì chuyển qua cho hàm xử lý WndProc Và như thế, ta chỉ cần lập trình cho từng thao tác ứng với các thông ñiệp khác nhau Ta có cấu trúc hàm WndProc
LRESULT CALLBACK WndProc(
HWND hWnd, // ðịnh danh cửa sổ nhận thông ñiệp
UINT message, // Giá trị thông ñiệp gởi vào
WPARAM wParam, // Hai giá trị thông tin gởi kèm
LPARAM lParam // tuỳ thuộc vào từng thông ñiệp
);
Hàm có kiểu thực hiện là CALLBACK có nghĩa là hàm sẽ ñược gọi lặp ñi lặp mỗi khi có một tác ñộng từ người dùng hay từ một ứng dụng khác lên cửa sổ Giá trị trả về có kiểu long (LRESULT) sẽ do người lập trình viết ứng với mỗi xử lý của các thông ñiệp
Và như vậy, ñối với các ứng dụng dạng Win32 Application, hầu như chúng ta chỉ cần tìm hiểu các thông ñiệp, với các giá trị gởi kèm là có thể lập trình ñể xử lý cho các thao tác cần thiết
Ví dụ ñể xử lý menu, control thì ta cần hiểu về thông ñiệp WM_COMMAND Các bài học tiếp theo sẽ giúp sinh viên tìm hiểu tuần tự các ñặc trưng chính của Windows ñồng thời cài ñặt một vài dạng thông ñiệp liên quan
1.3.3 Một số lưu ý
ðể kết thúc bài giới thiệu này, chúng tôi xin nêu lên một số lưu ý mà các bạn cần quan tâm khi học lập trình bằng ngôn ngữ C trên MS Windows
So với các môn học khác thì môn học này ñòi hỏi các bạn thực hành nhiều Sau khi nắm
ñược từng ñặc ñiểm chính của các ứng dụng trên Windows, chúng ta cần phải cài ñặt chúng thông qua một số ví dụ, bài tập – từ ñó sẽ dễ dàng hiểu rõ lại về lý thuyết, và như vậy có thể xây dựng các ứng dụng khác một cách nhanh chóng
Thứ hai là vấn ñề áp dụng lý thuyết vào việc xây dựng các ứng dụng cụ thể, việc tham khảo các tài liệu…Giáo trình này chỉ giúp các bạn nắm ñược cách lập trình trên Windows một cách cơ
bản Và ñây không phải là tài liệu tham chiếu Sau khi nắm ñược các ñiểm chính, khi lập trình, các bạn nên tham chiếu với thư viện MSDN của Microsoft ñể có những hướng dẫn ñầy ñủ nhất
Và như vậy, nếu ñã làm quen ñược với việc lập trình các ứng dụng trên Windows chỉ với các hàm API, các bạn có thể tự tìm hiểu ñể có thể lập trình bằng MFC hay một ngôn ngữ nào khác… (hiện tại bên cạnh việc xây dựng các ứng dụng Windows-based, việc tìm hiểu và xây dựng các ứng dụng NET-based, ví dụ VB.NET, C#,… là rất cần thiết)
Cuối cùng là một vấn ñề rất cơ bản, cách thức trình bày code khi lập trình Chúng ta ñã làm
quen với việc lập trình qua nhiều môn học, tuy nhiên theo những gì chúng tôi nhận thấy, phần ñông sinh viên vẫn rất hạn chế trong việc trình bày source code Việc trình bày source code
Trang 16không phải là yếu tố quyết ñịnh tính ñúng sai hay hiệu quả của chương trình, nhưng lại là yếu tố quyết ñịnh kết quả và khả năng lập trình của các lập trình viên Hầu như rằng các lập trình viên giỏi luôn trình bày source code của họ rất rõ ràng, logic
Trong các ứng dụng viết bằng C trên MS Windows, người ta thường dùng cú pháp Hungary
(Hungarian Notation) ñể ñặt tên biến, hàm,… trong chương trình Các biến thường bắt ñầu bằng
các ký tự chữ thường thể hiện cho kiểu dữ liệu sau ñó là tên biến, như nCmdLine là một biến kiểu
số nguyên int (number), hwndEdit là một biến ñịnh danh (handle) của cửa sổ edit kiểu HWND
Về tên biến và tên hàm sẽ viết hoa ký tự ñầu tiên trong mỗi từ (word), các từ viết liền nhau Ví
dụ biến szWindowClass, hàm InitInstance, hàm CreateWindow
Tương tự, việc canh lề các dòng code cũng cần lưu ý Bằng cách ñặt cùng lề cho các dòng lệnh cùng cấp, ta dễ dàng tìm ra các lệnh nào liên hệ với nhau như thế nào,… Và như vậy rất dễ dàng khi chỉnh sửa các lỗi về cú pháp Ví dụ như ở chương trình mẫu ở trên, ở hàm About (dòng
127 ñến 144) việc rẽ nhánh các thông ñiệp (lệnh switch ở dòng 130) cùng cấp với lệnh return
(dòng 143), các trường hợp (case) của các thông ñiệp thì cùng cấp (dòng 132, 134)
1.4 TÓM TẮT
Trong bài học ñầu tiên này, chúng ta ñã hiểu ñược thế nào là một ứng dụng ñược lập trình trên Windows, so với một ứng dụng dạng console (MS-DOS), trong ñó tập trung giới thiệu về cấu trúc một project dạng Win32 Application gồm hai hàm chính WinMain (1.3.1) và WndProc
(1.3.2)
Chúng ta cũng ñã tìm hiểu về cách thức tạo lập dạng project này trên IDE MS Visual C++ 6.0 (1.2.1), phân tích giao diện IDE (1.2.2) và quy trình build tạo file khả thi của project (1.2.3) Phần lý thuyết chung của bài học (1.1) trình bày các ñặc ñiểm chính của MS Windows và các ứng dụng trên hệ ñiều hành này, gồm cơ chế xử lý thông ñiệp, lập trình giao diện ñồ họa GUI,
lập trình giao tiếp thiết bị ñồ họa GDI, cơ chế quản lý vùng nhớ ñộng và thư viện liên kết ñộng
DLL, trong ñó xử lý thông ñiệp (liên quan tới hàm vòng lặp nhận thông ñiệp trong hàm WinMain
rồi chuyển cho hàm WndProc xử lý) là quan trọng nhất
Ở các bài học tiếp theo chúng ta sẽ tìm hiểu lần lượt các ñặc ñiểm nêu trên qua các kiến thức
lý thuyết cơ bản, và áp dụng trên các ví dụ cụ thể
1.5 CÂU HỎI ÔN TẬP – BÀI TẬP
1.5.1 Trình bày các ñặc ñiểm chính của các ứng dụng trên MS Windows? Các ñặc ñiểm này
khác với trên môi trường MS-DOS như thế nào?
1.5.2 Cho biết quy trình biên dịch và liên kết ñể tạo file khả thi dạng Win32 Application Quy
trình này khác quy trình build thông thường trên MS-DOS như thế nào?
1.5.3 Hàm WinMain của một ứng dụng thông thường dạng Win32 Application gồm những thao tác gì? Việc gọi vòng lặp thông ñiệp có ý nghĩa như thế nào?
Trang 171.5.4 Tạo lập project Win32 Application theo ba dạng khác nhau: “An empty project.”, “A
simple Win32 Application ” và “A typical “Hello World!” application.” - xem bước 3 phần 1.2.1
Tìm hiểu cấu trúc thể hiện khác nhau của các project trên cửa sổ Workspace, cũng như các file ñược tạo lập theo từng chọn lựa (trên thư mục chứa project tương ứng)
1.5.5 Với project “A typical “Hello World!” application.”, liệt kê các kiểu dữ liệu và các hàm
trong ứng dụng Sau ñó tìm hiểu về chúng trong MSDN
Trang 18Bài 2
GIAO DIỆN ðỒ HỌA NGƯỜI DÙNG
Như đã giới thiệu trong bài học đầu tiên, giao diện đồ họa người dùng (GUI – Graphical User
Interface) chính là đặc điểm làm cho các ứng dụng trên MS Windows trở nên thân thiện, dễ sử dụng… Và cũng chính nhờ đặc điểm này mà các chương trình trên máy tính trở nên gần gũi và thực tế hơn rất nhiều so với các ứng dụng trước đây trên MS-DOS
Bài học này sẽ giúp các bạn tạo lập cũng như lập trình xử lý cho các đối tượng giao diện quen thuộc trên Windows như thanh trình đơn (menu), các đối tượng điều khiển (control) như button, edit, combo box,… trên mơi trường cơng cụ MS Visual C++ 6.0
2.1 CƠ CHẾ LẬP TRÌNH SỬ DỤNG TÀI NGUYÊN
Khi sử dụng các ứng dụng trên Windows, chắc chắn các bạn dễ dàng nhận ra chúng cĩ cùng những đặc trưng giao diện cũng như cách xử lý các đối tượng giao diện này Ví dụ, khi muốn thực hiện một cơng việc nào đĩ, người ta thường chọn theo từng nhĩm các thao tác đã được định
trước trên một thanh menu; khi thao tác các phím nĩng người ta cĩ thể dùng tổ hợp Alt + ký tự
gạch dưới của một đối tượng menu item, button, static,… nào đĩ
Việc thể hiện giao diện giống nhau cũng như cĩ cùng cơ chế xử lý của các ứng dụng trên Windows là do hệ điều hành quyết định Chúng được định nghĩa trong các tập tin liên kết của hệ thống (USER.EXE - 16 bits và USER32.DLL - 32 bits) Dựa trên đặc điểm này, các cơng cụ soạn thảo và biên dịch được xây dựng sao cho các lập trình viên khi lập trình chỉ cần thực hiện các thao tác khá đơn giản là đưa thơng tin các đối tượng cần tạo lập theo các mẫu (template) đã được định nghĩa trước Cơ chế này gọi là lập trình sử dụng tài nguyên (resource-based
programming)
2.1.1 Cửa sổ workspace ResourceView
Trong các project ứng dụng trên Windows viết bằng C, tài nguyên được định nghĩa trong các tập
tin RC (resource) Với sự hỗ trợ trực quan của MS Visual C++ 6.0, ta dễ dàng xem và thao tác
các resource này trong workspace ResourceView
Trang 19Hình 2.1 Tạo lập resource trên môi trường Visual C++ 6.0
Bằng cách chọn tập tin BaiTap.rc trong workspace FileView hoặc click chọn vào
ResourceView của project BaiTap trong bài 1, các tài nguyên của project này sẽ thể hiện như
trong hình 2.1
ðể thao tác resource nào ta chỉ việc click chọn vào ñối tượng ấy trên ResourceView Khi ñó mặc ñịnh Visual C++ 6.0 sẽ tự ñộng mở cửa sổ resource editor trực quan tương ứng ñể ta thêm, bớt, hoặc chỉnh sửa
2.1.2 Cấu trúc resource dạng văn bản và editor trực quan
Thật ra, tài nguyên ñược ñịnh nghĩa theo các ñịnh dạng văn bản (text) cho sẵn trong tập tin RC
Và khi ta mở các tập tin này, Visual C++ 6.0 mới nạp dữ liệu text này và thể hiện giao diện trực quan cho ta dễ thao tác
Trang 20Hình 2.2 Chọn mở xem tập tin resource dưới dạng text
Do ñó, trong bài học, chúng ta chủ yếu tìm hiểu cách thao tác trực quan Bên cạnh ñó, chúng
ta cũng cần hiểu cơ bản về cách tổ chức văn bản của các resource ñể có thể chỉnh sửa khi dữ liệu không chính xác và Visual C++ 6.0 không thể nạp ñược
Khi chọn xem tập tin RC dưới dạng text (Open as Text)- xem hình 2.2, Visual C++ 6.0 sẽ
thể hiện toàn bộ nội dung văn bản của tập tin này ở cửa sổ soạn thảo (text editor) Với project
BaiTap ta có nội dung tập tin BaiTap.rc như sau:
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32 LANGUAGE 9, 1
#pragma code_page(1252)
#endif //_WIN32 /////////////////////////////////////////////////////////////////////////////
//
// Icon //
// Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems
IDI_BAITAP ICON DISCARDABLE "BaiTap.ICO"
IDI_SMALL ICON DISCARDABLE "SMALL.ICO"
/////////////////////////////////////////////////////////////////////////////
//
// Menu //
IDC_BAITAP MENU DISCARDABLE BEGIN
POPUP "&File"
BEGIN MENUITEM "E&xit", IDM_EXIT END
POPUP "&Help"
BEGIN MENUITEM "&About ", IDM_ABOUT END
END /////////////////////////////////////////////////////////////////////////////
//
// Accelerator //
IDC_BAITAP ACCELERATORS MOVEABLE PURE BEGIN
"?", IDM_ABOUT, ASCII, ALT
Trang 21IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 230, 75 STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU CAPTION "About"
FONT 8, "System"
BEGIN ICON IDI_BAITAP,IDC_MYICON,14,9,16,16 LTEXT "BaiTap Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX LTEXT "Copyright (C) 2003",IDC_STATIC,49,20,119,8
DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP END
#ifdef APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE //
2 TEXTINCLUDE DISCARDABLE BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// String Table //
STRINGTABLE DISCARDABLE BEGIN
#ifndef APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
Trang 22Dialog IDD_ABOUTBOX tương ứng ñoạn text từ dòng 55 ñến 64, v.v…
Ngoài các macro ñịnh nghĩa các dạng mẫu resource - ví dụ DIALOG là macro cho biết
resource là hộp thoại, LTEXT là macro của control static text canh lề trái trên hộp thoại,… -
trong nội dung tập tin RC ở trên còn có các giá trị ñược ñịnh nghĩa xác ñịnh ñịnh danh
(identifier) của các ñối tượng tài nguyên ðây là ñiểm cần chú ý khi tạo lập và chỉnh sửa các
resource Trong phần tìm hiểu các ñối tượng resource cụ thể như menu, control, … ở sau, chúng
ta sẽ nói lại về vấn ñề này Chúng ñược ñịnh nghĩa (define) thay cho các số nguyên ñể người
dùng dễ lập trình Khi viết code, ví dụ trong phần xử lý menu trong thông ñiệp WM_COMMAND của project bài tập, lập trình viên xử lý menu item thông qua giá trị ñã ñịnh
nghĩa này (IDM_EXIT, IDM_ABOUT) mà không quan tâm tới giá trị số nguyên cụ thể của nó;
trong khi ñể quản lý các menu item thì hệ thống quản lý thông qua giá trị số của chúng
Hình 2.3 Chọn xem thông tin của tài nguyên
Trong project Win32 Application mà Visual C++ 6.0 tạo ra thì các giá trị này ñược phát sinh ñồng thời trong tập tin resource.h khi ta thao tác các resource tương ứng Nội dung tập tin
resource.h của project BaiTap như sau:
Trang 23Hình 2.4 Xem thông tin về tập tin resource.h
Trang 24Hình 2.5 Macro và giá trị của tài nguyên ñã ñịnh nghĩa Ngoài ra, ñể xem các macro ñược ñịnh nghĩa của các tài nguyên trong tập tin BaiTap.rc ta
có thể xem trực quan qua sự hỗ trợ của MS Visual C++ 6.0 Bằng cách click chuột phải (mặc ñịnh) lên “BaiTap resources” trên ResourceView – hình 2.3 – ta chọn tiếp “Resouce
Includes…” ñể xem các thông tin về tập tin resource.h – hình 2.4, hoặc chọn “Resource Symbols…” ñể xem giá trị các macro ñã ñược ñịnh nghĩa – hình 2.5.
2.2 TRÌNH ðƠN
Trong hầu hết các ứng dụng, thao tác của người dùng thường thể hiện theo từng nhóm công việc
ðể tiện việc chọn lựa công việc, chúng ta có thể lần theo một cây phân cấp gọi là menu (trình ñơn) Nhờ có menu, giao tiếp giữa người dùng với ứng dụng trở nên dễ dàng, ñơn giản thông qua
lựa chọn trực quan các mục chọn gọi là menu item
MS Windows hỗ trợ một số dạng thể hiện menu dựa trên các yêu cầu thao tác khác nhau của
người dùng như shortcut menu (thường thể hiện khi người dùng rightclick lên cửa sổ ứng dụng), popup menu (thanh menu thông thường dưới thanh tiêu ñề ở các ứng dụng), system
menu (menu hệ thống của cửa sổ ứng dụng, gằn liền các thao tác như Maximize, Minimize,
Close,…)
Trong phạm vi môn học, chúng ta chỉ tìm hiểu cơ bản về menu với chức năng ñơn giản nhất
là nhận thao tác của người dùng (mà không xử lý thể hiện giao diện phức tạp với font chữ, icon ảnh,…) thông qua thanh menu thông thường – xem hình 2.6
Các item của thanh menu hiển thị ngay dưới thanh tiêu ñề của ứng dụng tạo thành thanh
menu chính (top-level menu) Trên menu chính, chúng ta có một hay nhiều item liệt kê – ñại diện cho nhóm chức năng - gọi là drop-down menu (thả xuống) hay popup menu (bung lên) Ứng với
Trang 25mỗi item liệt kê như vậy, chúng ta lại có thể cấu trúc như ở menu chính, nghĩa là có thể tạo thêm các popup cấp 2 (rồi tương tự sau ñó là cấp 3, 4,…) và các item con khác Chú ý là chúng ta chỉ nhận và xử lý thao tác của các menu item mà thôi
Hình 2.6 Thanh menu của MS Visual C++ 6.0
2.2.1 Tạo mẫu tài nguyên menu trên công cụ trực quan
Có hai cách chính ñể tạo lập một tài nguyên menu trong tập tin resource (.RC)
1 Cách 1: Mở tập tin RC dưới dạng text và sau ñó tiến hành tạo menu bằng cách gõ các dòng
lệnh theo cấu trúc của menu trong tập tin RC Thông thường, cách này ít dùng vì khá phức tạp, khó nhớ và khó quản lý các giá trị chỉ danh của các item mà ta phải ñịnh nghĩa Chúng ta
sẽ không tìm hiểu về cách thao tác này trong môn học, tuy nhiên, các bạn cần tham khảo cấu trúc có sẵn của chúng sau khi tạo lập trực quan (ở cách 2) ñể có thể xử lý khi có lỗi về phần code tương ứng
2 Cách 2: Sử dụng công cụ tạo lập và thao tác tài nguyên trực quan của MS Visual C++ 6.0
Bằng cách chọn tài nguyên của project qua workspace ResourceView như ở hình 2.1, rồi
chọn mục Menu ñể thao tác Ta có thể chọn một menu ñã có sẵn ñể sửa lại, hoặc Insert
Menu ñể thêm menu mới (Visual C++ 6.0 sẽ tạo một menu với ID của menu tự phát sinh Ta
có thể click chuột phải trên menu mới tạo, chọn Properties ñể thay ñổi các thông số cần thiết – xem hình 2.7)
Trang 26Hình 2.7 Properties của tài nguyên menu IDC_NOTEPAD
Một ứng dụng thường có nhiều menu item, và mỗi menu item ñược hệ thống quản lý thông qua một số nguyên xác ñịnh như là ñịnh danh của nó Khi lập trình, ñể dễ quản lý các ñịnh danh,
ta ñịnh nghĩa (define) chúng thông qua các tên khác nhau
Một item ñược ñịnh nghĩa với nội dung hiển thị (Caption), ñịnh danh ID và kiểu thể hiện (styles) Chúng ta có thể thao tác trực quan khi click chọn properties của chúng trên công cụ trực quan – xem hình 2.8
Hình 2.8 Properties của menu item IDM_NEW
1 Caption chính là nội dung hiển thị trên menu ðể thể hiện chức năng hỗ trợ phím nóng
<Alt+ký tự gách dưới>, ta dùng ký tự ‘&’ trước ký tự tương ứng ðể ñặt tab (theo cột cho
các item của một popup), ta dùng ký tự tab ‘\t’ trước phần text của cột mới, và dùng ký tự canh lề align ‘\a’ trong text ñể canh hàng phải cho các ký tự ñi sau nó
2 ID là giá trị ñịnh danh của mỗi item Như ñã nói ở trên, ñây là các giá trị số nguyên, tuy
nhiên ñể dễ lập trình, ta sẽ ñịnh nghĩa chúng dưới dạng các tên gợi nhớ Khi thao tác, chúng
ta chỉ cần ghi tên gợi nhớ, mặc ñịnh với các project của chúng ta thì Visual C++ 6.0 phát sinh
phần ñịnh nghĩa trong tập tin resource.h (tập tin này ñược include trong tập tin RC tương
Trang 27Kiểu Tác dụng
Separator Tạo ñường phân cách giữa các item
Check Thêm dấu check bên trái item
Pop-up Tạo item dạng popup
Grayed Vô hiệu hoá một item Text hiển thị của item
2.2.2 Nạp tài nguyên menu vào ứng dụng
ðể nạp tài nguyên menu vào ứng dụng khi thực thi chương trình, ta có những cách thông dụng sau:
1 Khi ñịnh nghĩa lớp cửa sổ chính trong hàm MyRegisterClass, ta thiết lập thuộc tính
lpszMenuName của cấu trúc lớp WNDCLASSEX (hoặc WNDCLASS) bằng ñịnh danh của
menu trong tập tin tài nguyên
Ví dụ: Nếu khai báo biến WNDCLASSEX wcex trong hàm MyRegisterClass(HINSTANCE
hInstance )thì ta nạp menu IDR_MENU1 bằng cách gán:
wcex.lpszMenuName = (LPCSTR)IDR_MENU1;
2 Dùng hàm LoadMenu với tham số thứ hai là ID của menu và nhận về handle của menu
HMENU hMenu;
hMenu = LoadMenu(hInstance, (LPCSTR)IDR_MENU1);
Khi có handle của menu rồi ta nạp menu vào ứng dụng bằng một trong hai cách sau:
1 Cách 1: Khi gọi hàm tạo cửa sổ chính CreateWindow (hoặc CreateWindowEx) trong hàm InitInstance,ta chỉ ñịnh tham số hMenu là handle nhận ñược ở trên
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);
Trong trường hợp này menu chỉ ñịnh trong hàm CreateWindow sẽ thay thế lên menu trước ñó (nếu có) của lớp cửa sổ szWindowClass
2 Cách 2: Nếu trong hàm CreateWindow tham số hMenu bằng NULL, ta gọi hàm
SetMenu với tham số thứ hai là ñịnh danh của menu ñã tạo lập
SetMenu(hWnd, hMenu);
Cách này cho phép ta thay ñổi ñộng menu của cửa sổ
Trang 282.2.3 Xử lý thông ñiệp menu
Windows phát sinh một số thông ñiệp và gởi cho cửa sổ cha của của menu khi người dùng tác ñộng lên chúng ðối với cửa sổ chính của các ứng dụng thông thường mà chúng ta ñang thực hành, ta có thể chặn các thông ñiệp thường gặp sau ñây và viết code xử lý trong hàm WndProc
1 WM_INITMENU: Thông ñiệp này ñược phát sinh khi các menu item nhận tác ñộng, với hai thông số wParam, và lParam như sau
2 wParam: Handle của menu
3 lParam: Không sử dụng
Thông thường, ta chặn thông ñiệp này ñể xử lý các thao tác thay ñổi trạng thái các item trước khi chúng nhận tác ñộng của người dùng
2 WM_INITMENUPOPUP: Thông ñiệp ñược phát sinh khi một drop-down menu hoặc
submenu có khả năng ñược kích hoạt
3 wParam: handle của menu
4 lParam: Giá trị word thấp (LOWORD) xác ñịnh thứ tự (từ 0) của các item mở
drop-down hay submenu; và word cao (HIWORD) cho biết drop-drop-down menu có phải là system menu không, bằng TRUE nếu phải và ngược lại
Tương tự thông ñiệp WM_INITMENU, thông ñiệp này ñược dùng ñể hiệu lực hoá
hay vô hiệu hoá một item trong popup menu trước khi nó ñược hiển thị Ví dụ như trong
chương trình soạn thảo văn bản Notepad, trước khi hiển thị item Paste, chương trình
kiểm tra dữ liệu trong bộ nhớ ñệm clipboard, nếu có thì item Paste mới có hiệu lực còn ngược lại item Paste không có hiệu lực
ðể hiệu lực hoá hay vô hiệu hoá một item uIDItem trong menu hMenu ta dùng hàm EnableMenuItem với thông số cờ uEnable là MF_ENABLED, MF_DISABLED hoặc
MF_GRAYED kết hợp cách chọn MF_BYCOMMAND, hoặc MF_BYPOSITION BOOL EnableMenuItem(HMENU hMenu, UINT uIDItem, UINT uEnable );
3 WM_MENUSELECT: Thông ñiệp này ñược phát sinh khi người dùng bàn phím hay
thiết bị chuột chuyển thao tác chọn (select) các item của menu
4 LOWORD(wParam): Menu ID hoặc chỉ mục của popup menu
5 HIWORD(wParam): Các cờ chọn lựa
6 lParam: Handle của menu
Các cờ cửa thông ñiệp này có thể là kết hợp của các giá trị MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP,MF_POPUP, MF_HELP, MF_SYSMENU, và MF_MOUSESELECT Chúng ta thường sử dụng thông ñiệp
WM_MENUSELECT khi muốn thay ñổi một số thể hiện trên vùng làm việc của cửa sổ
Trang 29khi di chuyển chọn các item Mặc ñịnh ta ñể Windows xử lý khi gọi hàm
DefWindowProc
4 WM_COMMAND: Thông ñiệp ñược phát sinh khi người dùng chọn một item trên
menu ðây là thông ñiệp ñược sử dụng nhiều nhất khi ta thao tác menu ứng dụng
WM_COMMAND cũng là thông ñiệp gởi cho cửa sổ cha của các control (phần 2.4) khi
chúng nhận thao tác, hoặc là tác ñộng menu và control dưới dạng phím nóng (phần 2.3) Các thông số gởi kèm thông ñiệp này như sau
5 LOWORD(wParam): Menu ID hoặc control ID
6 HIWORD(wParam): Mã thông ñiệp thông báo (notification code) của control, bằng
1 cho phím nóng và bằng 0 nếu thao tác menu
7 lParam: Handle của control gởi thông ñiệp thông báo Các trường hợp khác giá trị
này là NULL
8 Giống như thông ñiệp WM_COMMAND, thông ñiệp WM_SYSCOMMAND ñược
phát sinh khi người dùng chọn một item trong menu hệ thống Menu hệ thống là menu do Windows cấp với ñịnh danh ID của các item ñược ñịnh nghĩa sẵn Các ID này ñược xác ñịnh thông qua tham số wParam với các giá trị như SC_CLOSE, SC_MAXIMIZE,
SC_MINIMIZE, SC_MOVE Thông số lParam xác ñịnh vị trí tọa ñộ màn hình nếu
system menu nhận tác ñộng bằng thiết bị chuột
ðể hiểu rõ ta tiến hành tạo project ứng dụng Win32 Application dạng A typical “Hello
World!” application có tên là MyNotepad - tương tự chương trình soạn thảo văn bản Notepad của Windows Tạo sẽ tạo lập các menu item với Caption như ở bảng 2.2 (phần phím tắt sẽ tìm
Page Set&up… IDM_PAGE_SETUP
Trang 30&Paste\tCtrl+V IDM_PASTE Ctrl+V De&lete\tDel IDM_DELETE Delete
&Find \tCtrl+F IDM_FIND Ctrl+F Find &Next\tF3 IDM_FIND_NEXT F3
&Replace \tCtrl+H IDM_REPLACE Ctrl+H
&Go To \tCtrl+G IDM_GOTO Ctrl+G Select &All\tCtrl+A IDM_SELECT_ALL Ctrl+A Time/&Date\tF5 IDM_TIME_DATE F5
Popup F&ormat
&Word Wrap IDM_WORD_WRAP
Popup &Help
&Help Topics IDM_HELP_TOPICS
&About MyNotepad IDM_ABOUT Sau khi tạo lập bằng công cụ trực quan, ta có thể xem lại nội dung của tài nguyên menu
IDC_MYNOTEPAD dưới dạng text như sau:
MENUITEM "Page Set&up…", IDM_PAGE_SETUP MENUITEM "&Print \tCtrl+P", IDM_PRINT MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_EXIT END
POPUP "&Edit"
BEGIN MENUITEM "&Undo\tCtrl+Z", IDM_UNDO MENUITEM SEPARATOR
MENUITEM "Cu&t\tCtrl+X", IDM_CUT MENUITEM "&Copy\tCtrl+C", IDM_COPY MENUITEM "&Paste\tCtrl+V", IDM_PASTE MENUITEM "De&lete\tDel", IDM_DELETE MENUITEM SEPARATOR
MENUITEM "&Find \tCtrl+F", IDM_FIND MENUITEM "Find &Next\tF3", IDM_FIND_NEXT MENUITEM "&Replace \tCtrl+H", IDM_REPLACE MENUITEM "&Go To \tCtrl+G", IDM_GOTO MENUITEM SEPARATOR
MENUITEM "Select &All\tCtrl+A", IDM_SELECT_ALL MENUITEM "Time/&Date\tF5", IDM_TIME_DATE END
POPUP "F&ormat"
BEGIN MENUITEM "&Word Wrap", IDM_WORD_WRAP MENUITEM "&Font…", IDM_FONT END
POPUP "&Help"
Trang 312.3.1 Tạo lập tài nguyên Accelerator
Tương tự như các dạng tài nguyên khác, ta có thể tạo lập tài nguyên phím tắt dưới dạng văn bản theo cấu trúc ñược ñịnh nghĩa sẵn Tuy nhiên, ta cũng không làm theo cách ñó mà chỉ thao tác với công cụ trực quan của MS Visual C++
Hình 2.9 Thao tác bảng phím tắt trên Visual C++ 6.0
Từ cửa sổ workspace ResourceView của project MyNotepad, ta chọn mục Accelerator, rồi
chọn ñối tượng IDC_MYNOTEPAD Visual C++ sẽ liệt kê cho chúng ta các phím tắt nào ñã
ñược ñịnh nghĩa, và ta chỉ việc thêm hay thay ñổi các giá trị của chúng
Ta có thể thêm một phím tắt bằng cách click vào một mục trống trên bảng phím tắt, lúc này
Visual C++ mở hộp thoại Accel Properties ñể ta cập nhật các thông tin cần thiết theo mục ñích của project ứng dụng Bảng 2.3 giải thích các thông số trên hộp thoại Accel Properties
Bảng 2.3 Các thông số thuộc tính Accelerator
ID ðịnh danh ID của Item muốn tạo phím tắt
Trang 32Key Phím kết hợp trong tổ hợp phím tắt
Ctrl Kết hợp thêm phím Ctrl
Alt Kết hợp thêm phím Alt
Shift Kết hợp thêm phím Shift
Type Chọn mã phím ảo (virtual) hay mã ASCII
ðể ý là trong mục Key, bên cạnh các phím thể hiện ký tự cụ thể, các phím hệ thống ñược ñịnh nghĩa với tên là VK_XXX, như VK_F1, VK_ADD khi muốn kết hợp các phím này thì ta chọn các tên mã có sẵn
Với liệt kê ở bảng 2.2, các bạn hãy tạo bảng phím tắt cho ứng dụng MyNotepad
2.3.2 Nạp và xử lý bảng Accelerators trong ứng dụng
ðể sử dụng tài nguyên Accelerator, ta cần khai báo một biến handle của nó, kiểu là HACCEL Rồi dùng hàm LoadAccelerators ñể nạp tài nguyên lpTableName vào cho tiến trình ứng dụng
hInstance Hàm này trả về handle của bảng Accelerators
HACCEL LoadAccelerators(HINSTANCE hInstance, LPCTSTR lpTableName );
Sau khi có handle của bảng Accelerators, ta dùng hàm TranslateAccelerator ñể chuyển các
thao tác phím có liên quan thành thông ñiệp WM_COMMAND ñể gởi cho cửa sổ tương ứng
trong vòng lặp xử lý thông ñiệp của hàm WinMain Các bạn xem lại ñoạn code xử lý tương ứng ñược MS Visual C++ 6.0 phát sinh trong các project ñã tìm hiểu
int TranslateAccelerator(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg );
ðoạn code dưới ñây của hàm WndProc trong tập tin MyNotepad.cpp thể hiện việc xuất lên
màn hình thông báo việc người dùng chọn các item (hoặc trực tiếp hoặc bằng tổ hợp phím tắt)
của ứng dụng MyNotepad, sử dụng hàm MessageBox
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
Tham số hWnd xác ñịnh cửa sổ cha quản lý hộp thoại thông báo, lpCaption là tiêu ñề hộp thoại và lpText là nội dung thông báo Ta có thể kết hợp các giá trị cờ xác ñịnh dạng thông báo
MB_ICONQUESTION Cho tham số uType Hàm trả về giá trị xác ñịnh cách chọn thao tác của
người dùng như IDOK, IDCANCEL, IDYES,…
Trang 33// Popup File case IDM_NEW:
MessageBox(NULL, "Ban vua chon muc File New",
"Thong bao", MB_OK); break;
case IDM_OPEN:
MessageBox(NULL, "Ban vua chon muc File Open",
"Thong bao", MB_OK); break;
case IDM_SAVE:
MessageBox(NULL, "Ban vua chon muc File Save",
"Thong bao", MB_OK); break;
case IDM_SAVE_AS:
MessageBox(NULL, "Ban vua chon muc File Save As",
"Thong bao", MB_OK); break;
case IDM_PAGE_SETUP:
MessageBox(NULL, "Ban vua chon muc File Page
Setup", "Thong bao", MB_OK); break;
case IDM_PRINT:
MessageBox(NULL, "Ban vua chon muc File Print",
"Thong bao", MB_OK); break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
// Popup Edit case IDM_UNDO:
MessageBox(NULL, "Ban vua chon muc Edit Undo",
"Thong bao", MB_OK); break;
case IDM_CUT:
MessageBox(NULL, "Ban vua chon muc Edit Cut",
"Thong bao", MB_OK); break;
case IDM_COPY:
MessageBox(NULL, "Ban vua chon muc Edit Copy",
"Thong bao", MB_OK); break;
case IDM_PASTE:
MessageBox(NULL, "Ban vua chon muc Edit Paste",
"Thong bao", MB_OK); break;
case IDM_DELETE:
MessageBox(NULL, "Ban vua chon muc Edit Delete",
"Thong bao", MB_OK); break;
case IDM_FIND:
MessageBox(NULL, "Ban vua chon muc Edit Find",
"Thong bao", MB_OK); break;
case IDM_FIND_NEXT:
MessageBox(NULL, "Ban vua chon muc Edit Find
Next", "Thong bao", MB_OK); break;
case IDM_REPLACE:
MessageBox(NULL, "Ban vua chon muc Edit Replace",
"Thong bao", MB_OK); break;
Trang 34MessageBox(NULL, "Ban vua chon muc Edit Goto",
"Thong bao", MB_OK);
break;
case IDM_SELECT_ALL:
MessageBox(NULL, "Ban vua chon muc Edit Select
All", "Thong bao", MB_OK);
break;
case IDM_TIME_DATE:
MessageBox(NULL, "Ban vua chon muc Edit Time
Date", "Thong bao", MB_OK);
break;
// Popup Format case IDM_WORD_WRAP:
MessageBox(NULL, "Ban vua chon muc Format Word
Wrap", "Thong bao", MB_OK);
break;
case IDM_FONT:
MessageBox(NULL, "Ban vua chon muc Format Font",
"Thong bao", MB_OK);
break;
// Popup Help case IDM_HELP_TOPICS:
MessageBox(NULL, "Ban vua chon muc Help Topics",
"Thong bao", MB_OK);
}
2.4 CÁC ðỐI TƯỢNG ðIỂU KHIỂN
Trên môi trường trực quan của Windows, bên cạnh cửa sổ chính dùng ñể thể hiện dữ liệu ứng dụng, hay menu dùng ñể thể hiện các nhóm thao tác chọn lựa của người dùng, chúng ta còn thường thấy
một số dạng cửa sổ con có giao diện và cách xử lý ñặc trưng, gọi là ñối tượng ñiều khiển (control)
Về bản chất, ñối tượng control ñược cấu trúc như một cửa sổ, nhưng có những xử lý khác hơn Chúng thường ñược sử dụng như các công cụ hữu hiệu nhất ñể xuất (thể hiện) và nhận thông tin cơ bản từ người dùng theo các ñịnh dạng cho trước Các control thường ñược sử dụng trên các hộp thoại
Trang 35(xem bài 3), tuy nhiên chúng ta cũng có thể thao tác chúng ngay trên cửa sổ chính
Khi lập trình bằng Win32 Application, chúng ta sử dụng các ñối tượng control ñã ñược Windows ñịnh nghĩa trước, hoặc xây dựng dạng control riêng cho ứng dụng Các ñối tượng ñã ñược ñịnh nghĩa sẵn bao gồm:
1 Button: ðối tượng nút nhấn hoặc chọn, gồm push button (nút nhấn), check box (ô chọn), radio button (ô chọn theo nhóm), group box (khung nhóm), bitmap button (nút nhấn dưới
dạng ảnh)
2 Edit: ðối tượng cho phép người dùng thao tác văn bản như gõ văn bản, copy, xóa
3 Rich edit: Tương tự như edit text, nhưng ñối tượng này còn hỗ trợ thêm việc ñịnh dạng văn
bản text và có khả năng kết hợp ñược các ñối tượng nhúng COM Hiện tại Microsoft cung
cấp cho chúng ta hai dạng Rich edit qua hai phiên bản 1.0 và 2.0
4 List box: ðối tượng cho phép liệt kê và chọn lựa một hay nhiều mục chọn trong một danh
sách ñược xây dựng
5 Combo box: ðối tượng có dạng kết hợp của một edit và một list box, cho phép người dùng
thao tác edit hay chọn lựa các mục chọn trong một danh sách
6 Scroll bar: ðối tượng thanh trượt, thường ñược sử dụng ñể xác ñịnh hướng và kích thước hỗ
trợ cho việc cuộn xem các thông tin của một cửa sổ nào ñó
7 Static: ðối tượng control ñơn giản nhất, ñóng vai trò như phần nhãn (label) text, hình ảnh hỗ
trợ cho các control khác ðối tượng static, theo như cách gọi tên của nó, là tĩnh – thường không nhận các tác ñộng của người dùng
Trong phạm vi môn học, chúng ta chỉ tìm hiểu về các control thông qua ñối tượng edit, với
những vấn ñề chính nhất là tạo lập như một cửa sổ, phân tích các kiểu của từng loại ñối tượng, và xử
lý các dạng thông ñiệp liên quan, nhất là thông ñiệp thông báo
2.4.1 Thao tác control với các hàm cửa sổ
Như ñã nói ở trên, control cũng là ñối tượng cửa sổ, vì thế chúng cũng ñược tạo lập giống như cửa sổ chính Ta có thể sử dụng hàm CreateWindow hoặc CreateWindowEx ñể tạo lập một ñối tượng control
HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int
x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance ,
LPVOID lpParam );
Hàm CreateWindow tạo lập và trả về một ñối tượng control HWND xác ñịnh bởi lớp
control lpClassName ñược ñịnh nghĩa trước Ứng với các ñối tượng liệt kê ở trên, ta dùng các tên
lớp là BUTTON, EDIT, RICHEDIT, LISTBOX, COMBOBOX, SCROLLBAR và STATIC
Với mỗi lớp ñối tượng, Windows căn cứ vào tham số dwStyle ñể tạo lập kiểu cụ thể Như ñã
nói, ñối tượng button có thể là push button, radio button, check box,… và ñược xác ñịnh
Trang 36thông qua giá trị của dwStyle là BS_PUSHBUTTON, BS_RADIOBUTTON,
BS_CHECKBOX Ngoài ra, chúng ta cũng cần kết hợp một số cờ khác của kiểu cửa sổ theo yêu
cầu của ứng như control là cửa sổ con WS_CHILD, ñược hiển thị khi gọi tạo lập
WS_VISIBLE,…
Các thông số còn lại tương tự như ñã tìm hiểu khi tạo lập cửa sổ chính của ứng dụng Tuy
nhiên, lưu ý là nếu ta tạo control như một cửa sổ con (kiểu là WS_CHILD) thì vị trí x, y của nó dựa vào tọa ñộ của cửa sổ cha hWndParent
Nếu tạo lập thành công, ứng dụng trả về handle của control, và ta cũng sử dụng các hàm chung về ñối tượng cửa sổ ñể thao tác như ShowWindow, EnableWindow, MoveWindow 2.4.2 Xử lý thông ñiệp cho cửa sổ control
Với các lớp cửa sổ control hỗ trợ xử lý thông ñiệp, ta có thể thao tác chúng bằng cách gởi các thông ñiệp ñến giống như ñối với một cửa sổ thông thường ðể gởi một thông ñiệp ñến một cửa
sổ, ta có thể dùng hàm SendMessage hoặc PostMessage
LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
LRESULT PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
Hàm SendMessage kết thúc khi thủ tục xử lý của cửa sổ hWnd ñã xử lý xong thông ñiệp
Msg Còn hàm PostMessage chỉ gởi thông ñiệp mà không cần ñợi xử lý xong
Vấn ñề của người lập trình chúng ta là phải biết ñược ứng với mỗi ñối tượng control thì ta có thể gởi những thông ñiệp nào, với các tham số wParam và lParam ñi kèm ra sao ñể xử lý ñược các công việc theo yêu cầu ñặt ra của ứng dụng
Bên cạnh các thông ñiệp thông thường của một cửa sổ mà lớp control có hỗ trợ như vừa ñề cập, mỗi dạng control còn ñịnh nghĩa thêm một số thông ñiệp khác gắn liền với thao tác xử lý của nó Dạng thông ñiệp này ñược gởi cho cửa sổ cha quản lý ñối tượng control thông qua thông ñiệp WM_COMMAND, và ñược biết dưới tên là thông ñiệp thông báo (notification message) khi control này nhận tác ñộng khác nhau từ người dùng ðây chính là giá trị xác ñịnh bởi thông
số HIWORD(lParam) ñã ñược tìm hiểu ở trên trong phần 2.2.3
Chẳng hạn, ñối với ñối tượng button, khi người dùng click chuột, double click hoặc chuyển
focus (tìm hiểu ở bài 5) thì control này gởi cho cửa sổ cha giá trị thông ñiệp thông báo thông qua
các tên thông ñiệp ñã ñược Windows ñịnh nghĩa trước là BN_CLICKED, BN_DBLCLK,
BN_SETFOCUS, và BN_KILLFOCUS
2.4.3 Minh họa với ứng dụng MyNotepad
Với phạm vi môn học, chúng ta không thể liệt kê ñầy ñủ các kiểu và các dạng thông ñiệp liên quan ñến các loại control Vì thế, khi xây dựng các ứng dụng cụ thể, chúng ta cần tham khảo trong MSDN
Ở ñây, tôi sẽ cùng các bạn minh họa việc tạo lập và thực hiện một số thao tác cơ bản về văn
Trang 37bản trên edit text gắn với cửa sổ chính của ứng dụng MyNotepad
ðầu tiên, ta cần tạo lập thêm cửa sổ thao tác văn bản cho ứng dụng Chúng ta có thể tạo lập
cửa sổ này dưới dạng là một ñối tượng edit hoặc là rich edit Ở ñây chúng tôi chọn ñối tượng là rich edit version 1.0, với handle là biến tĩnh hwndEdit Chúng ta kết hợp tạo lập tại thông ñiệp
WM_CREATE của cửa sổ chính – xin xem code minh họa ở cuối phần này
ðể ý là ñể sử dụng ñối tượng rich edit, chúng ta cần include thư viện header richedit.h, và nạp thư viện liên kết ñộng lpFileName làriched32.dll vào project bằng hàm LoadLibrary
HMODULE LoadLibrary(LPCTSTR lpFileName );
Chúng ta lại thấy là edit soạn thảo luôn có kích thước cùng với kích thước vùng làm việc của
cửa sổ chính Vì thế, ñể giải quyết vấn ñề này, ta chặn thông ñiệp WM_SIZE là thông ñiệp gởi
cho cửa sổ chính mỗi khi ứng dụng thay ñổi kích thước và cập nhật lại kích thước edit bằng hàm
MoveWindow với chiều rộng và chiều cao mới của edit chính là giá trị LOWORD và
HIWORD của thông số lParam
Một vấn ñề cần quan tâm của ứng dụng MyNotepad nữa là vấn ñề focus Chúng ta sẽ tìm
hiểu rõ hơn về vấn ñề này ở bài 5 khi nói về thao tác bàn phím Tuy nhiên, ñể giải quyết cho ứng dụng này, các bạn có thể hiểu như vậy: Trên Windows, tại một thời ñiểm chỉ có một cửa sổ có thể nhận tác ñộng của bàn phím, và cửa sổ ñó ñược xem là ở trạng thái giữ focus Và chúng ta có
những hàm và thông ñiệp liên quan ñến vấn ñề thông ñiệp này ðối với ứng dụng MyNotepad,
chúng ta có ít nhất hai cửa sổ là cửa sổ chính và cửa sổ edit, tuy nhiên tại mọi thời ñiểm, ta ñều muốn rằng thao tác phím ñược gõ sẽ gởi cho edit Và ñể làm ñiều này, mỗi khi cửa sổ chính nhận ñược focus với thông ñiệp WM_SETFOCUS, ta ñều chuyển cho edit bằng cách sử dụng hàm
SetFocus
HWND SetFocus(HWND hWnd );
Hàm SetFocus thiết lập focus cho cửa sổ hWnd và trả về handle của cửa sổ nhận focus trước
ñó
ðến lúc này, khi các bạn thực thi ứng dụng thì chương trình ñã tương ñối giống ứng dụng
Notepad Tất cả các thao tác của chúng ta tác ñộng lên edit (mà chủ yếu là thao tác bàn phím)
ñều ñược ñối tượng control này xử lý theo cách mặc ñịnh mà Windows ñã cài ñặt Tuy nhiên, chúng ta không thể kết hợp các thao tác của menu (tương ứng là phím tắt) vào cho edit
Trước khi thao tác menu và phím tắt, chúng ta cần chỉnh sửa một ít cho phần code tự phát
sinh của ứng dụng dạng A typical “Hello World!” application Chúng ta cần thay ñổi thông số
cửa sổ có nhiệm vụ dịch thông ñiệp phím tắt thành thông ñiệp COMMAND, và loại bỏ những gì
liên quan ñến thông ñiệp WM_PAINT của cửa sổ chính
Vì ứng dụng không vẽ trên vùng làm việc của cửa sổ chính (mà sử dụng edit), nên chúng ta
sẽ xoá tất cả những dòng lệnh về khai báo biến, cài ñặt,… liên quan ñến thông ñiệp này
Vì trong hàm WinMain, ở vòng lặp xử lý thông ñiệp, tham số thứ nhất của hàm
Trang 38TranslateAccelerator là msg.hwnd, tức là cửa sổ gắn liền với thông ñiệp Nếu ta ñể như vậy thì
khi ta gõ phím lên edit (luôn giữ focus), chúng sẽ không chuyển cho cửa sổ chính, và vì thế không giải quyết xem có phải là phím nóng cho menu của cửa sổ chính hay không Do ñó, ta sẽ sửa lại thành biến handle của cửa sổ chính, tức ta có dòng code thay thế:
TranslateAccelerator(hWnd, hAccelTable, &msg)
Khi ñó cần ñịnh nghĩa hWnd là biến toàn cục xác ñịnh handle của cửa sổ chính thay vì là biến toàn cục trong hàm InitInstance
Bây giờ chúng ta sẽ tìm hiểu các thông ñiệp liên quan tới edit control ñể lập trình thao tác
cho các menu item Edit như Copy, Paste, Select All, … của ứng dụng MyNotepad
ðể thực hiện các thao tác Undo, Cut, Copy, Paste và Delete, ta chỉ ñơn giản gởi các thông ñiệp WM_UNDO, WM_CUT, WM_COPY, WM_PASTE, và WM_CLEAR với các thông số
wParam và lParam ñều bằng 0 cho cửa sổ edit hwndEdit Các thông ñiệp này ñược Windows
ñịnh nghĩa trước cho ñối tượng edit và một số dạng cửa sổ khác
ðể thực hiện thao tác Select All, ta sử dụng thông ñiệp EM_SETSEL ñược ñịnh nghĩa cho ñối tượng edit Thông ñiệp này chọn (select) một ñoạn văn bản trên một edit xác ñịnh bởi vị trí ñầu và vị trí cuối gởi qua hai tham số wParam và lParam Nếu hai tham số này tương ứng là 0
và -1 thì Windows chọn toàn bộ văn bản
Các thông ñiệp trên ñược gởi bằng hàm SendMessage khi ta chặn các ID ứng với các menu
item trong thông ñiệp WM_COMMAND
Ngoài ra, như ñã trình bày ở phần 2.2.3, ta có thể kiểm tra các giá trị của ứng dụng ñể cho phép hiệu lực hoặc vô hiệu lực các item của một popup tại thông ñiệp
WM_INITMENUPOPUP Ta sử dụng thông ñiệp EM_CANUNDO ñể kiểm tra xem có cho
phép thực hiện thao tác Undo trên edit hay không, và EM_GETSEL ñể nhận xem có người dùng có ñang chọn một ñoạn text trong edit không ñể cho phép Cut, Copy hoặc Delete ðồng
thời sử dụng thêm hàm IsClipboardFormatAvailable ñể kiểm tra có dữ liệu text trong clipboard
của hệ thống hay không hầu cho phép có thể thực hiện thao tác Paste
Ta có nội dung tập tin MyNotepad.cpp sau khi cập nhật thêm một số thao tác như sau:
HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
HWND hWnd; // Chuyển biến cửa sổ hWnd thành toàn cục
// Foward declarations of functions included in this code module:
Trang 39ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// TODO: Place code here
MSG msg;
HACCEL hAccelTable;
// Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_MYNOTEPAD, szWindowClass,
MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow)) {
return FALSE;
} hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MYNOTEPAD); // Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(hWnd, hAccelTable, &msg))
{ TranslateMessage(&msg);
DispatchMessage(&msg);
} } return msg.wParam;
} //
// FUNCTION: MyRegisterClass() //
// PURPOSE: Registers the window class
Trang 40// FUNCTION: InitInstance(HANDLE, int) //
// PURPOSE: Saves instance handle and creates main window //
NULL, hInstance, NULL);
if (!hWnd) { return FALSE;
} ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
} //
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
// PURPOSE: Processes messages for the main window
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ int wmId, wmEvent;
static HWND hwndEdit;
int iSelBeg, iSelEnd, iEnable ; switch (message)
{ case WM_CREATE:
LoadLibrary("riched32.dll");
hwndEdit = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
"RICHEDIT", NULL, WS_CHILD|WS_VISIBLE|
WS_HSCROLL|WS_VSCROLL|ES_AUTOHSCROLL| ES_AUTOVSCROLL|ES_NOHIDESEL|