1. Trang chủ
  2. » Công Nghệ Thông Tin

BÀI GIẢNG: LẬP TRÌNH WINDOWS ppt

96 371 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Bài Giảng: Lập Trình Windows
Trường học Học viện hàng hải - [Trang chủ Học viện Hàng hải](http://www.hvh.edu.vn)
Chuyên ngành Công nghệ thông tin
Thể loại Bài giảng môn học
Năm xuất bản 2010
Thành phố Hải Phòng
Định dạng
Số trang 96
Dung lượng 877,41 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Mục đích của học phần này là cung cấp cho học viên một cái nhìn tổng quan, từ cơ bản tới chi tiết về các khía cạnh của lập trình trên hệ điều hành Windows, từ các chi tiết trong cấ

Trang 1

BỘ GIAO THÔNG VẬN TẢI

TRƯỜNG ĐẠI HỌC HÀNG HẢI

BỘ MÔN: KHOA HỌC MÁY TÍNH KHOA: CÔNG NGHỆ THÔNG TIN

BÀI GIẢNG LẬP TRÌNH WINDOWS

TÊN HỌC PHẦN : Lập trình Windows

MÃ HỌC PHẦN : 17214

TRÌNH ĐỘ ĐÀO TẠO : ĐẠI HỌC CHÍNH QUY

DÙNG CHO SV NGÀNH : CÔNG NGHỆ THÔNG TIN

HẢI PHÕNG - 2010

Trang 2

i

Tên học phần: Lập trình Windows Loại học phần: 2

Bộ môn phụ trách giảng dạy: Khoa học Máy tính

Khoa phụ trách: CNTT

TS tiết Lý thuyết Thực hành/Xemina Tự học Bài tập lớn Đồ án môn học

60 30 30 0 0 0

Điều kiện tiên quyết:

Sinh viên phải học xong các học phần sau mới được đăng ký học phần này:

Lâ ̣p trình hướng đối tượng, Cấu trúc dữ liê ̣u

Mục tiêu của học phần:

- Cung cấp các kiến thức cơ bản về lập trình trực quan trên hê ̣ điều hành Windows

- Cung cấp các kiến thức về truy câ ̣p và can thiê ̣p vào các t hành phần của hệ điều hành Windows

Nội dung chủ yếu

Các kiến thức về thao tác với file và thư mục , cơ sở dữ liê ̣u registry , các luồng , tiến trình, dịch vụ, các thư viện liên kết động và lập trình sockets trên Windows

Nội dung chi tiết của học phần:

TÊN CHƯƠNG MỤC

PHÂN PHỐI SỐ TIẾT

TS LT TH/Xemina BT KT

1.1 Giới thiệu về môi trườ ng lâ ̣p trình trên

Windows

1.1.1 Cở sở về hê ̣ điều hành Windows

1.1.2 Các phiên bản của hê ̣ điều hành

Chương II Hê ̣ thống file và thư mục 4 4 0

2.1 Truy cập và sử du ̣ng hê ̣ thống file trên môi

trường Windows

2.1.1 Hệ thống file và thư mu ̣c của Windows

2.1.2 Các thao tác với file và thư mục trên

Windows

2.1.3 Các vấn đề liên quan tới Unicode

2.2 Các ví dụ về thao tác với file

2.2.1 Tạo file và xử lý các lỗi liên quan

2

1

2

1

Trang 3

2.3 Quản lý file và thư mục nâng cao

2.3.1 Con trỏ file

2.3.2 Truy cập tới các thuô ̣c tính của file và

thư mu ̣c

1 1

Chương III Hê ̣ thống cơ sở dữ liê ̣u Registry 4 6 0 1

3.1 Khái niệm và vai trò của CSDL Registry

3.1.1 Các khóa, các hive

3.1.2 Các kiểu dữ liệu

3.2 Quản lý CSDL Registry

3.2.1 Thay đổi khóa

3.2.2 Thêm mớ i khóa

3.2.3 Liệt kê các khóa

3.3 Can thiệp Windows qua Registry

3.3.1 Thay đổi giao diện

3.3.2 Thay đổi các thiết lâ ̣p đối với các ổ đĩa

3.3.3 Thay đổi các thiết lâ ̣p với người dùng

Chương IV Quản lý các tiến trình và luồng 4 6 0 1

4.1 Các tiến trình và luồng trên Windows

4.2 Các thao tác với tiến trình

4.2.1 Tạo tiến trình

4.2.2 Kết thú c và thoát khỏi mô ̣t tiến trình

4.2.3 Các thao tác với biến môi trường của

Windows

4.2.4 Ví dụ : Ghi nhật ký thời gian thực hiê ̣n

của các tiến trình

4.3 Quản lý luồng (thread) trên Windows

4.3.1 Các khái niệm cơ bản

4.3.2 Mô hình Boss /Worker và các mô hình

khác

4.3.3 Bộ nhớ dành cho luồng

4.3.4 Độ ưu tiên và các trạng thái của luồng

4.4 Một số ví du ̣ về tiến trình và luồng

4.4.1 Tìm kiếm song song với các tiến trình

4.4.2 Thuật toán sắp xếp trô ̣n bằng đa luồng

Chương V Các dịch vụ của Windows 4 6 0 1

5.1 Tổng quan về dịch vu ̣ trên Windows

5.2 Các thành phần của một dịch vụ

5.2.1 Hàm main()

5.2.2 Hàm ServiceMain()

5.2.3 Kiểm soát di ̣ch vu ̣ qua các Handler

5.3 Ví du: dịch vụ đơn giản trên Windows

1

1

2

2

Trang 4

iii

TÊN CHƯƠNG MỤC

PHÂN PHỐI SỐ TIẾT

TS LT TH/Xemina BT KT

5.4 Quản lý các dịch vụ của Windows

5.4.1 Các phương pháp kiểm soát các dịch vụ

Chương VI Lâ ̣p trình ma ̣ng với Sockets 4 4 0 0

6.1 Khái niệm sockets trên Windows

6.2 Các hàm sockets phía server

6.3 Các hàm sockets phía client

6.4 Ứng dụng mạng đơn giản

6.4.1 Phía server

6.4.2 Phía client

6.5 Windows Sockets 2.0

0,5 0,5 0,5

2

0,5

1 0,5 0,5

2

Chương VII Thư viê ̣n liên kết đô ̣ng 4 4 0 0

7.1 Khái niệm và ứng dụng của thư viện liên

kết đô ̣ng

7.2 Hệ thống thư viê ̣n DLL của Windows

7.3 Các bước tạo một thư viện DLL

Nhiệm vụ của sinh viên :

Tham dự các buổi thuyết trình của giáo viên, tự học, tự làm bài tập do giáo viên giao, tham dự các bài kiểm tra định kỳ và cuối kỳ

Tài liệu học tập :

- Lê Hữu Đa ̣t Lập trình Windows NXB Giáo du ̣c

- Charles Petzold Programming Windows, fifth edition Microsoft Press 1998

- Johnson M Hart Windows System Programming Third Edition Addison Wesley

Professional 2004

Hình thức và tiêu chuẩn đánh giá sinh viên:

- Hình thức thi cuối kỳ : Thi vấn đáp

- Sinh viên phải đảm bảo các điều kiện theo Quy chế của Nhà trường và của Bộ

Thang điểm: Thang điểm chữ A, B, C, D, F

Điểm đánh giá học phần: Z = 0,3X + 0,7Y

Trang 5

i

MỤC LỤC

LỜI NÓI ĐẦU 1

CHƯƠNG 1: CÁC KHÁI NIỆM CƠ BẢN 2

1 Giới thiệu về môi trường lập trình Windows 2

1.1 Cơ sở về hệ điều hành Windows 2

1.2 Các phiên bản của hệ điều hành Windows 2

1.3 Vai trò của Windows trên thị trường phần mềm 3

2 Thư viện Win32 và Win64 3

2.1 Win32 API 3

2.2 Win64 API 4

3 Các bước phát triển ứng dụng trên Windows 4

3.1 Chương trình Win32 đơn giản nhất 4

3.2 Chương trình cửa sổ đơn giản 5

3.3 Quản lý các thông điệp 14

3.4 Vòng lặp xử lý thông điệp 17

Bài tập: 20

CHƯƠNG 2: HỆ THỐNG FILE VÀ THƯ MỤC 21

1 Truy cập và sử du ̣ng hê ̣ thống file trên môi trường Windows 21

2 Các ví dụ về thao tác với file 21

2.1 Serialization 21

2.2 Cài đặt một lớp Serializable 24

3 Quản lý file và thư mục nâng cao 40

Bài tập: 40

CHƯƠNG 3: HỆ THỐNG CSDL REGISTRY 41

1 Khái niệm và vai trò của CSDL Registry 41

1.1 Các khóa, các hive 41

1.2 Các kiểu dữ liệu 42

2 Quản lý CSDL Registry 43

2.1 Thay đổi khóa 43

2.2 Thêm mớ i khóa 43

2.3 Liệt kê các khóa 44

3 Can thiệp Windows qua Registry 44

3.1 Thay đổi giao diện 44

3.2 Thay đổi các thiết lâ ̣p đối với các ổ đĩa 44

Trang 6

ii

3.3 Thay đổi các thiết lâ ̣p với người dùng 44

Bài tập: 44

CHƯƠNG 4: QUẢN LÝ CÁC TIẾN TRÌNH VÀ LUỒNG 45

1 Các tiến trình và luồng trên Windows 45

2 Các thao tác với tiến trình 46

2.1 Tạo tiến trình 46

2.2 Kết thúc và thoát khỏi một tiến trình 47

2.3 Các thao tác với biến môi trường của Windows 47

2.4 Ví dụ: Ghi nhật ký thời gian thực hiê ̣n của các tiến trình 47

3 Quản lý luồng (thread) trên Windows 49

3.1 Các khái niệm cơ bản 49

3.2 Mô hình Boss/Worker và các mô hình khác 49

3.3 Bộ nhớ dành cho luồng 49

3.4 Độ ưu tiên và các trạng thái của luồng 50

4 Một số ví du ̣ về tiến trình và luồng 50

4.1 Tìm kiếm song song với các tiến trình 50

4.2 Thuật toán sắp xếp trô ̣n bằng đa luồng 52

Bài tập: 55

CHƯƠNG 5: CÁC DỊCH VỤ CỦA WINDOWS 56

1 Tổng quan về dịch vu ̣ trên Windows 56

2 Các thành phần của một dịch vụ 56

2.1 Hàm main() 56

2.2 Hàm ServiceMain() 56

2.3 Kiểm soát dịch vụ qua các Handler 56

3 Ví du: dịch vụ đơn giản trên Windows 57

4 Quản lý các dịch vụ của Windows 60

4.1 Các phương pháp kiểm soát các dịch vụ của Windows 60

4.2 Ví dụ : Điều khiển các di ̣ch vu ̣ của Windows 60

Bài tập: 64

CHƯƠNG 6: LẬP TRÌNH SOCKET 65

1 Khái niệm sockets trên Windows 65

2 Các hàm sockets phía server 65

3 Các hàm sockets phía client 66

4 Ứng dụng mang đơn giản 66

Trang 7

iii

4.1 Phía server 66

4.2 Phía client 72

5 Windows Sockets 2.0 74

Bài tập: 74

CHƯƠNG 7: THƯ VIỆN LIÊN KẾT ĐỘNG 75

7.1 Khái niệm và ứng dụng của thư viện liên kết động 75

7.2 Hệ thống thư viện liên kết động của Windows 75

7.3 Các bước tạo một thư viện DLL 76

7.4 Chia sẻ bô ̣ nhớ giữa các thư viê ̣n liên kết đô ̣ng 83

7.5 Các vấn đề khác về thư viện liên kết động 84

Bài tập: 85

TÀI LIỆU THAM KHẢO 86

ĐỀ THI THAM KHẢO 87

Trang 8

1

LỜI NÓI ĐẦU

Hệ điều hành Windows của Microsoft là hệ điều hành được cài đặt nhiều nhất trên các máy PC hiện nay Sự phổ biến của Windows và nền tảng phần cứng của Intel dẫn tới sự cần thiết phải có những hiểu biết sâu về chúng, đặc biệt đối với những lập trình viên Mục đích của học phần này là cung cấp cho học viên một cái nhìn tổng quan, từ cơ bản tới chi tiết về các khía cạnh của lập trình trên hệ điều hành Windows, từ các chi tiết trong cấu trúc của một chương trình tới các khái niệm cấp cao về tiến trình, luồng, xử lý song song, thư viện DLL, lập trình Socket, can thiệp vào cơ sở dữ liệu Registry

Tài liệu này dựa trên những kinh nghiệm và nghiên cứu mà tác giả đã đúc rút, thu thập trong quá trình giảng dạy môn học Lập trình C trên Windows, cùng với sự tham khảo của các tài liệu của các đồng nghiệp , các tác giả trong và ngoài nước , từ điển trực tuyến Wikipedia Với bẩy chương được chia thành các chủ đề khác nhau từ các khái niê ̣m cơ bản cho tới các thao tác với hệ thống file, thư mục, hệ thống CSDL Registry, quản lý tiến trình và luồng, lập trình quản lý dịch vụ, lập trình socket, thư viện liên kết động DLL… hy vọng sẽ cung cấp cho các em sinh viên , các bạn độc giả một tài liệu bổ ích Mặc dù đã rất cố gắng song vẫn không tránh khỏi một số thiếu sót , hy vọng sẽ được các bạn bè đồng nghiệp , các em sinh viên , các bạn độc giả góp ý chân thành để tôi có thể hoàn thiện hơn nữa tài liệu này

Xin gửi lời cảm ơn chân thành tới các bạn bè đồng nghiệp và Ban chủ nhiệm khoa Công nghệ Thông tin đã tạo điều kiện giúp đỡ để tài liệu này có thể hoàn thành

Trang 9

2

Chương 1: Các khái niệm cơ bản

Tài liệu này được biên soạn để cung cấp cho người ho ̣c những kiến thức cơ bản vế việc viết các chương trình sử dụng giao diện lập trình API trên môi trường Win32 Ngôn ngữ được sử dụng là ngôn ngữ C, hầu hết các trình biên dịch C++ hiện nay đều có thể dịch được các chương trình mẫu trình bày trong tài liệu này Hầu hết tất cả các thông tin được trình bày trong tài liệu này đều có thể ứng dụng cho bất cứ ngôn ngữ nào có thể truy cập các hàm API, chẳng hạn như Java, Assembly, và Visual Basic

Tài liệu này được biên soạn không phải để dạy các bạn độc giả lập trình bằng ngôn ngữ

C, hoặc dạy chúng ta sử dụng bất cứ một trình biên dịch cụ thể nào (chẳng hạn như Borland C++, Visual C++, …) tuy nhiên trong phần phụ lục tôi sẽ dành một chút để cung cấp cho các bạn một số chú ý về một số trình biên dịch mà tôi đã sử dụng

1 Giới thiệu về môi trường lập trình Windows

1.1 Cơ sở về hệ điều hành Windows

Hệ điều hành Windows là một hệ điều hành dành cho người dùng cuối (End User Operating System) với các tính năng cơ bản sau: đa nhiệm, giao diện đồ họa, plug and play và quan trọng nhất là Windows Interface Based - tức là giao diện các chương trình chạy trên Windows đều có dạng các cửa sổ

1.2 Các phiên bản của hệ điều hành Windows

Do cách dùng tiếng Anh và việc hiểu tiếng Anh dẫn tới việc nhiều người hiểu về các phiên bản của hệ điều hành Windows chưa chính xác Ví dụ có bạn cho rằng Windows XP Professional Edition và Windows XP Home Edition là hai phiên bản khác nhau của hệ điều hành Windows Thực ra như vậy vừa đúng lại vừa không đúng, đúng là Windows XP Professional Edition và Windows XP Home Edition là hai Edition khác nhau của cùng 1 Version Windows XP, có lẽ sai là vì hiểu từ Edition và Version sai Version có nghĩa là một phiên bản, thường đi kèm với các số hiệu của phiên bản (1.0, 1.2 … 5.0) và thường là một thay đổi lớn đối với bản thân phần mềm, ví dụ như đối với Windows thì có 3 thay đổi lớn: thay đổi về kiến trúc nền tảng của hệ điều hành (tức là phần kernel của hệ điều hành), hai là cập nhật các bản vá (patch) cho các lỗi của phiên bản trước đó đối với tất cả các phần của hệ điều hành, ba là các phần mới của hệ điều hành (có thể là các ứng dụng đi kèm hoặc hỗ trợ thêm các công nghệ mới, ví dụ như đối với Windows là chuẩn Wi-Fi, DVD, dot NET framework hay các ứng dụng như Windows Media Player, IE …)

Còn Edition là ấn bản khác nhau của cùng một phiên bản, các Edition thường gắn với các yếu tố về địa lý, ngôn ngữ khác nhau (ví dụ như Compact Edition nghĩa là bản rút gọn, Standard Edition là bản chuẩn, Ultimate Edition là bản có các tính năng cao cấp nhất …) Đối với hệ điều hành Windows các Edition khác nhau thường phân biệt bởi các tính năng của chúng, do nhắm tới việc phục vụ các đối tượng khác nhau nên Microsoft bỏ đi một số tính năng không cần thiết và tăng thêm các tính năng mà đối tượng người dùng hay dùng ví dụ như bản Home Edition nhắm tới người dùng gia đình nên các tính năng đồ họa, video, âm thanh phải tốt, còn bản Professional nhắm tới các người dùng chuyên nghiệp có trình độ cao nên các tính năng hệ thống sẽ cao hơn

Windows có các phiên bản sau đây:

Windows 1.01

Windows 2.03

Trang 10

Windows For Workgroups 3.11

Windows 3.2 (released in Simplified Chinese only)

Windows XP Professional x64 Edition

Windows Fundamentals for Legacy PCs

1.3 Vai trò của Windows trên thị trường phần mềm

Do sự thống trị của hãng Microsoft nói riêng và sự phổ biến gần như độc tôn của hệ điều hành Windows nói chung ở Việt Nam nên Windows đóng vai trò hết sức quan trọng trong việc phát triển phần mềm ở Việt Nam Về bản chất các chương trình đều phải thực hiện trên một nền tảng (platform) nhất định bao gồm các chi tiết từ phần cứng cho tới phần mềm, tuy nhiên đối với đa số ứng dụng, các lập trình viên cần quan tâm nhiều nhất tới hệ điều hành

ra cùng với hệ điều hành Windows NT Phiên bản đầu tiên của Win32 được phát hành cùng với hệ điều hành Windows 95 (gọi là Win32c - compatible), và sau này chỉ còn là Win32

Trang 11

4

2.2 Win64 API

Win64 là phiên bản 64 bit của thư viện Win32, là một biến thể dành cho các nền tảng 64 bit của hệ điều hành Windows (ví dụ hiện tại là AMD64 và IA-64) Cả hai phiên bản 32 và 64 bit của một ứng dụng đều có thể được biên dịch từ một mã chương trình chung, mặc dù có một số khác biệt trong hai phiên bản này Sự khác nhau cơ bản của hai phiên bản là các con trỏ địa chỉ của Win32 là 32 bit, còn với Win64 là 64 bit

3 Các bước phát triển ứng dụng trên Windows

3.1 Chương trình Win32 đơn giản nhất

Nếu bạn là một người mới bắt đầu lập trình trên môi trường Win32 thì thao tác đầu tiên

mà bạn cần phải làm được đó là dịch một chương trình cơ bản trên môi trường Windows Hãy

gõ đoạn chương trình trên vào và nếu mọi thứ suôn sẻ bạn sẽ có một chương trình đơn giản nhất (vô nghĩa) trong số các chương trình mà bạn đã làm

Hãy nhớ là dịch chương trình này theo kiểu dịch một chương trình viết bằng ngôn ngữ

C, không phải ngôn ngữ C++ Trong hầu hết các trường hợp điều này đơn giản thực hiện bằng cách đổi phần tên mở rộng từ cpp thành c Chẳng hạn bạn có thể đặt tên cho file chương trình sau là test.c và bắt đầu làm việc

mà bạn đang sử dụng Hãy chắc chắn là bạn có đầy đủ các file mà trình biên dịch yêu cầu Thật không may là tôi không thể giúp nhiều trong trường hợp này vì các lỗi phụ thuộc vào trình biên dịch và người sử dụng (trong việc gây lỗi cũng như sửa lỗi)

Bạn có thể nhận được một vài cảnh báo từ trình biên dịch về việc chương trình không sử dụng các tham số được cung cấp cùng với hàm WinMain() Nhưng không sao điều quan trọng là bạn dịch chương trình thành công và có một chương trình Win32 thực sự Chúng ta

sẽ phân tích các đoạn mã chương trình kỹ càng hơn

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

WinMain() là hàm chính của một chương trình trên môi trường Windows giống như hàm main trên môi trường DOS và UNIX Đó là nơi các đoạn mã chương trình sẽ được thực thi Các tham số của hàm main gồm có:

HINSTANCE hInstance

Trang 12

Tham số hInstance được dùng cho các công việc đại loại như nạp (load) các tài nguyên

và bất cứ tác vụ nào khác được thực hiện theo kiểu từng module cơ bản Một module là một file EXE hoặc file DLL được nạp vào chương trình của bạn Đối với hầu hết (không phải tất cả) các chương trình được trình bày trong tài liệu này, chỉ có một module mà chúng ta cần quan tâm đó là file chương trình (EXE)

Các qui ước gọi hàm

WINAPI chỉ định qui ước gọi hàm và được định nghĩa là _stdcall Nếu như bạn không hiểu ý nghĩa của chúng thì cũng không cần bận tâm nhiều vì chúng không ảnh hưởng tới tài liệu này chỉ cần nhớ là đây là một điều cần thiết (kiểu của hàm WinMain() là int, nhưng kiểu gọi hàm là WINAPI)

Các kiểu dữ liệu Win32

Các bạn sẽ thấy rằng rất nhiều từ khóa thông thường hoặc các kiểu dữ liệu đều có các cách định nghĩa cụ thể trên môi trường Windows, UINT là unsigned int, LPSTR là char *, vân vân Việc chọn lựa sử dụng cách nào thực sự phụ thuộc vào bạn Nếu bạn thích sử dụng char

* hơn là LPSTR thì hãy sử dụng cách đó nhưng cần phải kiểm soát được việc sử dụng của mình (cần nắm rõ các kiểu dữ liệu trước khi thay chúng bằng một kiểu khác)

Bạn chỉ cần nắm vững một số nguyên tắc và sẽ hiểu ý nghĩa của các tên này: LP là long pointer Trên môi trường Win32 phần long này không quan trọng và hoàn toàn có thể bỏ qua Còn nếu bạn không hiểu con trỏ là gì thì có lẽ bạn nên học lại về C hoặc bỏ qua chúng và tiếp tục

Tiếp đến là LPCSTR: long pointer const là một hằng xâu, tức là một xâu không thể thay đối, ngoài ra giữa hai kiểu LPSTR và LPCSTR còn có thể có thêm một chữ T nhưng nếu bạn không có ý định sử dụng các font Unicode trong chương trình thì nó cũng chẳng có ý nghĩa gì đặc biệt

3.2 Chương tri ̀nh cửa sổ đơn giản

Ví dụ: chương trình gồm 1 cửa sổ đơn giản trên Windows

Đôi khi mo ̣i người hỏi nhau trong khi ho ̣ chát với nhau bằng phần mềm IRC là “Làm thế nào để ta ̣o ra mô ̣t cửa sổ ?” Tôi e rằng điều này hoàn toàn không phải là mô ̣t điều đơn giản Không khó khi ba ̣n biết mình đang làm gì nhưng có khá ít những điều ba ̣n cần biết để tạo ra một cửa sổ , và những điều đó hoàn toàn có thể giải thích bằng một đoạn văn bản ngắn hoă ̣c khoảng mô ̣t vài phút chat bằng IRC

Trang 13

6

Tôi luôn muốn làm trước và ho ̣c kỹ càng sa u vì thế ở đây tôi sẽ trình bày đoa ̣n mã chương trình ta ̣o ra mô ̣t cửa sổ trước và giải thích sau:

#include <windows.h>

const char g_szClassName[] = "myWindowClass"; /* tên lớp cửa sổ */

// Step 4: the Window Procedure

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

// để windows xử lý các thông điệp còn lại

return DefWindowProc(hwnd, msg, wParam, lParam);

}

return 0;

}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPSTR lpCmdLine, int nCmdShow)

Trang 14

7

wc.style = 0;

wc.lpfnWndProc = WndProc; /* hàm xử lý thông điệp cửa sổ */

wc.cbClsExtra = 0;/* không cần thông tin thêm cho cửa sổ */

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); /* màu nền */

/*wc.hbrBackground = GetStockObject(WHITE_BRUSH); màu nền trắng */

wc.lpszMenuName = NULL; /* không có hệ thống thực đơn */

wc.lpszClassName = g_szClassName; /* tên lớp cửa sổ */

wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

// Step 2: Creating the Window

// Tạo ra một thể nghiệm của lớp cửa sổ cho ứng dụng

Trang 15

// Step 3: The Message Loop

// Step 3: Tạo vòng lặp xử lý thông điệp

Hình 1 – Cửa sổ của chương trình khi chạy

Đây có lẽ là mô ̣t chương trình windows đơn giản nhất mà ba ̣n có thể viết và chức năng của chương trình đơn giản là tạo ra một cửa sổ cũng rất đơn giản Bạn nên gõ chương trình và biên di ̣ch để cha ̣y thử và hãy đảm bảo là không có lỗi gì xảy ra

Bước 1: Đăng ký (Registering) lớp cửa sổ (Window)

Mô ̣t lớp cửa sổ (Window Class) chứa các thông tin về kiểu cửa sổ , bao gồm thủ tu ̣c Window của nó (thủ tục này kiểm soát cửa sổ khi nó được ta ̣o ra và đáp ứng la ̣i các sự kiê ̣n người dùng tác đô ̣ng lên cửa sổ), các icon lớn và nhỏ của cửa sổ, và màu nền của cửa sổ Theo cách này bạn có thể đăng ký một lớp và sau đó tạo ra bao nhiêu cửa sổ tùy thích mà không cần chỉ rõ tất cả các thuô ̣c tính đó thêm mô ̣t lần nào nữa Hầu hết các thuô ̣c tính mà ba ̣n thiết

lâ ̣p trong lớp cửa sổ (window) có thể thay đổi được theo một cách rất cơ bản (thổ mô ̣c) nếu như ba ̣n thích Và cần chú ý là lớp cửa sổ này không liên quan gì tới khái niệm các lớp trong C++

const char g_szClassName[] = "myWindowClass";

Biến trên lưu tên của lớp cửa sổ của chúng ta , chúng ta sẽ sử dụng nó để đăng ký lớp cửa sổ của chúng ta với hê ̣ thống

Trang 16

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

Các thành viên của cấu trúc ảnh hưởng đến lớp cửa sổ gồm có:

cbSize: kích thước của cấu trúc

style: Class Style (CS_*), để không nhầm lẫn với Window Style (WS_*) Thông thườ ng thuô ̣c tính này được gán bằng 0

lpfnWndProc: con trỏ trỏ tới thủ tu ̣c quản lý cửa sổ cho lớp cửa sổ này

cbClsExtra: Lượng dữ liê ̣u thêm được cấp phát trong bô ̣ nhớ cho mỗi cửa sổ thuô ̣c loa ̣i này Thông thường cũng được gán bằng 0

hInstance: Quản lý một thể nghiệm (instance) của ứng dụng (tham số đầu tiên củ a hàm WinMain())

hIcon: Icon lớ n (thường là 32x32) hiển thị khi chúng ta nhấn tổ hợp phím Alt+TAB

Trang 17

10

hCursor: Con trỏ sẽ xuất hiê ̣n khi con trỏ chuô ̣t di chuyển qua vùng cửa sổ của chương trình

hbrBackground: Tham số sử du ̣ng để thiết lâ ̣p mầu nền của cửa sổ

lpszMenuName: tên củ a tài nguyên menu được sử du ̣ng cho các cửa sổ của lớp

lpszClassName: Tên để định danh lớp cửa sổ

hIconSm: Icon nhỏ (16x16) hiển thi ̣ trên thanh task bar và góc trên bên trái của cửa sổ Bạn không nên lo lắng nếu chưa hiểu về các thuộc tính này, chúng sẽ được giải thích ở các phần tiếp theo của tài liệu này Mô ̣t điều khác cần phải nhớ là không nên cố nhớ những thuô ̣c tính này Tôi rất ít khi (không muốn nói là không bao giờ ) nhớ các thuô ̣c tính cũng như các tham số của các hàm , điều đó hoàn toàn là mô ̣t cố gắng lãng phí thời gian và công sức Nếu như ba ̣n biết các hàm ba ̣n cần sử du ̣ng trong mô ̣t chương trình nào đó , hãy tra trong các tài liệu help Nếu ba ̣n không có cá c file help hãy down chúng về , các file kiểu này trên mạng không hề thiếu Cuối cùng thì ba ̣n sẽ biết các tham số của các hàm mà ba ̣n thường xuyên sử dụng

Tiếp đến chúng ta go ̣i tới hàm RegisterClassEx () và kiểm tra xem hàm này có thành công hay không , nếu như hàm trả về sai chúng ta sẽ hiển thi ̣ mô ̣t thông báo thất ba ̣i và dừng chương trình

Bước 2: Tạo ra cửa sổ chương trình

Khi đã đăng ký lớp cửa sổ , chúng ta có thể tạo ra các cửa sổ trong chương trình Các bạn nên tra các tham số của hàm CreateWindowEx () (bạn luôn nên làm vậy khi làm việc với các hàm API mới), nhưng tôi sẽ giải thích mô ̣t chút ở đây:

NULL, NULL, hInstance, NULL);

Tham số đầu tiên (WS_EX_CLIENTEDGE) là kiểu cửa sổ mở rộng (extended), trong trường hợp này tham số này được sử dụng để tạo thành một đường viền chìm bên trong xung quanh cửa sổ Có thể đặt bằng 0 nếu chúng ta muốn khác Cũng nên thay đổi các giá trị khác để xem chúng làm việc như thế nào

Tiếp theo chúng ta cần có tên lớp (g_szClassName), tham số này báo cho hê ̣ thống biết loại cửa sổ mà chúng ta muốn tạo ra Vì chúng ta muốn tạo ra một cửa sổ từ lớp mà chúng ta vừa đăng ký nên chúng ta sử du ̣ng tên của lớp cửa sổ đó Sau đó chúng ta chỉ rõ tên của cửa sổ hoă ̣c tiêu đề (xâu ký tự) sẽ xuất hiện trong vai trò là tham số Caption hoặc Title Bar của cửa sổ của chúng ta

Trang 18

11

Tham số chúng ta sử du ̣ng WS_OVERLAPPEDWINDOW trong vai trò tham số Window Style Có khá ít các tham số kiểu này do đó bạn nên kiểm tra và thay đổi giá trị của chúng để xem kết quả họat động như thế nào Chúng ta sẽ bàn thêm về các tham số này sau Bốn tham số tiếp theo (CW_USEDEFAULT, CW_USEDEFAULT, 320, 240) là tọa độ

X và Y của góc trên bên trái của cửa sổ và các kích thước dài rô ̣ng của nó Tôi đã thiết lâ ̣p giá trị của X và Y là CW _USEDEFAULT để windows tự cho ̣n vi ̣ trí để đă ̣t cửa sổ chương trình khi cha ̣y Hãy nhớ l à bên trái của màn hình là một giá trị 0 của X và nó tăng dần khi sang phải Đỉnh của màn hình tương ứng với giá tri ̣ 0 của Y tăng dần theo chiều đi xuống Các đơn vị là các điểm ảnh , là đơn vị nhỏ nhất mà màn hình có thể hiển thi ̣ được ta ̣i đô ̣ phân giải hiê ̣n tại

Tiếp theo (NULL, NULL, g_hInst, NULL) chúng ta có một handle quản lý cửa sổ cha của cửa sổ được sinh ra , mô ̣t handle quản lý menu , mô ̣t handle cho thể nghiê ̣m của ứng du ̣ng

và một con trỏ trỏ tới dữ liê ̣u cửa sổ Trong windows các cửa sổ trên màn hình được sắp xếp theo mô ̣t cấu trúc phân cấp theo kiểu các cửa sổ cha và con Khi chúng ta nhìn thấy mô ̣t nút trên mô ̣t cửa sổ , nút đó là con và nó được chứ a trong cửa sổ là cha của nó Trong ví du ̣ này của chúng ta handle của cửa sổ cha là NULL vì chúng ta không có cửa sổ cha nào cả , đây là cửa sổ chính hoă ̣c cửa sổ ở cấp cao nhất của chúng ta Menu cũng là NULL vì chúng t a chưa có menu chương trình nào Handle dành cho thể nghiê ̣m của chương trình được gán là giá tri ̣ đươ ̣c truyền cho tham số đầu tiên của hàm WinMain () Dữ liệu cửa sổ (thường là chúng ta chẳng bao giờ sử du ̣ng chúng ) có thể đượ c sử du ̣ng để gửi các dữ liê ̣u khác tới cửa sổ đang đươ ̣c ta ̣o ra và cũng là NULL

Nếu ba ̣n đang băn khoăn về giá tri ̣ NULL bí hiểm này thì hãy yên tâm vì nó đơn giản chỉ là 0 (zero) Thực sự trong C nó được đi ̣nh nghĩa là ((void*)0) vì nó được sử dụng với các con trỏ Vì thế cho nên bạn có thể sẽ gặp các cảnh báo khi biên dịch chương trình nếu sử dụng NULL cho các giá tri ̣ kiểu nguyên , tùy thuộc vào trình biên dịch và cấp độ cảnh báo được thiết lâ ̣p với trình biên di ̣ch Bạn có thể bỏ qua các cảnh báo hoặc đơn giản là sử dụng giá trị 0 để thay thế

Nguyên nhân số 1 mà mọi người không biết tại sao chương trình của mình lại có lỗi là

do ho ̣ không kiểm tra cá c giá tri ̣ trả về của lời go ̣i hàm mà ho ̣ thực hiê ̣n trong chương trình của mình CreateWindow() sẽ trả về sai trong một số trường hợp ngay cả khi bạn là một lập trình viên có nhiều kinh nghiệm , chỉ đơn giản bởi vì có rất n hiều lỗi mà chúng ta rất dễ ta ̣o ra chúng Cho tới khi ba ̣n ho ̣c được cánh nhanh chóng xác đi ̣nh các lỗi kiểu như vâ ̣y , hãy tự cho mình một cơ hội để có thể tìm ra các lỗi trong chương trình , hãy luôn kiểm tra các giá trị tr ả về khi go ̣i tới các hàm

Sau khi chúng ta đã ta ̣o ra cửa sổ và kiểm tra để đảm bảo là chúng ta có một handle hợp

lê ̣, chúng ta sẽ hiển thị cửa sổ lên màn hình của windows (hê ̣ điều hành ) bằng cách sử du ̣ng

Trang 19

12

tham số cuối cùng của hàm WinMain () và sau đó cập nhật nó để đảm bảo là tự nó được vẽ lại

mô ̣t cách hợp lê ̣ trên màn hình

… Ba ̣n sẽ thấy các tùy chọn này trong các tham số của các shortcut của tới các cửa sổ

Bước 3: Vòng lặp xử lý thông điệp

Đây chính là trái tim của toàn bô ̣ chương trình , hầu hết những thứ mà chương trình của chúng ta xử lý là nằm ở đây

TranslateMessage() thực hiê ̣n thêm mô ̣t số xử lý trên các sự kiê ̣n bàn phím chẳng ha ̣n như sinh ra các thông điê ̣p WM _CHAR cùng với các thông điê ̣p WM _KEYDOWN Cuối cùng DispatchMessage() gửi thông điê ̣p tới cửa sổ mà thông điê ̣p cần được gửi tới (xử lý theo kiểu hướng sự kiê ̣n ) Đó có thể là cửa sổ chính của chương trình hoă ̣c có th ể là một cửa sổ khác, hoă ̣c là mô ̣t điều khiển , và trong một số trường hợp là một cửa sổ được tạo ra ở hậu trường bởi hê ̣ thống hoă ̣c mô ̣t chương trình khác Đây không phải là điều mà các ba ̣n cần phải

lo lắng vì tất cả những gì chúng ta bàn tới là lấy thông điê ̣p và gửi nó đi , hê ̣ thống sẽ xử lý để đảm bảo thông điê ̣p đó được gửi đến đúng nơi sẽ nhâ ̣n nó

Bước 4: Thủ tục cửa sổ

Nếu như vòng lă ̣p thông điê ̣p là trái tim của chương trình thì thủ tục cửa sổ là bộ não của chương trình Đây là nơi mà tất cả các thông điê ̣p được gửi tới cửa sổ của chúng ta được xử lý

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

Trang 20

vì chúng ta sử dụng handle nhận được để quản lý cửa sổ qua tham số thứ nhất nên các cửa sổ khác sẽ không bị ảnh hưởng, chỉ có cửa sổ nhận thông điệp là sẽ bị hủy

WM_CLOSE được gửi đến khi người dùng nhấn chu ột vào nút đóng cửa sổ hoặc nhấn tổ hơ ̣p phím Alt +F4 Thao tác này sẽ làm cho cửa sổ bi ̣ hủy theo mă ̣c đi ̣nh , nhưng tôi muốn xử lý nó mô ̣t cách dứt khoát , vì điều này có thể liên quan tới một số thao tác khác chẳng hạn như đóng các file dữ liê ̣u đang mở , ngắt các kết nối cơ sở dữ liê ̣u , các kết nối mạng vân vân trước khi thoát khỏi chương trình

Khi chúng ta go ̣i tới hàm DestroyWindow () hệ thống sẽ gửi thông điê ̣p

WM_DESTROY tớ i cửa sổ sẽ bi ̣ hủy bỏ, trong trường hơ ̣p này là cửa sổ của chúng ta , và sau đó hủy tất cả các cửa sổ con trước khi loa ̣i bỏ cửa sổ của chúng ta khỏi hê ̣ thống Vì chỉ có

mô ̣t cửa sổ trong chương trình của chúng ta nên cũng không có nhiề u viê ̣c phải làm và chúng

ta muốn thoát khỏi chương trình, vì thế hàm PostQuitMessage() được go ̣i tới Hàm này sẽ gửi thông điê ̣p WM _QUIT tới vòng lă ̣p thông điê ̣p Chúng ta không bao nhận được thông điệp này vì nó làm cho hàm GetMessage() trả về FALSE , và như bạn sẽ thấy trong đoạn mã vòng

lă ̣p xử lý thông điê ̣p của chúng ta , khi điều đó xảy ra chúng ta dừng viê ̣c xử lý các thông điê ̣p

và trả về giá trị cuối cùng , tham số wParam của thông điê ̣p WM _QUIT là giá tri ̣ được truyền qua hàm PostQuitMessage () Giá trị trả về chỉ thực sự có ích nếu chương trình của chúng ta đươ ̣c thiết kế để mô ̣t chương trình khác go ̣i và chúng ta muốn trả về mô ̣t giá tri ̣ cu ̣ thể

Bước 5: không có bước 5

Đến đây là hết , chúng ta không có bước 5 và tôi hy vọng các bạn đã hiểu được ít nhiều cấu trúc cơ bản của mô ̣t chương trình trên Windows

Trang 21

Trong phần tiếp theo tô i sẽ hướng dẫn các ba ̣n sửa đổi những gì chúng ta đã viết để ta ̣o

ra mô ̣t cái gì đó mới mẻ

Trước tiên cần đảm bảo là ba ̣n đã biên di ̣ch và cha ̣y chương trình simple _window thành công, chúng ta sẽ copy nó sang ví dụ mới và sử a đổi mã nguồn (code) của chương trình Chúng ta sẽ thêm cho chương trình khả năng hiển thị tên của chương trình khi người dùng kích chuột vào cửa sổ của chương trình Thực chất của khả năng mới này là chúng ta sẽ tự xử lý thông điệp nhấn chuột của người dùng, tất nhiên là trong hàm WndProc(), từ:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

switch(msg)

{

case WM_LBUTTONDOWN: // <-

Trang 22

Trước tiên tôi sẽ trình bày đoa ̣n mã lê ̣nh mà chúng ta sẽ thêm vào (hiển thị tên của chương trình của chúng ta ) và sau đó tôi sẽ tích hợp đoạn mã đó vào chương trình của chúng

ta Trong các phần sau của chương trình t ôi sẽ chỉ cho các ba ̣n đoa ̣n mã và để các ba ̣n tự tích

hơ ̣p đoa ̣n mã đó vào các chương trình Điều này vừa tốt cho tôi : tôi sẽ không phải gõ đi gõ la ̣i các đoạn mã lệnh và vừa tốt cho các bạn : các bạn có cơ hội thực hàn h những hiểu biết của mình để nâng cao kỹ năng thực hành Còn nếu như bạn không chắc chắn hãy tra trong mã nguồn chương trình đi kèm với tài liê ̣u này

GetModuleFileName(hInstance, szFileName, MAX_PATH);

MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);

Hàm WndProc() của chúng ta bây giờ sẽ như sau:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

Trang 23

16

GetModuleFileName(hInstance, szFileName, MAX_PATH);

MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);

Chúng ta có thể chú ý là ở đây tôi đã sử dụng hai biến khi gọi hàm

GetModuleFileName(), tham số thứ nhất là mô ̣t handle tham chiếu tới mô ̣ t chương trình đang chạy, nhưng chúng ta có thể lấy handle đó ở đâu ra ? Ở đây một lân nữa chúng ta lại sử dụng

mô ̣t hàm API khác GetModuleHandle(), thâ ̣t may mắn là đối với hàm này nếu chúng ta truyền tham số là NULL vào thì kết quả trả về sẽ là handle trỏ tới file được sử du ̣ng để ta ̣o ra tiến trình đã gọi hàm, đó chính xác là cái mà chúng ta cần Và do đó chúng ta có câu lệnh:

HINSTANCE hInstance = GetModuleHandle(NULL);

Tham số thứ hai khi go ̣i hàm GetModuleFileName() là một con trỏ xâu để chứa đường dẫn (kết quả của hàm ) tới chương trình có handle là tham số thứ nhất , kiểu củ a nó là LPTRSTR (hoặc LPSTR) và do LPSTR hoàn toàn tương tự như char * nên chúng ta khao báo

mô ̣t xâu như sau:

char szFileName[MAX_PATH];

MAX_PATH là mô ̣t macro thuô ̣c windows h đi ̣nh nghĩa đô ̣ dài tối đa của mô ̣t xâu để chứa đô ̣ dài đường dẫn tới mô ̣t file trên windows

Trang 24

17

3.4 Vòng lặp xử lý thông điệp

Những kiến thức về vòng lặp xử lý thông điệp và cấu trúc của việc gửi một thông điệp

là cần thiết để viết bất cứ ứng dụng nào Cho đến thời điểm hiê ̣n ta ̣i chúng ta mới xem xét mô ̣t chút về quá trình xử lý thông điệp , trong phần này tôi sẽ trình bày với các bạn kỹ càng hơn về

cả quá trình xử lý thông điệp

Thế nào là mô ̣t thông điê ̣p?

Mô ̣t thông điê ̣p (message) là một giá trị nguyên (số) Nếu ba ̣n tra trong các file header (đây là mô ̣t thói quen tốt khi làm việc với các hàm API) bạn sẽ thấy các dòng khai báo sau:

Mỗi mô ̣t thông điê ̣p có thể có nhiều nhất là hai tham số , wParam và lParam Nguyên bản wParam là 16 bit và lParam là 32 bit, nhưng trên các hê ̣ thống Win32 chúng đều là 32 bit Không phải tất các thông điê ̣p đều sử du ̣ng hai tham số này , và mỗi thông điê ̣p sử du ̣ng chúng theo các cách khác nhau Chẳng ha ̣n thông điê ̣p WM _CLOSE không sử du ̣ng cả hai tham số trên, và bạn có thể bỏ qua chúng Thông điệp WM_COMMAND sử du ̣ng cả hai tham số trên , wParam chứa hai giá tri ̣ , HIWORD(wParam) là thông điệp báo hiệu (nếu thích hợp ) và LOWORD(wParam) là định danh điều khiển hoặc menu gửi thông điệp lParam là HWND (window handle ) của điều khiển gửi thông điệp hoặc NULL nếu như các thông điệp không phải được gửi đi từ mô ̣t điều khiển nào đó

HIWORD() và LOWORD là các macro được định nghĩa bởi windows lấy ra 2 byte cao (High Word) của một giá trị 4 byte = 32 bit (0xFFFF0000) và hai byte thấp (0x0000FFFF) tương ứng Trên các hê ̣ thống Win 32 mô ̣t WORD là mô ̣t giá tri ̣ 16 bit còn DWORD (Double WORD) là một giá trị 32 bit

Để gửi mô ̣t thông điê ̣p ba ̣n có thể sử du ̣ng hàm PostMessage () hoặc SendMessage () PostMessage() đặt (put) thông điê ̣p vào hàng đợi thông điê ̣p và trả về ngay lâ ̣p tức Điều đó có nghĩa là mỗi khi chúng ta gọi tới hàm PostMessage () nó sẽ gửi thông điệp đi ngay nhưng việc thông điê ̣p đó có thực hiê ̣n ngay hay không hoă ̣c thâ ̣m chí có thực hiê ̣n hay không thì còn chưa chắc chắn Hàm SendMessage() gửi thông điê ̣p trực tiếp tới cho cửa sổ nhâ ̣n thông điê ̣p

và sẽ không kết thúc cho tới khi thông điệp đó được xử lý xong Nếu chúng ta muốn đóng mô ̣t cửa sổ chúng ta có thể gửi tới cửa sổ đó mô ̣t thông điê ̣p WM_CLOSE chẳng hạn như PostMessage(hwnd, WM_CLOSE, 0, 0); điều này có tác đô ̣ng tương tự như viê ̣c chúng ta nhấn chuô ̣t lên biểu tượng trên góc trên bên phải của cửa sổ Chú ý rằng wParam và

Trang 25

18

lParam đều bằng 0 trong trường hợp này Điều này là do như chúng ta đã nói trước chúng không có ý nghĩa gì đối với thông điê ̣p WM_CLOSE

Các hộp thoại (Dialog)

Khi ba ̣n đã bắt đầu sử du ̣ng các hô ̣p thoa ̣i , bạn sẽ cần gửi các thông điệp tới các điều khiển để có thể truyền thông với chúng Bạn có thể làm điều này bằng cách trước hết lấy handle quản lý điều khiển bằng hàm GetDlgItem () và sau đó sử dụng hàm SendMessage () hoă ̣c có thể sử du ̣ng hàm SendDlgItemMessage () (hàm này kết h ợp công việc của cả hai hàm trên) Bạn sẽ cung cấp cho hàm một handle của một cửa sổ và một ID con và hàm sẽ lấy handle con của hô ̣p thoa ̣i và gửi thông điê ̣p tới cho nó SendDlgItemMessage() và một vài hàm API tương tự khác chẳng ha ̣n như GetDlgItemText () sẽ làm việc trên tất cả các cửa sổ chứ không chỉ với các hô ̣p thoa ̣i

Thế nào là hàng đợi thông điê ̣p?

Giả sử bạn đang bận túi bụi với việc xử lý thông điệp WM _PAINT và đô ̣t nhiên người dùng thực hiê ̣n hàng loa ̣t thao tác trên bàn phím của máy tính Điều gì sẽ xảy ra ? Bạn sẽ bị ngăt viê ̣c đang vẽ để xử lý các thao tác bàn phím của người dùng hoă ̣c các thao tác đó sẽ bi ̣ bỏ qua? Tất cả các cách giải quyết như vâ ̣y đều có vấn đề , giải pháp ở đây là sử dụng một hàng

đơ ̣i để lưu các thông điêp cần xử lý , khi các thông điê ̣p được gửi đến chúng sẽ được thêm vào hàng đợi và khi các thông điệp được xử lý chúng sẽ được loại bỏ khỏ i hàng đợi Và để đảm bảo các thông điệp không bị bỏ qua nếu như bạn đang bận xử lý một thông điệp nào đó , các thông điê ̣p khác sẽ được chờ trong hàng đợi cho tới khi tới lượt chúng được xử lý

Thế nào là mô ̣t vòng lă ̣p thông điê ̣p (vòng lặp xử lý thông điệp – Message Loop)

sẽ dừng và đợi cho tới khi có một thông điệp mới (trạng thái Block)

2 Khi mô ̣t sự kiê ̣n xảy ra làm cho mô ̣t thông điê ̣p được thêm vào hàng đợi

(chẳng hạn như hê ̣ thống ghi nhâ ̣n mô ̣t sự kiê ̣n nhấn chuô ̣t ) hàm GetMessage() sẽ trả về

mô ̣t giá tri ̣ nguyên dương chỉ ra rằng có mô ̣t thông điê ̣p cần xử lý , và các thông tin về thông điê ̣p đó sẽ được điền vào cấu trúc MSG truyền cho hàm Hàm sẽ trả về 0 nếu như thông điê ̣p là WM_QUIT và là mô ̣t giá tri ̣ âm nếu như có lỗi xảy ra

3 Chúng ta nhận được thông điệp (qua biến Msg ) và truyền cho hàm TranslateMessage(), hàm này thực hiện xử lý thêm một chút , dịch các thông tin của thông điê ̣p thành các thông điê ̣p ký tự Bước này thực sự không bắt buô ̣c nhưng mô ̣t số thông điê ̣p sẽ không làm viê ̣c được nếu không có bước này

4 Sau đó chúng ta chuyển thông điê ̣p cho hàm DispatchMessage () Hàm này sẽ nhâ ̣n thông điê ̣p kiểm tra cửa sổ đích của thông điê ̣p và tìm hàm xử lý thông điê ̣p

Trang 26

19

(Window Procedure) của cửa sổ đó Sau đó nó sẽ go ̣i tới hàm xử lý thông điê ̣p của cửa sổ , gửi các tham số: handle của cửa sổ, thông điê ̣p và các tham số wParam, lParam

5 Trong hàm xử lý thông điê ̣p ba ̣n sẽ kiểm tra thông điê ̣p và các tham số của nó

và làm bất cứ điều gì mà bạn thích với chúng Nếu ba ̣n không muốn xử lý các thông điê ̣p

mô ̣t cách chi tiết cụ thể, bạn hầu như chỉ việc gọi tới hàm DefWindowProc (), hàm này sẽ thực hiê ̣n các hành đô ̣ng mă ̣c đi ̣nh cho ba ̣n (điều này thường có nghĩa là chẳng làm gì cả)

6 Sau khi ba ̣n đã kết thúc viê ̣c xử lý thông điê ̣p, hàm xử lý thông điê ̣p của ba ̣n trả về, hàm DispatchMessage() trả về và chúng ta qua trở lại đầu vòng lặp

Đây là mô ̣t khái niê ̣m cực kỳ quan tro ̣ng của các chương trình trên Windows Thủ tục xử lý thông điê ̣p của ba ̣n không được go ̣i mô ̣t c ách thần bí bởi hệ thống , mà chính bạn đã gọi tới chúng mô ̣t cách gián tiếp thông qua viê ̣c go ̣i tới hàm DitpatchMessage () Nếu như bạn muốn, bạn có thể sử dụng hàm GetWindowLong () trên handle của cửa sổ đích của thông điê ̣p để tìm ra thủ tục xử lý cửa sổ của nó và gọi tới hàm đó một cách trực tiếp:

Chú ý là chúng ta sử dụng hàm Get WindowLong() để lấy thủ tục xử lý cửa sổ của cửa sổ Tại sao chúng ta không đơn giản là gọi tới hàm WndProc () mô ̣t cách trực tiếp ? Vòng lặp các thông điệp của chúng ta chịu trách nhiệm đáp ứng cho tất cả các cửa sổ tron g chương trình của chúng ta , điều này bao gồm cả các thứ chẳng ha ̣n như các nút (button) và các hộp danh sách với các hàm xử lý thông điê ̣p của chúng , vì thế chúng ta cần đảm bảo là chúng ta gọi đến đúng hàm xử lý cửa sổ của các thành phần đó (đây thực sự là mô ̣t ví du ̣ của khái niê ̣m

đa thể trong lâ ̣p trình hướng đối tượng ) Vì các cửa sổ có thể sử dụng chung một hàm xử lý thông điê ̣p nên tham số đầu tiên (handle của cửa sổ ) được dùng để chỉ cho hàm xử lý thông điê ̣p biết cửa sổ nào là dành cho thông điê ̣p nào

Như ba ̣n có thể thấy ứng du ̣ng của ba ̣n dành phần lớn thời gian của nó cho vòng lă ̣p xử

lý thông điệp , nơi mà ba ̣n có thể hân hoan gửi các thông điê ̣p tới các cửa sổ sẽ xử lý chúng Nhưng ba ̣n cần làm gì khi muốn thoát khỏi chương trình ? Vì chúng ta sử dụng một vòng lặp while(), nếu như hàm GetMessage () trả về FALSE , vòng lặp sẽ thoát và chúng ta sẽ tới cuối hàm WinMain() và thoát khỏi chương trình Đó chính xác là những gì mà hàm

PostQuitMessage() đã làm Nó đặt một thông điệp WM _QUIT vào hàng đợi thônmg điê ̣p và thay vì trả về mô ̣t giá tri ̣ dương, hàm GetMesage() điền đầy đủ các thông tin cho cấu trúc Msg

và trả về 0 Tại thời điểm này thành viên wParam của cấu trúc Msg chứa giá trị mà bạn đã truyền cho hàm PostQuitMessage () và bạn cũng có thể bỏ qua nó , hoặc trả về qua hàm WinMain(), giá trị đó sẽ được dùng như là mã thoát chương trình khi tiến trình kết thúc

Trang 27

20

Chú ý: GetMessage() sẽ trả về -1 nếu như gă ̣p mô ̣t lỗi Bạn cần nhớ kỹ điều này nếu không có thể chương trình sẽ gă ̣p tru ̣c tră ̣c, mă ̣c dù hàm GetMessage() có kiểu BOOL song nó vẫn có thể trả về mô ̣t giá tri ̣ không phải là TRUE hay FALSE vì BOOL có nghĩa là UINT (unsigned int) Các ví dụ sau có vẻ sẽ làm việc tốt:

while(GetMessage(&Msg, NULL, 0, 0))

while(GetMessage(&Msg, NULL, 0, 0) != 0)

while(GetMessage(&Msg, NULL, 0, 0) == TRUE)

Thực tế tất cả các trường hợp đó đều sai Bạn có thể chú ý là tôi vẫn sử dụng trường hợp

1 cho tất cả các ví du ̣ cho tới thời điểm này , và như tôi đã đề cập nó vẫn làm việc tốt khi mà hàm GetMessage () không bao giờ trả về FALSE Tuy nhiên không thể đảm bảo là hàm GetMessage() không gặp lỗi trong tất cả các trường hợp và ba ̣n nên sử du ̣ng cách viết như sau:

while(GetMessage(&Msg, NULL, 0, 0) > 0)

Bài tập:

Bài tập 1: Viết chương trình mô phỏng chương trình máy tính với các chức năng tính toán

đơn giản trên Windows

Bài tập 2: Viết chương trình với hệ thống menu cho phép thay đổi màu nền

Trang 28

21

Chương 2: Hê ̣ thống file và thư mục

1 Truy câ ̣p và sử du ̣ng hê ̣ thống file trên môi trường Windows

Hầu hết các ứng du ̣ng ngày nay đều cho phép người dùng lựa cho ̣n ghi la ̣i những gì chương trình đã ta ̣o ra Những thứ cần ghi la ̣i có thể là các tài liê ̣u , dữ liê ̣u chương trình hoă ̣c

mô ̣t thiết lâ ̣p nào đó đối với chương trình Trong bài thực hành này chúng ta sẽ khám phá các hỗ trơ ̣ của Visual C ++ cho viê ̣c cài đă ̣t các chức năng này mô ̣t cách dễ dàng Cụ thể chúng ta

sẽ học:

Cách Visual C++ sử dung các luồng C++ để ghi lại dữ liệu trong chương trình

 Cách thức sử dụng file dạng nhị phân

 Cách làm cho các đối tượng chương trình có khả năng serialize

 Cách thức lưu các biến có kiểu khác nhau vào cùng một file

2 Các ví dụ về thao tác với file

2.1 Serialization

Có hai phần của serialization Khi dữ liê ̣u ứng du ̣ng được lưu trên đĩa hê ̣ thống dưới dạng một file thì đó gọi là serialization Khi tra ̣ng thái của ứng du ̣ng được phu ̣c hồi từ file đó đươ ̣c go ̣i là deserialization Sự kết hợp của hai phần này làm nên cái go ̣i là serialization của các đối tương ứng dụng trong Visual C++

2.1.1 Các lớp CArchive và CFile

Serialization trong các ứng du ̣ng của Visual C ++ được thực hiê ̣n qua lớp CArchive Lớp CArchive đươ ̣c thiết kế để làm viê ̣c với các luồng vào ra cho mô ̣t đối tượng CFile như minh họa trong hình vẽ sau:

Lớp này sử du ̣ng các luồng của C ++ để cho phép các luồng dữ liệu được nạp từ hoặc lưu la ̣i thành các file mô ̣t cách hiê ̣u quả Lớp CArchive không thể tồn ta ̣i nếu không có mô ̣t đối tươ ̣ng CFile gắn với nó

Lớp CArchive có thể chứa dữ liê ̣u trong rất nhiều kiểu file khác nhau , tất cả chúng đều

là hâ ̣u duê ̣ của lớp CFile Theo mă ̣c đi ̣nh App Wizard sẽ bao gói tất cả các chức năng để ta ̣o

và mở các đối tượng CFile thông thường để sử dụng với CArchive Nếu chúng ta muốn hoă ̣c

Trang 29

22

cần thiết phải làm viê ̣c với mô ̣t trong các lo ại file này chúng ta cần phải viết thêm các đoạn

mã chương trình cần thiết để có thể làm việc với các loại file khác nhau

2.1.2 Hàm Serialize

Lớp CArchive được sử du ̣ng trong hàm Serialize trên các đối tượng document và dữ liệu trong các ứng du ̣ng Visual C ++ Khi một ứng du ̣ng đo ̣c hoă ̣c ghi lên mô ̣t file hàm Serialize của đối tượng document sẽ được go ̣i đến , truyền đối tượng CArchive được sử du ̣ng để ghi hoặc đọc từ file Trong hàm Serialize thứ tự logic được tuân theo để xác đi ̣nh xem đó là thao tác đo ̣c hay ghi bằng cách go ̣i tới các hàm IsStoring hoă ̣c IsLoading Giá trị trả về từ hai hàm trên sẽ xác định xem ứng dụng cần ghi hay đọc từ luồng vào ra của lớp CArchive Một hàm Serialize điển hình sẽ có cấu trúc như sau:

void CAppDoc::Serialize(CArchive& ar)

Chúng ta có thể đặt một hàm Serialize trong bất cứ lớp nào mà chúng ta tạo ra để sau đó

gọ đến hàm Serialize từ hàm Serialize của lớp document Nếu chúng ta đă ̣t các đối tượng do chúng ta tạo ra vào một mảng các đối tươ ̣ng chẳng ha ̣n như CObArray như trong ứng du ̣ng trước đây 3 ngày chúng ta có thể gọi tới hàm Serialize của mảng từ hàm Serialize của lớp document Đối tượng mảng sẽ , khi tớ i lươ ̣t nó , gọi tới hàm Serialize của các đối tượng đươ ̣c chứa bên trong nó

2.1.3 Các đối tượng có thể Serialize

Khi chúng ta ta ̣o ra lớp CLine trong bài thực hành số 10 chúng ta cần phải thêm 2 macro trước khi chúng ta có thể ghi la ̣i hoă ̣c na ̣p dữ liê ̣u từ các file Hai macro này , DECLARE_SERIAL và IMPLEMENT _SERIAL sẽ bao gói chức năng cần thiết trong các lớp của chúng ta để hàm Serialize có thể hoạt động đúng đắn

Macro thứ nhất DECLARE _SERIAL được đă ̣t ngay dòng lê ̣nh đầu tiên trong khai báo lớp (khái niê ̣m này giống như khái niê ̣m giao diê ̣n của Java ), nó nhận một tham số là tên của lớp, tác dụng của nó là sẽ thêm vào lớp của chúng ta các khai báo toán tử và hàm cần thiết để chức năng serialization hoa ̣t đô ̣ng đúng đắn

Trang 30

DECLARE_SERIAL

Macro này nhâ ̣n 3 tham số Tham số thứ nhất là tên lớp , giống như macro thứ nhất Tham số thứ hai là tên của lớp cơ sở (lớp cha) Tham số thứ ba là mô ̣t số version có thể được sử du ̣ng để xác đi ̣nh mô ̣t file có là đúng version để thực hiê ̣n thao tác đo ̣c với ứng du ̣ng của chúng ta hay không Số version này phải là mô ̣t số dương nên được tăng lên mỗi lần phương thức serializeation của lớp được thay đổi thay bất cứ cách thức nào làm thay đổi dữ liê ̣u được ghi hoă ̣c đo ̣c từ file Ví dụ về khai báo của macro thứ hai này như sau:

// MyClass.cpp: implementation of the CMyClass class

#include "stdafx.h"

#include "MyClass.h"

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]= FILE ;

#define new DEBUG_NEW

Trang 31

2.1.4 Cài đă ̣t hàm Serialize

Cùng với hai macro chúng ta cần cài đặt hàm Serialize trong lớp của chúng ta Hàm này nên đươ ̣c khai báo là hàm kiểu void với mô ̣t tham số (CArchive &ar), kiểu public và là hàm virtual Khi cài đă ̣t hàm này ch úng ta thường sử dụng cách tiếp cận giống như cách tương tự đươ ̣c dùng trong lớp document ở trên, có nghĩa là cần phải kiểm tra đó là thao tác ghi hay đọc trước khi thực hiê ̣n thao tác

2.2 Cài đặt một lớp Serializable

Khi chúng ta bắt đầu thiết kế mô ̣t ứng du ̣ng mới , mô ̣t trong những điều đầu tiên mà chúng ta cần thiết kế là cách thức chứa dữ liệu trong lớp document , dữ liê ̣u mà chương trình

sẽ tạo ra và thao tác với Nếu chúng ta ta ̣o ra mô ̣t ứ ng du ̣ng da ̣ng data -oriented chứa mô ̣t tâ ̣p dữ liê ̣u từ người dùng chẳng ha ̣n như mô ̣t ứng du ̣ng cở sở dữ liê ̣u chẳng ha ̣n làm thế nào để chứa các dữ liê ̣u đó trong bô ̣ nhớ của chương trình Cách tổ chức dữ liệu như thế nào nế u chúng ta làm việc với một ứng dụng soạn thảo văn bản hoặc ứng dụng xử lý bảng tính ?

Khi chúng ta đã xác đi ̣nh được cách thiết kế các cấu trúc dữ liê ̣u mà chương trình sẽ dùng chúng ta có thể quyết định được cách tố t nhất để thực hiê ̣n chức năng serialize cho chương trình và các lớp trong chương trình Nếu chúng ta quyết đi ̣nh lưu trực tiếp tất cả dữ liê ̣u của chương trình trong lớp document thì tất cả những gì chúng ta cần là ghi dữ liê ̣ u và

đo ̣c dữ liê ̣u với đối tượng CArchive trong hàm Serialize của lớp document Nếu chúng ta ta ̣o

ra lớp riêng để lưu các dữ liê ̣u của chương trình (như chúng ta đã làm ở bài thực hành số 10) chúng ta cần phải thêm chức năng se rializable cho các lớp này để chúng có thể tự ghi và đo ̣c từ file

Trong ứng du ̣ng mà chúng ta sẽ xây dựng ở bài thực hành này chúng ta sẽ viết mô ̣t chương trình cơ sở dữ liê ̣u da ̣ng đơn giản , flat-file để minh ho ̣a cách thức kết hợp các kiểu dữ liê ̣u khác nhau trong mô ̣t luồng dữ liê ̣u trong ứng du ̣ng serialization Ứng dụng của chúng ta

sẽ hiển thị một số các trường dữ liệu có kiểu khác nhau , ghi và đo ̣c dữ liê ̣u từ mô ̣t luồng dữ liê ̣u duy nhất với đối tượng CArchive

2.2.1 Thiết kế giao diê ̣n chương trình

Chúng ta có thể tạo ra các lớp của riêng mình , các lớp này có thể serialized , có thể sử dụng với các ứng dụng SDI hoặc MDI Nói ngắn gọn bất cứ ứng dụng nào làm việc với bất cứ kiểu dữ liê ̣u nào, dù đó là một cơ sở dữ liệu hoặc một tài liệu đều có thể serialized

Chúng ta tiến hành các bước sau:

1 Tạo một project dạng SDI có tên là Serialize

2 Trong phần Document Template String gõ phần File Extension là fdb

Trang 32

25

3 Phần Advanced features bỏ dấu check ActiveX control

4 Phần Generated Classes cho ̣n lớp cơ sở của lớp CSerialzeView là CFormView

5 Nhấn Finish , chúng ta sẽ nhận được thông báo là chương trình sẽ không có chức năng in ấn, nhấn Yes để tiếp tu ̣c

Sau khi ta ̣o mô ̣t ứng du ̣ng SDI hoă ̣c MDI trong đó lớp view kế thừa từ lớp CFormView chúng ta cần thiết kế form view của chương trình trình , quá trình này cũng giống như thiết kế các hộp thoại trong các ứng du ̣ng da ̣ng Dialog based nhưng chúng ta không cần có các nút để thoát khỏi chương trình hoặc hủy bỏ quá trình thực hiện như trên các hộp thoại thông thường Với mô ̣t ứng du ̣ng SDI hoă ̣c MDI chức năng ghi và thoát cử a sổ được đă ̣t trên các hê ̣ thống menu chương trình hoă ̣c trên các thanh công cu ̣

Chú ý : Nếu chúng ta làm viê ̣c với các ứng du ̣ng da ̣ng Dialog based App Wizard sẽ không cung cấp các đoa ̣n mã serialization cho ứng du ̣ng chúng ta cần p hải tự thực hiện điều này nếu muốn

Thiết kế form của chương trình gồm các điều khiển như bảng sau:

Object Property Setting

Static Text ID IDC_STATIC

Caption &Name:

Edit Box ID IDC_ENAME Static Text ID IDC_STATIC

Caption &Age Edit Box ID IDC_EAGE Static Text ID IDC_STATIC

Caption Marital Status:

Radio Button ID IDC_RSINGLE

Caption &Single Group Checked Radio Button ID IDC_RMARRIED

Caption &Married Radio Button ID IDC_RDIVORCED

Caption &Divorced Radio Button ID IDC_RWIDOW

Caption &Widowed Check Box ID IDC_CBEMPLOYED

Caption &Employed Button ID IDC_BFIRST

Caption &First

Trang 33

26

Button ID IDC_BPREV

Caption &Previous Button ID IDC_BNEXT

Caption Nex&t Button ID IDC_BLAST

Caption &Last Static Text ID IDC_SPOSITION

Caption Record 0 of 0

Giao diê ̣n chương trình sau khi thiết kế trong như sau:

Khi chúng ta phát triển các ứng du ̣ng hoă ̣c cửa sổ kiểu dialog -based chúng ta sẽ gắn các biến cho các điều khiển trên cửa sổ của lớp hô ̣p thoa ̣i Tuy nhiên đối với mô ̣t ứng du ̣ng da ̣ng SDI hoă ̣c MDI chúng ta sẽ gắn cho lớp nào ? Vì hàm UpdateData là một hàm của lớp CWnd

và lớp view là một hậu duệ của lớp CWnd, mă ̣c dù lớp document thì không phải , nên lớp view sẽ là nơi logic nhất để đă ̣t các biến gắn với các điều khiển trên cửa sổ chương trình Để gán các biến cho các điều khiển của chương trình chúng ta mở Class Wizard v à gắn các biến cho các điều khiển và chỉ định lớp chứa chúng là lớp view cụ thể ở ứng dụng này là CSerializeView Các biến được gán cho các điểu khiển như sau:

Object Name Category Type

IDC_CBEMPLOYED m_bEmployed Value BOOL IDC_EAGE m_iAge Value int IDC_ENAME m_sName Value CString IDC_RSINGLE m_iMaritalStatus Value int

Trang 34

27

IDC_SPOSITION m_sPosition Value CString Nếu như chúng ta kiểm tra mã chương trình của lớp view chúng ta sẽ thấy rằng không có hàm OnDraw Nếu chúng ta sử du ̣ng lớp tổ tiên CFormView cho ứng du ̣ng SDI hoă ̣c MDI chún g ta không cần lo lắng về hàm OnDraw Thay vào đó chúng ta sẽ đối xử với lớp view rất giống với lớp hô ̣p thoa ̣i như trong mô ̣t cửa s ổ hộp thoại hoặc một ứng dụng dạng dialog -based Sự khác nhau chủ yếu ở đây là dữ liê ̣u chương trình mà chúng ta cần sử du ̣ng để gán cho các điều khiển của cửa sổ chương trình không phải là của lớp view mà là của lớp

document Do đó chúng ta cần xâu dựng các tương tác giữa các lớp này để truyền các dữ liê ̣u giữa chúng

2.2.2 Tạo lớp Serializable

Khi chúng ta ta ̣o ra mô ̣t ứng du ̣ng da ̣ng form-based chúng ta sẽ giả sử rằng chương trình

sẽ chứa nhiều bản ghi trong form và người dùng có thể duyê ̣t qua các bản ghi này để thực hiê ̣n các thay đổi và ghi la ̣i Người dùng cũng có thể thêm vào các bản ghi mới và loa ̣i bỏ bản ghi nào đó Thách thức đối với chúng ta trong viê ̣c xây dựng ứng du ̣ng là làm thế nào để biểu diễn các bản ghi đó để có thể hỗ trợ tất cả các chức năng của chương trình

Mô ̣t cách tiếp câ ̣n là ta ̣o ra mô ̣t lớp bao gói tất cả các bản ghi và sau đó chứa tất cả các bản ghi trong một mảng giống như cách mà chúng ta đã thực hiện với các ứng dụng trong ba bài thực hành trước đây Lớp này sẽ cần kế thừa từ lớp CObject và cần chứa các biến cho tất

cả các biến tương ứng với các điều k hiển đươ ̣c thêm vào lớp view cùng với phương thức đo ̣c

và ghi tất cả các biến này Cùng với việc thêm vào các phương thức đọc và ghi các biến chúng

ta cần phải làm cho lớp này là mô ̣t lớp serialiable bằng các thêm vào hàm Serialize cho lớp cùng với hai macro đã mô tả ở trên

Tạo một lớp cơ bản

Như các ba ̣n có thể nhớ từ bài thực hành số 10 khi chúng ta muốn ta ̣o ra mô ̣t lớp mới chúng ta cần chọn tab Class View và nhấn chuột phải chọn Add | Add Class Sau đó trong hô ̣p thoại tiếp theo chọn đó là một Generic class , chúng ta sẽ học nhiều hơn về các lớp này trong bài thực hành số 16 Tiếp theo chúng ta gõ tên lớp là CPerson , lớ p cơ sở của lớp là lớp CObject Sau khi tạo xong lớp chúng ta tiếp tu ̣c thêm các biến thành viên cho lớp , các biến thành viên này cần phải khớp với các biến được gán cho các điều khiển trên cửa sổ chương trình trong lớp view như trong bảng sau:

m_bEmployed BOOL m_iAge int m_sName CString m_iMaritalStatus int

Cài đặt phương thức đọc và ghi cho lớp CPerson

Sau khi đã ta ̣o ra lớp CPerson chúng ta sẽ cung cấp mô ̣t phương tiê ̣n để đo ̣c và ghi các biến cho lớp Cách dễ nhất để cung cấp các chức năng này là thêm các hàm inline trong định nghĩa của lớp, chúng ta có thể thêm một tập cá c hàm inline để thiết lâ ̣p và lấy giá tri ̣ của các

Trang 35

// Functions for setting the variables

void SetEmployed(BOOL bEmployed) { m_bEmployed = bEmployed;}

void SetMaritalStat(int iStat) { m_iMaritalStatus = iStat;}

void SetAge(int iAge) { m_iAge = iAge;}

void SetName(CString sName) { m_sName = sName;}

// Functions for getting the current settings of the variables

BOOL GetEmployed() { return m_bEmployed;}

int GetMaritalStatus() { return m_iMaritalStatus;}

int GetAge() {return m_iAge;}

CString GetName() {return m_sName;}

2.2.3 Cài đặt hàm Serialize

Tiếp đến chúng ta sẽ thêm hàm Serialize cho lớp bắt đầu bằng viê ̣c thêm 2 macro vào đúng vi ̣ trí của chúng trong các file khai báo và cài đă ̣t của lớp CPerson , sau đó thêm hàm virtual, public Serialize(CArchive &ar) cho lớ p CPerson nhƣ sau:

void CPerson::Serialize(CArchive &ar)

Trang 36

29

if (ar.IsStoring())

// Write all of the variables, in order

ar << m_sName << m_iAge << m_iMaritalStatus << m_bEmployed;

else

// Read all of the variables, in order

ar >> m_sName >> m_iAge >> m_iMaritalStatus >> m_bEmployed;

}

Về bản chất hàm này cũng khá đơn giản , ban đầu nó go ̣i tới phương thức Serialize của lớp cha để xác đi ̣nh xem đó là thao tác đo ̣c hay ghi dữ liê ̣u, sau đó nó go ̣i tới hàm IsStoring để xác định nếu là ghi dữ liệu thì thực hiện ghi các biến thành viên của lớp còn ngược lại thì thực hiê ̣n thao tác đo ̣c (chú ý thứ tự của các biến là quan trọng)

Chú ý: chúng ta biết là một ứng dụng không phải khi nào cũng có thể đọc hoặc ghi dữ liê ̣u thành công nên trên thực tế khi chúng ta tiến hành thực hiê ̣n hàm trên sẽ có mô ̣t

Exception đươ ̣c throw để kiểm soát lỗi nhưng ở đây chúng ta tạm thời bỏ qua vấn đề này (xem phụ lu ̣c A cuốn Teach yourself Visual C++ 6.0 in 21 days để biết thêm chi tiết)

Cài đặt các chức năng cho lớp document

Khi chúng ta xây dựng mô ̣t ứng du ̣ng da ̣ng form -based trong đó form nằm trên cửa sổ là không gian chính để người dùng có thể tương tác với ứng du ̣ng có mô ̣t giả sử không được đề

câ ̣p rõ ràng là ứng du ̣ng của chúng ta sẽ cho phép người dùng làm viê ̣c với nhiều bản ghi Điều này có nghĩa là chúng ta cần hỗ trợ các tính năng lưu và duyệt qua các bản ghi này Viê ̣c lưu các bản ghi có thể thực hiê ̣n dễ dàng bằng cách sử du ̣ng mô ̣t mảng như chúng ta đã từng làm trong bài thực hành số 10 Cách làm này cho phép chúng ta có thể thêm vào mô ̣t số lượng bản ghi không hạn chế (không biết có đúng không nữa) Viê ̣c duyê ̣t qua các bản ghi được thực hiê ̣n qua bốn thao tác là First (duyê ̣t bản ghi đầu tiên), Last (bản ghi cuối cùng), Previous (bản ghi trước) và Next (bản ghi tiếp theo) Chúng ta cần một chức năng thông báo để xác định bản ghi nào đang được hiển thi ̣

Để lưu trữ và hỗ trợ các tính năng này lớp document cần hai biến : mô ̣t mảng và mô ̣t chỉ số bản ghi hiê ̣n ta ̣i như bảng sau:

e

m_iCurPosition

int

m_oaPeople

CObArray

Mô ̣t viê ̣c khác chúng ta cần làm là include file header của lớp CPerson vào file cài đă ̣t của lớp document (vị trí trước các file header của lớp document và view) như sau:

#include "stdafx.h"

#include "Serialize.h"

#include "Person.h"

Trang 37

30

#include "SerializeDoc.h"

#include "SerializeView.h"

Thêm mô ̣t bản ghi mới

Trước khi chúng ta có thể duyê ̣t qua các bản ghi trong chương trình chúng ta cần xây dựng chức năng thêm mô ̣t bản ghi mới cho mảng các đối tượng Cách tiếp cận tương tự như bài thực hành sẽ được sử dụng , và vì các bản ghi mặc định đều có các trường dữ liệu là rỗng nên chúng ta chỉ cần sử du ̣ng hàm cấu tử mă ̣c đi ̣nh do Visual C ++ cung cấp là đủ , đồng thời mỗi khi thêm vào mô ̣t bản ghi mới chúng ta sẽ gán bản ghi hiê ̣n ta ̣i là bản ghi mới đó (hàm này là private)

CPerson* CSerializeDoc::AddNewRecord(void)

{

// Create a new CPerson object

CPerson *pPerson = new CPerson();

// Did we run into a memory exception?

catch (CMemoryException* perr)

{

AfxMessageBox("Out of memory", MB_ICONSTOP | MB_OK);

Trang 38

Tương tự như bài thực hành số 10 chúng ta cần các hàm lấy tổng số bản ghi , số thứ tự

bà đối tượng tương ứng với bản ghi hiện tại như sau (các hàm này là public):

int CSerializeDoc::GetTotalRecords(void)

Trang 39

// After incrementing the position marker, are we

// past the end of the array?

Trang 40

Tiếp đến là hàm Serialize cho mảng các đối tượng của lớp document (CSerializeDoc):

void CSerializeDoc::Serialize(CArchive& ar)

// TODO: Add your specialized code here and/or call the base class

// Get the number of lines in the object array

int liCount = m_oaPeople.GetSize();

int liPos;

// Are there any objects in the array?

if (liCount)

{

Ngày đăng: 05/03/2014, 22:20

HÌNH ẢNH LIÊN QUAN

Hình thức và tiêu chuẩn đánh giá sinh viên: - BÀI GIẢNG: LẬP TRÌNH WINDOWS ppt
Hình th ức và tiêu chuẩn đánh giá sinh viên: (Trang 4)

TỪ KHÓA LIÊN QUAN

w