Download as pdf or txt
Download as pdf or txt
You are on page 1of 43

Lập trình với Symbian OS

Ra đời với sự hậu thuẫn của những “ông lớn” trong làng
điện thoại di động, hệ điều hành Symbian (Symbian OS)
đang ngày càng chứng tỏ khả năng của mình. Những chiếc
smartphone chạy trên nền Symbian OS ngày càng được ưa
chuộng và doanh số của các hãng ở thị phần này liên tiếp
tăng mạnh.

* Giới thiệu về hệ điều hành Symbian


- Trong thập kỉ 1980, Symbian OS vốn được biết nhiều trong giới công nghệ với tên
gọi EPOC, do hãng Psion thiết kế. Hệ điều hành này ban đầu được viết cho các máy
vi tính đời cũ với mục tiêu phát triển để có thể vận hành những dòng máy có kích
thước nhỏ, tài nguyên (bộ nhớ, CPU…) có giới hạn. EPOC được sử dụng đầu tiên trên
chính các sản phẩm PDA đời đầu của Psion.
- Đến năm 1998, thấy được tiềm năng của hệ điều hành này, Nokia, Ericsson,
Motorola, Matsushita quyết định cùng liên kết với Psion thành lập một liên danh
chuyên sản xuất hệ điều hành cho PDA và điện thoại di động. EPOC lúc này đã ở
phiên bản 5.
- Năm 1999, EPOC xuất hiện trong một vài model điện thoại di động của Ericsson và
được chính thức đổi tên thành Symbian OS, theo tên của liên danh 5 hãng, khi phiên
bản 6 ra đời. Tuy nhiên, vào thời điểm đó hệ điều hành này không được xem là một
“hệ điều hành thân thiện” vì không hỗ trợ người dùng cài đặt thêm các phần mềm ứng
dụng.
- Đến năm 2001, chiếc điện thoại Nokia đầu tiên chạy hệ điều hành Symbian OS ra
đời. Người dùng có thể cài đặt những phần mềm ứng dụng vào máy cũng như gỡ bõ
khá dễ dàng. Cùng giao diện dễ sử dụng, Symbian OS bắt đầu chứng tỏ sức mạnh của
việc được hậu thuẫn tốt.
- Hiện nay, Symbian là hệ điều hành chiếm thị phần lớn nhất trong các smartphone
trên thị trường, bao gồm những dòng Series 40, 60, 80, 90, UIQ và các dòng máy của
nhà cung cấp mạng DoCoMo tại Nhật Bản.

* Mở mà đóng
- Symbian không phải là một hệ điều hành mở như Linux trên máy vi tính hay
MobiLinux trên điện thoại di động. Tuy nhiên, mã nguồn của Symbian OS được sẵn
sàng cung cấp cho bất cứ nhà sản xuất điện thoại di động hay thiết kế chương trình
ứng dụng nào có nhu cầu. Bên cạnh đó, người dùng cũng không thể can thiệp để sửa
chữa mã nguồn của máy. Chỉ duy nhất các ứng dụng viết bằng ngôn ngữ lập trình
Java mới có thể can thiệp một phần nào đó vào các bước vận hành của hệ điều hành
này.

* Các phiên bản Symbian


- Series 40: Những điện thoại chạy Symbian OS series 40 có màn hình vuông, độ
phân giải 128x128, thường chỉ cài được nhạc, hình phim và các ứng dụng Java. Một
số dòng máy tiêu biểu: Nokia 6100, 6610, 6230, 7250…
- Series 60: Có màn hình lớn hơn, độ phân giải 176x208, có thể cài đặt được nhiều
chương trình, ứng dụng như từ điển, game dung lượng lớn,… Rất nhiều model điện
thoại Nokia chạy bằng phiên bản này như: 3650, 7650, 6600, 7610, N-Gage, các máy
thuộc N series…
- Series 80: Số điện thoại chạy trên Symbian series 80 hiện chưa nhiều, gồm: 9210,
9290, 9300, 9300i, 9500. Hệ điều hành này hỗ trợ bàn phím QWERTY trên điện thoại
di động, nhiều ứng dụng văn phòng, truy cập internet bằng trình duyệt Opera…
- Serie 90: Hiện chỉ có 2 model Nokia sử dụng phiên bản hệ điều hành này là 7700 và
7710. Các điện thoại này không sử dụng bàn phím điện thoại truyền thống mà người
dùng tương tác với máy qua màn hình cảm ứng hoặc chương trình nhận diện chữ viết
tay (7710). Các phần mềm hỗ trợ Series 60 và UIQ hoàn toàn không tương thích với
phiên bản hệ điều hành này trong khi một số chương trình ít dành cho Series 80 có thể
hoạt động trên 7700 và 7710.
- UIQ: Đây là phiên bản dành cho những điện thoại di động có màn hình cảm ứng và
người dùng có thể tương tác với máy qua cả bàn phím thật lẫn bàn phím ảo. Motorola
có các model A920, A1000, M1000… sử dụng UIQ. Có thể nói Sony Ericsson hiện là
hãng có nhiều mẫu điện thoại chạy trên nền UIQ nhất với các máy dòng P (800, 900,
910, 990, P1i), M600, W950.

* Những thử thách


Tuy đang chiếm lĩnh thị phần lớn nhất trong những chiếc điện thoại di động thông
minh nhưng Symbian OS đang gặp khá nhiều thách thức:
- Các virus máy tính tác oai tác quái thế nào thì cũng đang lăm le đe dọa điện thoại di
động như thế. Dĩ nhiên, “miếng mồi” ngon nhất không gì khác hơn là thị phần lớn
nhất. Từ sau sự xuất hiện của Cabir – virus đầu tiên tấn công vào những điện thoại
chạy Symbian OS , người ta nhận ra rằng việc bảo vệ cho những chiếc điện thoại vốn
không được quan tâm đúng mức. Tuy nhiên, thực tế là những virus này lây lan đều do
sự chủ quan của người dùng. Nếu không bấm phím kích hoạt nhận file lạ từ máy
khác, Cabir hoàn toàn không lây nhiễm vào điện thoại được. Vì thế, trong khi những
“nhà phát minh” tiếp tục viết ra những virus mới, vẫn còn đủ thời gian để những nhà
sản xuất tạo thêm thành trì bảo vệ người dùng.
- Có lẽ thử thách lớn nhất hiện nay của Symbian là những hệ điều hành nguồn mở
khác. Tuy cùng góp sức tạo dựng nên Symbian nhưng Motorola gần đây lại chuyển
hướng sang dùng MobiLinux cho những chiếc điện thoại mới xuất xưởng của mình.
Theo nhiều người, bên cạnh tính mở hơn, MobiLinux còn giúp những nhà lập trình dễ
dàng sửa đổi và tùy biến hơn Symbian khá nhiều.
- Theo một số dự báo, Symbian có thể sẽ thất thế khi các hệ điều hành khác cho điện
thoại di động ngày xuất hiện càng nhiều, với nhiều hứa hẹn ứng dụng cực kì hấp dẫn.
Nổi trội trong số này là Android của Google. Tuy nhiên, tương lai này có lẽ vẫn còn
khá xa khi những chiếc điện thoại chạy trên nền Android vẫn chưa chính thức xuất
hiện còn Nokia đã thâu tóm toàn bộ Symbian và nhiều ông lớn của ngành điện thoại
di động vẫn tiếp tục đều đặn cho ra đời những chiếc smartphone chạy trên nền hệ điều
hành này.

Ở phần trên, tôi nói qua về Symbian OS. Trước khi nói về lập trình Symbian, tôi xin
đưa ra một vài tip nhỏ giúp bạn dễ dàng hơn trong việc học lập trình trên Symbian:
- Bạn đọc các sách về lập trình Symbian. Nếu bạn mới học, hãy bắt đầu với cuốn
"Wiley S60 Programming - A Tutorial Guide (Apr - 2007).pdf” – Bạn có thể
download ở một trong các địa chỉ sau: Tại Rapidshare hoặc Tại Megaupload.
- Bạn xem các example của SDK. Có rất nhiều ví dụ hữu ích trong SDK về nhiều vấn
đề khác nhau.
- Bạn có thể tra cứu trong Help của SDK
- Bạn có thể tìm và tham khảo ở trang NewLC, một forum khá nổi tiếng dành cho lập
trình Symbian. Bạn sẽ tìm thấy rất nhiều thứ mình cần ở trang này.
-…

Sau đây tôi xin giới thiệu loạt bài để giúp các bạn có thể lập trình được với hệ điều
hành này. Ở đây chủ yếu tôi nói tới phiên bản Series 60 dành cho rất nhiều máy điện
thoại hiện nay.
- Quản lý các tệp thực thi trên Symbian
- Cấu trúc Project trên Symbian OS
- Hướng dẫn cài đặt và lập trình Symbian S60 3rd với Visual Studio 2005
- Hướng dẫn cài đặt và lập trình Symbian S60 1st & 2nd với Visual Studio 6.0
- Cấu trúc chương trình trên Symbian S60
- Quy ước và cách đặt tên khi lập trình với Symbian OS
- Xử lý ngoại lệ trên Symbian (Leave-Symbian exeption)
- An toàn hơn với Cleanup stack
- Khởi tạo hai pha (Two-phase construction) trong Symbian
- Sử dụng chuỗi (Descriptor) trên Symbian
- Một số thao tác vẽ cơ bản với đồ họa trong Symbian
- Sử dụng font và vẽ chữ trên Symbian
- Xử lý phím nhấn
- Sự kiện cho Menu
- Một số lỗi liên quan đến lập trình và cài đặt trên Symbian
- Một số mã lỗi trong Symbian OS
...

Quản lý các tệp thực thi trên Symbian

* Tệp thực thi


Trên Symbian hỗ trợ 2 hệ thống chương trình thực thi với các kiểu file khác nhau:
+ Chương trình .exe: được lưu trữ trong các file thực thi có phần mở rộng là exe.
Đây là chương trình với một đầu vào chính từ hàm E32Main(). Khi hệ thống nạp một
chương trình .exe mới, đầu tiên nó tạo một tiến trình mới. Trong tiểu trình chính của
tiến trình này, điểm vào sẽ được gọi để thực thi chương trình đó. Thông thường đây là
các server hay các ứng dụng console.
+ Thư viện liên kết động (Dynamic link library-DLL): một thư viện chứa các mã
chương trình với nhiều điểm đầu vào. Hệ thống sẽ nạp một DLL vào trong ngữ cảnh
hoạt động của tiểu trình. Có 2 loại DLL quan trọng:
- Shared DLL: cung cấp một nhóm API nhất định cho một hay nhiều chương trình sử
dụng. Hầu hết các thư viện này nằm trong các file có phần mở rộng là .dll. Một
chương trình thực thi sẽ được nối với thư viện dùng chung mà nó yêu cầu và khi hệ
thống nạp chương trình thực thi, thư viện dùng chung cần cho chương trình này sẽ
được nạp tự động.
- Polymorphic DLL: Cung cấp một nhóm hàm API được lưu trữ trong các file có
phần mở rộng khác nhau phục vụ cho các chức năng riêng như điều khiển máy in
(.prn), giao thức socket (.prt), hay đó là một ứng dụng đồ họa GUI (.app). Trong hệ
điều hành Symbian, polymorphic DLL thường chỉ có một điểm vào, nó khai báo và
khởi tạo một lớp dẫn xuất từ các lớp cơ sở trong DLL này. Thư viện DLL loại này
được nạp bởi chương trình sử dụng nó.
Chú ý: Chương trình .exe là không thể chia sẻ trong khi DLL thì hoàn toàn có thể.

* Nạp chương trình khi thực thi


Trong Symbian thì ổ C chính là RAM, ổ Z là ROM và thẻ nhớ thường là ổ D hoặc E.
+ Chương trình .exe: Một chương trình .exe được lưu trữ ở RAM hoặc trên thẻ nhớ,
khi chạy, chương trình sẽ được nạp vào RAM, được cấp một vùng nhớ riêng cho mã,
dữ liệu chỉ đọc, dữ liệu động. Nếu một phiên bản thứ 2 của chương trình được nạp
vào RAM thì một vùng nhớ mới sẽ được cấp cho nó. Với file chương trình .exe chứa
trong ROM thì chỉ có dữ liệu động được nạp vào RAM, mã chỉ thị và dữ liệu chỉ đọc
được đọc trực tiếp từ ROM.
+ Thư viện DLL: Khi một thư viện DLL lần đầu tiên được nạp vào RAM, nó được
cấp một vùng nhớ riêng, khi được yêu cầu sử dụng lần thứ hai, nó không nạp tiếp
DLL này vào RAM mà đơn giản chỉ gắn địa chỉ nó trên RAM cho tiểu trình yêu cầu.
Hệ điều hành Symbian kiểm tra số lượng tiểu trình tham khảo DLL này và giải phóng
nó khi không còn tiểu trình nào sử dụng nó nữa. Đó là lý do mà các ứng dụng đồ họa
Symbian (một loại polymorphic DLL), không hề có chức năng exit, nhất là các ứng
dụng hệ thống vì việc thoát nó sẽ do hệ thống đảm trách khi thiếu RAM cho các ứng
dụng khác) Với các DLL chứa trên ROM thì nó không cần nạp vào RAM nữa mà
được sử dụng trực tiếp trên ROM.
=> Việc các ứng dụng lưu trữ trên ROM không cần nạp vào RAM khi thực thi là đặc
điểm của Symbian để phù hợp với tài nguyên bộ nhớ giới hạn của điện thoại. Ngoài ra
để tối ưu hóa kích thước chương trình, hệ điều hành Symbian sử dụng điểm vào của
DLL là một số thứ tự, trên các hệ điều hành khác có thể dùng số thứ tự hay tên. Do đó
khi nâng cấp DLL thì số thứ tự phải giống như phiên bản trước.

* Server và việc thực thi ứng dụng


+ Các server được lưu trữ trong các file .exe, như ewsrv.exe là window server, hay
efsrv.exe là file server. Để giảm chi phí chuyển đổi ngữ cảnh các server có cùng
nhóm chức năng được dùng chung một tiến trình. Một server chính sẽ tạo tiến trình và
các server khác sẽ thực thi tiểu trình của nó với tiểu trình của server chính.
+ Ứng dụng console (không có giao diện đồ họa) được thực thi qua file chương trình
.exe. Các ứng dụng dạng này phải tạo một console riêng để tương tác với người dùng.
+ Các ứng dụng có giao diện đồ họa (GUI) là những thư viện polymorphic DLL với
phần mở rộng là .app. Điểm vào của ứng dụng này là NewApplication() tạo và trả về
một đối tượng dẫn xuất từ lớp CEikApplication (Series 80/9200Series/Series90) hay
các lớp dẫn xuất từ CEikApplication phù hợp theo từng dòng điện thoại Symbian như
CQikApplication (UIQ), CAknApplication (Series 60). Tiến trình ứng dụng được
tạo bởi một chương trình nhỏ .exe, Apprun.exe, và tên của file chương trình ứng
dụng .app được chuyển làm tham số cho Apprun.exe.

* Định danh tệp


Symbian không quản lý các file dựa trên tên và phân biệt loại file dựa trên phần mở
rộng như các hệ điều hành khác vẫn làm mà quản lý dựa trên một tổ hợp 3 số 32-bit.
Mỗi một số như vậy được gọi là định danh tệp (Unique Identifier - UID). UID được
dùng để phân biệt và xác nhận, chọn lựa đúng các loại đối tượng khác nhau tại thời
điểm nạp và thực thi, như phân biệt ứng dụng console, DLL, server, v.v... UID cũng
là thành phần cơ bản để liên kết ứng dụng với tài liệu, ví dụ tài liệu ứng với một ứng
dụng sẽ yêu cầu hệ thống nạp ứng dụng khi tài liệu đó được mở.
Ba UID này (UID1, UID2 và UID3) có giá trị hằng ứng với các tên gọi do Symbian
quy định, nhưng cũng có thể sử dụng số hệ 10 hay hệ 16.
- UID1: Định danh cấp hệ thống, chương trình thực thi .exe hay DLL được phân biệt
nhờ UID1. Với các giá trị tương ứng KExecutableImageUid=0x1000007A và
KDynamicLibraryUid=0x10000079.
- UID2: Định danh cấp giao tiếp, phân biệt các đối tượng cùng UID1. Ví dụ, UID2
được dùng để phân biệt thư viện dùng chung .dll và thư viện polymorphic (như .app,
.mdl, .fep hay .ctl) qua các giá trị: KSharedLibraryUid=0x1000008d cho thư viện
dùng chung và KUidApp=0x100039CE cho một ứng dụng đồ họa .app,
recognizer(auto start)=0x10003A19, front-end procesors=0x10005E32, hay control
panel=0x10003A34.
- UID3: Định danh cấp chương trình thực thi, phân biệt các đối tượng có cùng UID2,
chẳng hạn các ứng dụng đồ họa khác nhau sẽ có UID3 khác nhau. Do đó, có một tổ
chức quản lý UID3 này cho toàn môi trường Symbian. Để có nó, lập trình viên phải
gởi mail về uid@symbiandevnet.com để xin một số UID3 duy nhất trên môi trường
Symbian.
=> Tổ hợp 3 số UID sẽ là duy nhất trên toàn môi trường Symbian. Nếu bạn sử dụng
UID3 tùy tiện thì chương trình của bạn vẫn có thể chạy được nhưng nếu trên 1 máy
nào đó có sẵn chương trình khác cùng loại và có cùng UID3 (nghĩa là trùng cả 3 số)
thì chương trình của bạn sẽ không chạy vì chương trình cài trước đó sẽ được ưu tiên.

Một đối tượng hay một file trong Symbian có thể có một, hai, ba hay không cần
UID.
- Để sử dụng thuận tiện trong việc tương tác và chuyển đổi dữ liệu với các hệ thống
khác, hệ điều hành cho phép không cần sử dụng UID. Khi không có UID, Symbian sẽ
phân biệt dựa vào quy ước đặt tên.
- Ứng dụng thực thi .exe thường chỉ có UID1 với giá trị KExecutableImageUid.
- Ứng dụng DLL: Các ứng dụng này có UID1 là KDynamicLibraryUid. Với các thư
viện dùng chung .dll, UID2 sẽ là KSharedLibraryUid. Với các thư viện polymorphic,
UID2 sẽ có nhiều giá trị khác nhau tùy từng loại. UID3 thì các DLL hầu như không
cần, chỉ có các loại thư viện polymorphic là cần đến.
- Đối với các loại tài liệu thì UID1 là KDirectFileStoreLayoutUid hoặc
KPermanentFileStoreLayoutUid ứng với tài liệu độc lập và tài liệu cơ sở dữ liệu.
UID2 và UID3 phụ thuộc ứng dụng mà tài liệu phục vụ.
=> Vì UID là giá trị được sử dụng để phân biệt nên cần sự chính xác. Đối với UID3
dùng trong ứng dụng đồ họa, trong quá trình phát triển, có thể sử dụng một giá trị bất
kỳ trong khoảng 0x01000000 và 0x0fffffff. Nhưng khi cài ứng dụng vào điện thoại thì
nhất định đó phải là con số được cấp chính xác và duy nhất.

Cấu trúc Project trên Symbian OS


Thông thường các project được xây dựng trên các IDE hỗ trợ nhưng trên Symbian thì
khác. Vì có nhiều loại điện thoại, nhiều nền hệ thống và nhiều bộ công cụ phát triển
khác nhau nên hệ điều hành Symbian đã cho phép project được đặc tả trong một định
dạng độc lập. Sau đó, với các công cụ đi kèm trong các bộ công cụ phát triển hay trên
các IDE hỗ trợ, các file project này được định dạng lại thành các file project phù hợp
với IDE cho dòng điện thoại cụ thể và bộ SDK cho điện thoại đó.

* File định nghĩa project (.mmp)


File này mô tả các thông tin của project. Đây là một file project độc lập, nó sẽ được
chuyển thành file phù hợp môi trường phát triển cụ thể với các công cụ makmake
hay lệnh abld.bat. Nó định nghĩa các file tài nguyên và file thông tin ứng dụng cần
cho quá trình biên dịch.
Cấu trúc file .mmp gồm: các dòng khai báo với các loại câu khai báo khác nhau.
Thông thường chỉ một số ít khai báo được dùng. Sau đây là cú pháp các khai báo
thông dụng qua ví dụ project HelloWorld với file HelloWorld.mmp:
- Aif:
Mẫu khai báo:

aif target-file source-path resource [color-depth] source-


bitmap-list

Với:
target-file: Tên file đích, thường viết luôn phần đuôi và nằm trong thư mục ứng dụng.
source-path: Đường dẫn đến nơi chứa file tài nguyên mà aif cần
resource: Tên các file tài nguyên mà aif cần với tên đầy đủ.
color-depth: Đặc tả cho tất cả các file bitmap và ở dạng [c][digit] với c là color bitmap
và "digit" thể hiện độ sâu.

Ví dụ:
aif helloworld.aif \helloworld\aif\ helloaif.rss c8 hello.bmp
hellom.bmp

- Target:

target filename.ext

Ví dụ:

target HelloWorld.app

- TargetType:
Mẫu khai báo:

targettype target-type

Với target-type được hỗ trợ bao gồm: ani, app, ctl, dll, ecomiic, epocexe, exe, exedll,
fsy, kdll, kext, klib, ldd, lib, mda, mdl, notifier, opx, pdd, pdl rdl, var, wlog.
Ví dụ:

targettype app

- UID:
Mẫu khai báo:

uid uid2 [uid3]


Mỗi ứng dụng thực thi có 3 loại UID, UID thứ nhất là targettype ở trên. Loại thứ hai
và thứ ba là tùy chọn. UID được viết dưới dạng số hệ 10 hoặc hệ 16.
Ví dụ:
uid 0x100039CE 0x10004299

- TargetPath:
Mẫu khai báo:

targetpath target-path

Ví dụ:

targetpath \system\apps\HelloWorld

- SourcePath:
Mẫu khai báo:

sourcepath directory

Có thể có nhiều sourcepath nhưng đối với file mã và tài nguyên công cụ biên dịch chỉ
quan tâm đến trong khai báo sourcepath cuối cùng.
Ví dụ:

sourcepath ..\group

- Source:
Mẫu khai báo:

source source-file-list

Ví dụ:

source HelloWorld_Main.cpp

- UserInclude:
Mẫu khai báo:

userinclude directory-list

Ví dụ:

userinclude ..\inc

- SystemInclude:
Mẫu khai báo:

systeminclude directory-list

Ví dụ:
systeminclude \epoc32\include

- Resource:
Mẫu khai báo:

resource resource-file-list

Khai báo tên nên đầy đủ phần mở rộng.


Ví dụ:

resource HelloWorld.rss

- Library:
Mẫu khai báo:

library filename-list

Ví dụ:

library euser.lib apparc.lib cone.lib eikcore.lib

=> Tệp HelloWorld.mmp hoàn chỉnh:

TARGET HelloWorld.app
TARGETTYPE app
UID 0x100039CE 0x10004299
TARGETPATH \system\apps\HelloWorld
SOURCEPATH .
SOURCE HelloWorld_Main.cpp
SOURCE HelloWorld_Application.cpp
SOURCE HelloWorld_Document.cpp
SOURCE HelloWorld_AppUi.cpp
SOURCE HelloWorld_AppView.cpp
USERINCLUDE .
SYSTEMINCLUDE \epoc32\include
RESOURCE HelloWorld.rss
LIBRARY euser.lib apparc.lib cone.lib eikcore.lib
AIF helloworld.aif \helloworld\aif\ helloaif.rss c8 hello.bmp
hellom.bmp
* File mô tả thành phần bld.inf
File này luôn luôn có tên là bld.inf. Nó liệt kê danh sách các file project (thường chỉ
1), file xuất, các nền hệ thống và các file xuất với phần kiểm tra. Nó được công cụ
bldmake thực thi để tạo ra file bó abld.bat và các file thực thi khác. Thông thường
chỉ có khai báo prj_mmpfiles cho các file project được sử dụng.
Ví dụ: Với project HelloWorld trên, file bld.inf có cấu trúc như sau:

// Project files
prj_mmpfiles
HelloWorld.mmp

Chú ý:
- Mỗi câu khai báo xuất hiện trên một hàng riêng
- Sử dụng cách ghi chú của C++ cho phần ghi chú(// hoặc /* */)
- Các file nên khai báo đầy đủ phần mở rộng.
- Dấu \ được sử dụng để xác định sự liên tục dòng (dòng dưới cũng thuộc câu lệnh với
dòng trên, do dài được cắt xuống), do đó khi khai báo đường dẫn, dấu \ sau cùng phải
bỏ đi. Ví dụ nên viết SYSTEMINCLUDE \epoc32\include chứ không phải là
SYSTEMINCLUDE \epoc32\include\.
Hướng dẫn cài đặt và lập trình Symbian S60 3rd với VS2005

Dòng Symbian S60 3rd là hệ điều hành cho rất nhiều con điện thoại mạnh như Nokia
3250, Nokia E60, Nokia N91/N91 ME, N95, N95-8GB, N6290, N71, N73, N73 ME,
N75, N76, N92, N93, N93i, N6110 navigator, N5700 Xpress Music, N6120 classic,
N6121 classic, N81-MicroSD, N81-8GB, E50, E65, E90, E61/E61i, Nokia E63,
Nokia E70, Nokia E71, Nokia N80, Nokia N90, Nokia N77, Nokia N82... Vì vậy tôi
ưu tiên hướng dẫn cài đặt để lập trình cho dòng này trước.

Để cài đặt Symbian S60 3rd Edition tích hợp với IDE Microsoft Visual Studio
2005, thì đầu tiên bạn phải download Carbide.vs 3.0, bạn có tìm trên mạng chương
trình này hoặc download từ một trong hai địa chỉ sau:
- Download tại Rapidshare
- Download tại Megaupload
Sau khi download xong, bạn chạy chương trình này. Chương trình sẽ hướng bạn chi
tiết cần cài những gì và cả địa chỉ để download những thứ này. Cụ thể bạn phải cài đặt
những thứ sau:
1. Bạn cài Active Perl 5.6.1. Bạn hãy download tại địa chỉ Download Active Perl
5.6.1.638. Tôi cài nó vào thư mục C:\Symbian\Perl
2. Bạn cài SDK S60 3rd Edition. Bạn download SDK S60 tại địa chỉ Download SDK
S60. Ở đây tôi download và cài bản 3rd Edition, Maintenance Release. Tôi cài vào
thư mục C:\Symbian\9.1. Trong khi cài đặt, chương trình có hỏi bạn có cài đặt CSL
ARM Q1C Toolchain, thì bạn nhấn Yes để cài đặt nó. Và tôi cài nó vào thư mục
C:\Symbian\CSL Arm Toolchain
3. Bạn cài Java Runtime. Bạn download chương trình này trực tiếp tại trang chủ của
Java Sun
4. Bạn cài Visual Studio 2005. Sau khi cài xong Visual Studio 2005, bạn phải cài
Visual Studio 2005 SP1.
=> Các chương trình trên, bạn cài cái nào trước cũng được. Sau khi đã cài hết bạn
chạy lại Carbide.vs 3.0 và cài đặt bình thường. Ở đây tôi cài đặt vào thư mục
C:\Symbian\Carbide_vs30
Sau khi cài đặt xong, bạn restart lại máy tính.

Bây giờ bạn bật Visual Studio 2005, khi đó Carbide.vs 3.0 sẽ tự động chạy, nó hỏi
bạn đăng ký hoặc dùng 14 ngày. Nếu bạn lập trình bạn nên đăng ký luôn nhưng để
đăng ký được bạn phải đăng ký account trên trang Nokia Forum. Để đăng ký bạn chỉ
cần chọn "I don't have serial number" rồi nhấn Next. Nếu không có mạng, bạn chọn "I
don't have Internet Connection" nhấn Next và làm theo hướng dẫn. Nếu bạn có mạng
bạn chọn "I have Internet Connection" và nhấn next, nó sẽ tự động link đến trang để
lấy key, bạn đăng nhập xong, bạn sẽ nhập được Serial Number. Bạn quay lại
Carbide.vs và nhập Username (Là Username mà bạn đăng nhập
http://www.forum.nokia.com) và nhập Serial Number vào để đăng ký.

Như vậy bạn đã hoàn thành cài đặt. Bây giờ bạn thử tạo Project Symbian mới. Bạn
vào File/New/Project, trong Visual C++ bạn chọn Symbian. Bạn chọn New
Symbian OS Project trong mục chọn Template, đánh tên Project (HelloWorld
chẳng hạn) và chọn thư mục. Các Project của Symbian tôi để trong thư mục
C:\Symbian\Projects. Bạn nhấn Next, và chọn SDK (S60 3.0 Maint) rồi nhấn OK.
Bây giờ xuất hiện hộp thoại, bạn chọn:
- Project type: Symbian 9
- Project template: S60 3rd Ed. Hello World Application
- Bạn chọn S60 3.0 Maint, bạn chọn cả:
- WINSCW (Để lập trình và chạy trên máy ảo, luôn được chọn)
- GCCE (Để bạn build file sis chạy trên máy thật)
Bây giờ bạn nhấn Next để tùy chình thêm hoặc Finish để hoàn thành.
Bây giờ bạn để chế độ Debug, build và chạy thử. Máy ảo tự động được bật, nhưng
chương trình không tự động chạy được. Bạn phải vào mục Installations, bạn thấy
chương trình HelloWorld mà bạn vừa viết. Bạn hãy chạy thử xem.

Bây giờ bạn chọn chế độ Release, build lại Project. Trong thư mục Project của bạn,
bạn sẽ nhận được file HelloWorld.SIS trong thư muc sis. Bạn chỉ cần Sign file sis
này là có thể cài được trên máy thật sử dụng Symbian 3rd.
Bạn nên tìm hiểu và tham khảo các ví dụ của SDK trong thư mục:
C:\Symbian\9.1\S60_3rd_MR\Examples.

Dòng Symbian S60 3rd và Symbian UIQ 3.x được thiết kế với chế độ bảo mật cao
hơn hẳn các dòng Symbian S60 1st và 2rd. Dòng này yêu cầu ứng dụng của bạn phải
được Sign trước khi bạn cài đặt nó lên máy thật. Với mỗi ứng dụng tương ứng với
một IMEI, bạn có thể vào trang SymbianSigned.com để Sign, và bạn phải chờ đợi để
nhận được ứng dụng đã được sign. Nếu bạn đã cài SDK, tôi xin hướng dẫn bạn tự ký
ứng dụng của mình (self-sign Symbian software) và ứng dụng của bạn đã sign sẽ chạy
trên tất cả các máy Symbian S60 3rd và Symbian UIQ 3.x mà không phân biệt từng
IMEI khác nhau. Bây giờ tôi hướng dẫn cách sign thông qua việc sign ứng dụng
HelloWorld.SIS của chúng ta:
- Đầu tiên, bạn bật hộp thoại Run, đánh cmd rồi Enter để vào màn hình Command
Line.
- Trong màn hình Command, bạn đánh lệnh:

cd \
cd "C:\Symbian\Projects\HelloWorld\HelloWorld\sis"

để vào thư mục chưa file HelloWorld.SIS.


- Bạn đánh lệnh sau để tạo ra hai file .key và .cer:

makekeys -cert -password 123456 -len 1024 -dname "CN=HelloWorld


OU=ThangDao OR=ThangDao CO=VN EM=myshop4vn@gmail.com" helloworld.key
helloworld.cer

Bạn di chuyển chuột trên màn hình command line để lấy dữ liệu ngẫu nhiên cho việc
tạo tệp certificate.
- Bây giờ bạn đánh lệnh sau để thực hiện Sign tệp SIS:

signsis HelloWorld.SIS HelloWorld_Signed.SIS helloworld.cer


helloworld.key 123456
Bây giờ bạn lấy tệp HelloWorld_Signed.SIS (đã được Sign) để cài đặt trên máy thật.

Chú ý:
- Bạn làm tương tự để signed cho ứng dụng khác.
- Bạn có thể dùng trực tiếp tệp helloworld.cer và helloworld.key để sign cho ứng
dụng khác, khi đó bạn chỉ cần dùng lệnh signsis mà thôi. Bạn chỉ phải thay tên file sis
dùng để sign và file sis tạo ra sau khi sign.
- Chứng chỉ của bạn (.cer và .key) chỉ có thời hạn trong một năm, kể từ lúc bạn tạo
hai tệp này.
- Với các ứng dụng tự sign, khi cài đặt trên máy thì sẽ có cảnh báo Warning. Bạn
chọn Continue để cài đặt.

Hướng dẫn cài đặt và lập trình Symbian S60 1st & 2nd với VC6

Để lập trình cho các dòng Symbian đời cũ như S60 1st và S60 2nd, nếu bạn không sử
dụng các hàm nào đặc biệt thì bạn nên cài bản 1st Edition, FP1, Wins. Khi đó ứng
dụng của bạn sẽ tương thích với tất cả các máy sử dụng dòng Symbian S60 1st và
2nd.

Để cài đặt và lập trình cho dòng này, bạn thực hiện như sau:
1. Bạn cài đặt ActivePerl-5.6.1 (Nếu bạn chưa cài). Bạn hãy download tại địa chỉ
Download Active Perl 5.6.1.638. Tôi cài nó vào thư mục C:\Symbian\Perl
2. Bạn cài đặt S60 SDK 1st Edition. Bạn download SDK S60 tại địa chỉ Download
SDK S60. Ở đây tôi download và cài bản 1st Edition, FP1, Wins. Tôi cài vào thư
mục C:\Symbian\6.1.
3. Bạn cài Visual Studio 6.0 để làm IDE lập trình. IDE này tôi cài vào thư mục mặc
định.

Trong thư mục “C:\Symbian\6.1\Series60\Epoc32Ex” chứa các ví dụ sẵn có của


SDK, bạn có thể vào đây để xem và tham khảo. Ở đây, chúng ta bắt đầu thử với
project đầu tiên “Hello World”. Các Projects cho Symbian tôi để ở thư mục:
C:\Symbian\Projects
- Để bắt đầu với ví dụ này, bạn copy thư mục helloworld trong
“C:\Symbian\6.1\Series60\Epoc32Ex” sang thư mục C:\Symbian\Projects.
- Bây giờ bạn bật hộp thoại Run, sau đó đánh cmd, rồi Enter để vào màn hình
Command Line. Bạn đánh lệnh sau để sang chuyển sang thư mục Project:

cd \
cd c:\symbian\projects\helloworld

- Tiếp bạn đánh lệnh:

bldmake bldfiles

khi đó tệp ABLD.BAT sẽ được tạo trong thư mục project helloworld.
- Bây giờ bạn đánh tiếp lệnh:
abld makefile vc6

để tạo ra tệp Project (HelloWorld.dsw) trên VC6. Bạn đừng đóng màn hình
Command Line vội. Tí nữa bạn sẽ còn phải dùng tiếp.

- Bây giờ bạn search file “helloworld.dsw” trong thư mục cài đặt SDK
(C:\Symbian\6.1), chính xác thì nó ở trong thư mục
C:\Symbian\6.1\Series60\Epoc32\BUILD\SYMBIAN\PROJECTS\HELLOWOR
LD\HELLOWORLD\WINS

- Bạn mở tệp “helloworld.dsw” bằng Visual Studio 6.0, bạn chon chế độ Debug
(Release cũng vậy thôi), và nhấn F7 để build thử. Không lỗi gì đúng không bạn. Bây
giờ bạn ấn Ctrl+F5 để chạy thử chương trình trên máy ảo. Nếu nó xuất hiện hộp thoại
thì bạn chỉ đường dẫn đến tệp epoc.exe cho nó:
+ Nếu dịch Debug bạn chỉ đến
C:\Symbian\6.1\Series60\Epoc32\Release\wins\udeb\epoc.exe
+ Nếu dịch Release thì bạn chỉ đến thư mục
C:\Symbian\6.1\Series60\Epoc32\Release\wins\urel\epoc.exe

Bạn nhấn OK, máy ảo sẽ được bật lên. Bạn dùng phím mũi tên di chuyển xuống dưới,
bạn sẽ thấy ứng dụng helloworld mà bạn vừa build. Bạn hãy chọn nó để chạy thử:
- Chạy máy ảo vậy là okie. Bây giờ bạn sẽ build để chạy máy thật. Bạn đánh lệnh sau:

abld build armi urel

- Bây giờ bạn sẽ thấy trong thư mục


C:\Symbian\6.1\Series60\Epoc32\Release\armi\urel có các tệp
HELLOWORLD.APP và HELLOWORLD.RSC

- Bạn copy hai tệp này vào thư mục nào đó trong ổ C:, chẳng hạn tôi copy vào thư
mục C:\Symbian\Apps\helloworld
-Trong thư mục C:\Symbian\Apps\helloworld bạn tạo thêm tệp helloworld.pkg như
sau:

; helloworld.pkg
;

; standard SIS file header


#{"Helloworld"},(0X10008ACE),1,0,0,TYPE=SISAPP

;Supports Series 60 v2.0


(0x101F6F88), 0, 0, 0, {"Series60ProductID"}

;Tep lien quan den ung dung


"HELLOWORLD.APP"-"!:\system\apps\HelloWorld\HelloWorld.app"
"HELLOWORLD.RSC"-"!:\system\apps\MDictPro\HelloWorld.rsc"

- Sau đó bạn đánh lệnh sau để vào thư mục chứa các tệp này. Sau đó đánh các lệnh
sau:

cd \
cd "C:\Symbian\Apps\helloworld"
makesis helloworld.pkg

Bây giờ bạn đã có file helloworld.SIS để cài đặt và chạy trên máy thật.
=> Vậy là Okie rồi. Chúng ta đã cài đặt và chạy thử thành công.

Upload lại links cho các bạn down:


ActivePerl-5.6.1.638 (8 MB):
http://downloads.activestate.com/ActivePerl/Windows/5.6/
ActivePerl-5.8.8.819 (15 MB): http://oldapps.com/Perl.php

Sau đây là chương trình Helloworld đã được viết và đóng gói lại thành file *sis

Download tại đây: http://www.mediafire.com/file/cmtmkwinz ... oworld.rar


Cấu trúc chương trình trên Symbian S60

Bây giờ chúng ta đi sau phân tích cấu trúc của một chương trình trên Symbian S60
thông qua ví dụ HelloWorld ở trên. Đây là ứng dụng dạng GUI có đuôi .APP. Để
thống nhất tên lớp thì trên Symbian S60 3rd, bạn tạo Project với tên là Example chứ
không phải HelloWorld như trước nữa.

Chúng ta thấy rằng, ứng dụng HelloWorld bao gồm các lớp như sau:
- Lớp CExampleApplication: Đây chính là lớp Application và kế thừa từ
CEikApplication (trên Symbian S60 1st) hoặc lớp CAknApplication (trong
Symbian S60 3rd). Lớp này chịu trách nhiệm thiết lập và thực thi ứng dụng.
- Lớp CExampleDocument: Đây là lớp Document của ứng dụng. Lớp này được kế
thừa từ lớp CEikDocument (trên Symbian S60 1st) hoặc lớp CAknDocument (trên
Symbian S60 3rd). Lớp này được tạo bởi lớp Application, nó quản lý dữ liệu của ứng
dụng. Lớp này cũng có nhiệm vụ khởi tạo lớp giao diện ứng dụng AppUi.
- Lớp CExampleAppUi: Đây là giao diện ứng dụng AppUi. Lớp này kế thừa từ
CEikAppUi (Symbian S60 1st) hoặc CAknAppUi (Symbian S60 3rd), và có nhiệm
vụ điều khiển các sự kiện. Lớp này sẽ nhận các sự kiện như chọn menu, phím bấm, …
và gửi sự kiện này tới các View và Container.
Lớp này điều khiển sự kiện chọn menu thông qua hàm

void HandleCommandL(TInt aCommand);

và điều khiển sự kiện phím nhấn thông qua hàm:

void HandleWsEventL(const TWsEvent &aEvent, CCoeControl


*aDestination)

- Lớp CExampleAppView: Đây chính là view để hiển thị ra màn hình, nó kế thừa từ
lớp CCoeControl. Bạn hãy thực hiện thao tác vẽ trong hàm:

void Draw(const TRect& /*aRect*/) const;

của lớp view.

Như vậy chúng ta thấy cấu trúc ứng dụng GUI trên Symbian S60 như sau:
Khi ứng dụng được chọn thực thi, chương trình apprun.exe sẽ hoạt động với tên ứng
dụng và tên file ứng dụng làm tham số. Chương trình apprun sẽ sử dụng kiến trúc
ứng dụng APPARC để nạp ứng dụng qua việc kiểm tra UID2 là KUiApp
(0x100039ce) và tạo đối tượng ứng dụng đồ họa qua hàm NewApplication(). Các
hàm này bao gồm hàm [color=#0000FF]NewApplication()[/color] và hàm
E32Dll(TdllReason) (một hàm chỉ cài đặt, không sử dụng). Từ khóa EXPORT_C để
báo hàm NewApplication là đầu vào của một DLL.

EXPORT_C CApaApplication* NewApplication()


{
return new CExampleApplication;
}
GLDEF_C TInt E32Dll(TDllReason)
{
return KErrNone;
}

Khi NewApplication() được gọi, đối tượng CExampleApplication sẽ được tạo, đối
tượng này tạo CExampleDocument, và lớp CExampleDocument sẽ tạo lớp giao
diện ứng dụng CExampleAppUi. Khi lớp CExampleAppUi được tạo, hàm khởi tạo
ConstructL của AppUi sẽ được gọi, và trong hàm này lớp CExampleAppView được
tạo thông qua lệnh:

iAppView = CExampleAppView::NewL(ClientRect());

Khi view được tạo, ngoài các hàm khởi tạo ContructL, nó gọi thêm hàm

void Draw(const TRect& /*aRect*/) const;

để thực hiện vẽ lên màn hình hiển thị.

Trong lớp CExampleAppUi, thì bạn dùng hàm

void HandleCommandL(TInt aCommand);

để điều khiển sự kiện cho menu. Nhưng trong ứng dụng trên Symbian S60 1st của
chúng ta không có menu nên bạn không thể thoát khỏi ứng dụng. Bây giờ tôi thực
hiện điều khiển sự kiện phím nhấn để thực hiện thoát khỏi ứng dụng khi nhấn phím
trái. Ở đây tôi sử dụng hàm HandleWsEventL để điều khiển nhận sự kiện phím trái để
thoát khỏi ứng dụng. Để thực hiện được điều này, bạn làm như sau:
- Bạn thêm khai báo vào phần khai báo của lớp CExampleAppUi:

void HandleWsEventL(const TWsEvent &aEvent, CCoeControl


*aDestination);

ngay sau khai báo hàm HandleWsEventL


- Trong phần mã lệnh của lớp CExampleAppUi bạn thêm đoạn mã sau:

void CExampleAppUi::HandleWsEventL(const TWsEvent &aEvent,


CCoeControl *aDestination)
{
if (aEvent.Type()==EEventKey && aEvent.Key()-
>iCode==EKeyDevice0)
Exit();

CEikAppUi::HandleWsEventL(aEvent, aDestination); // Tren


Symbian S60 1st
//CAknAppUi::HandleWsEventL(aEvent, aDestination); // Tren
Symbian S60 3rd
}

Bây giờ bạn chạy ứng dụng, và dùng phím chức năng bên trái để thoát khỏi ứng dụng.
Việc bạn bắt sự kiện phím trên Symbian OS 3rd cũng tương tự.

+ Mô hình MVC trong Symbian


Như vậy một ứng dụng GUI trên Symbian tuân theo mô hình MVC (Model - View -
Control). Mô hình này bao gồm:
- Model: Dữ liệu của ứng dụng, nó đảm nhận việc lưu trữ các thông tin dữ liệu của
ứng dụng.
- View: Nơi thể hiện dữ liệu của ứng dụng, người dùng chỉ có thể biết ứng dụng thông
qua nó.
- Controller: Phần này có nhiệm vụ thao tác trên dữ liệu ứng dụng: cập nhật model
sau đó yêu cầu view thể hiện lại phần cập nhật.
=> Tuy nhiên không phải lúc nào cũng phải đầy đủ cả phần này. Tùy theo tính chất
của ứng dụng mà có thể ranh giới giữa model và view không rõ ràng hay có thể thiếu
đi phần controller. Đây chính nguyên tắc cho các ứng dụng đồ họa: "Những gì thể
hiện trên màn hình giao tiếp với người dùng sẽ do các hàm vẽ (draw) đảm nhận.
Chúng chỉ có nhiệm vụ đơn giản là vẽ lại dữ liệu của ứng dụng lên màn hình, chúng
không thay đổi dữ liệu. Nếu bạn muốn thay đổi gì đó thì bạn phải sử dụng hàm khác
và rồi gọi lại hàm vẽ này để vẽ lại dữ liệu đã thay đổi".

Mô hình MVC và Symbian có mối quan hệ rất mật thiết, nó là mô hình thiết kế chủ
đạo trong Symbian. Nếu để ý kỹ bạn sẽ thấy lớp document là hiện thân của model,
lớp AppView chính là view, còn AppUi sẽ đóng vai trò là controller trong mô hình
MVC. Với những ứng dụng phức tạp thì sẽ có nhiều lớp đảm nhận một thành phần
trong MVC.

Không những vậy, hầu hết các control trong Symbian đều được thiết kế theo mô hình
MVC. Nắm bắt được điều này, bạn sẽ dễ dàng thao tác với các control trong
Symbian. Tôi nhớ là có khá nhiều người mới lập trình Symbian khi làm quen với
listbox đều đặt câu hỏi: "Làm sao để lấy dữ liệu một item trong listbox đây bởi trong
lớp CEikListbox không thể tìm thấy hàm nào đảm nhận việc này". Đó là vì listbox
trong Symbian cũng được thiết kế theo mô hình MVC nên nếu muốn lấy dữ liệu, bạn
phải đến lớp MListBoxModel thông qua hàm Model() trong lớp CEikListbox.

Quy ước và cách đặt tên khi lập trình với Symbian OS

Symbian đưa ra một số quy ước trong lập trình. Một số quy ước bạn không nhất thiết
phải theo nhưng nhưng một số thì bạn nên tuân thủ để phục vụ cho việc lập trình của
bạn thuận lợi, tránh sai sót và dễ nâng cấp sau này.

1. Tên lớp
Symbian sử dụng các quy ước đặt tên sau để xác định đặc tính cơ bản của một lớp:
- Lớp T: Lớp đơn giản (tựa như typedef) thường xây dựng từ các kiểu dữ liệu cơ sở
hay kết hợp chúng lại, có thể so sánh lớp T với struct đơn giản bao gồm các dữ liệu
public. Nó không có destructor (có thể có constructor nhưng hiếm) và thường được
lưu trên stack, có thể lưu trên heap. Kiểu liệt kê (enum) cũng thường khai báo dưới
dạng lớp T. Ví dụ: TInt, TBool, TPoint, TDes, TMonthsOfYear ...
- Lớp C: Lớp có constructor và destructor và tất cả đều là dẫn xuất từ CBase. Các đối
tượng của chúng được tạo bằng new và luôn được lưu trữ trên heap. Ví dụ:
CConsoleBase, CActive,...
- Lớp R: Lớp R (đại diện cho Resource), thường đại diện cho một loại tài nguyên,
quản lý một sesion kết nối với một server phục vụ một tài nguyên. Đối tượng lớp R
thường có một hàm khởi tạo (Open() hoặc Create() hay Initialize()) và một hàm kết
thúc (Close() hay Reset()) để giải phóng tài nguyên. Quên gọi hàm kết thúc khi dùng
đối tượng lớp R là một lỗi thường gặp và kết quả là sẽ bị leak bộ nhớ. Nó có thể được
lưu trên heap, nhưng thường là trên stack. Ví dụ: RFile, RTimer, RWindow,...
- Lớp M (Mix-ins): Lớp ảo (abstract) giống như interface trong Java, nó chỉ bao
gồm các phương thức ảo rỗng và không có dữ liệu cũng như constructor. Việc kế thừa
nhiều lớp trong Symbian là cho phép tuy nhiên phải theo quy tắc là kế thừa chính từ 1
lớp C (bắt buộc phải có và viết đầu tiên) và nhiều lớp M. Ví dụ:
MGraphicsDeviceMap, MGameViewCmdHandler,...
=> Việc phân biệt giữa T, C và R lớp là rất quan trọng, nó ảnh hưởng tới việc giải
phóng bộ nhớ khi sử dụng cũng như cách thức xử lý các đối tượng thuộc các lớp này.
Ngoài ra trong Symbian còn có các lớp tĩnh (static) phục vụ cho một số chức năng
riêng như lớp User hay lớp Mem. Một ngoại lệ khác là lớp HBufC, chúng ta sẽ nói
đến nó trong bài viết về sử dụng xâu trên Symbian.

2 Tên dữ liệu
Tương tự, Symbian cũng dùng chữ cái đầu để phân biệt các loại dữ liệu:
- Hằng liệt kê (Enumerated constant): Bắt đầu với ký tự E, nó đại diện cho một giá
trị hằng trong một dãy liệt kê. Nó có thể là một phần của lớp T. Ví dụ: (ETrue,
EFalse) của TBool hay EMonday là một thành phần của TDayOfWeek.
- Hằng (constant): Bắt đầu với ký tự K, thường được dùng trong các khai báo
#define hay các giá trị hằng do Symbian quy định. Ví dụ: KMaxFileName hay
KErrNone.
- Biến thành phần (member variable): Bắt đầu với chữ cái i (instance), được dùng
khi sử dụng các biến động là thành viên của một lớp. Đây là quy ước quan trọng,
dùng cho việc hủy vùng nhớ trên heap của các đối tượng này trong destructor. Tôi
thường chỉ dùng quy ước này nếu biến này sẽ được lưu trên heap, còn trên stack thì
không. Ví dụ: iDevice, iX, …
- Tham số (argument): Bắt đầu bằng chữ a (argument), được dùng khi các biến làm
tham số. Ví dụ: aDevice, aX, …
- Macro: Không có quy ước đặc biệt. Tất cả đều viết hoa và dùng dấu gạch dưới để
phân tách từ. Ví dụ: IMPORT_C, _TEST_INVARIANT, _ASSERT_ALWAYS,
v.v…
- Biến cục bộ (automatic): Chữ cái đầu nên viết thường.
- Biến toàn cục (global): Nên viết hoa chữ cái đầu nhưng để tránh nhầm lẫn nên bắt
đầu tên bằng chữ cái “g”. Tuy nhiên trên Symbian không khuyến khích dùng biến
toàn cục.

3. Tên hàm
Tên hàm bắt đầu bằng ký tự hoa. Khác với 2 trường hợp trên, quy ước đặt tên hàm lại
dựa trên ký tự cuối cùng:
- Hàm không ngắt giữa chừng (non-leaving function): Đó là hàm mà trong quá
trình thực thi nó đều diễn ra suông sẻ, chi tiết tôi sẽ nói sau. Ví dụ: Draw() hay
Intersects().
- Hàm ngắt giữa chừng (leaving function): Là hàm bị ngắt ngang vì một lý do nào
đó: lỗi, thiếu tài nguyên, ... Hàm này kết thúc bằng ký tự L. Ví dụ: DrawL() hay
RunL().
- Hàm LC: Kết thúc với cặp ký tự LC. Các hàm này trong lòng nó có khai báo một
đối tượng mới, và có đặt đối tượng này lên cleanup stack (ngăn xếp chứa các đối
tượng cần xóa khi có ngắt xảy ra, sẽ nói rõ sau) và có khả năng xuất hiện ngắt trong
khối xử lý hàm. Bạn lưu ý là sau khi gọi hàm này sẽ phải gọi Cleanup:PopAnd
Destroy(), lý do tôi sẽ nói trong phần về cleanup stack, nếu quên gọi nó chắc chắn bạn
sẽ bị lỗi 3 mà không hiểu tại sao. Ví dụ: AllocLC(), CreateLC(), OpenLC() hay
NewLC(),...
- Các hàm Get và Set: Trong trường hợp đơn giản thường là các hàm thành viên của
một lớp. Set dùng cho việc xác lập giá trị cho một biến thành viên của lớp. Get được
dùng cho các hàm sẽ trả về giá trị trên tham số. Khi hàm có giá trị trả về thì thường
không dùng Get.
Ví dụ: SetThing(aData); GetThing(aData); nhưng iData = Thing();

4. Cấu trúc thư mục project


Tuy không bắt buộc nhưng Symbian khuyến khích lập trình viên xây dựng thư mục
project ứng dụng thành các thư mục con với chức năng riêng biệt. Thông thường thư
mục project có cấu trúc như sau:
- Thư mục project: Chứa các file project .mmp, bld.inf, file tài nguyên .rss. Thư mục
này cũng sẽ lưu trữ các file thông tin cụ thể cho chương trình dùng với các IDE. Thư
mục này thường được đặt tên là: group.
- Thư mục các file khai báo: Chứa các file khai báo cho file tài nguyên và các file
mã nguồn. Thư mục này thường có tên là: inc.
- Thư mục mã nguồn: Chứa các file cài đặt các lớp chương trình. Thư mục này
thường có tên là: src.
- Thư mục dữ liệu: Chứa dữ liệu cần cho chương trình ứng dụng và có tên là data.
- Thư mục thông tin ứng dụng: Chứa file tài nguyên .rss để tạo file .aif và các hình
ảnh, tài nguyên phục vụ cho ứng dụng. Tậo hợp các hình này được lưu trữ trong một
file .mbm (multi bitmap). Thư mục này có tên là aif.
- Thư mục cài đặt: Chứa các file .pkg, các thành phần cài đặt bổ sung cho ứng dụng.
Thư mục này thường có tên là: install.
- Thư mục chương trình: Lưu trữ file cài đặt .sis. Thường nó được gộp với thư mục
install. Thư mục này có tên là release.
=> Tuy nhiên các project thường chỉ gồm các thư mục: group, inc và src.

5. Trình bày code


Nếu lập trình trên C và Java thấy 2 phong cách trình bày quen thuộc là:

void Example()
{
.........
.........
}

void Example(){
.........
.........
}

thì Symbian đề xuất cách trình bày sau:

void Example()
->{
->.........
->........
->}
Xử lý ngoại lệ trên Symbian (Leave-Symbian exeption)

1. Cơ chế bắt lỗi trên Symbian


Nếu bạn đã quen với lập trình C++ hay Java thì exeption handling là một khái niệm
chẳng xa lạ gì. Đây là cơ chế giúp ta quản lý các lỗi phát sinh. Lúc Symbian được
thiết kế thì cơ chế exeption chưa được giới thiệu trong C++ hơn nữa sau này khi được
giới thiệu thì nó cũng tỏ ra không phù hợp trong môi trường hạn chế về xử lý và bộ
nhớ như Symbian bởi chúng làm tăng đáng kể kích thước mã biên dịch và tốn nhiều
RAM, lại không thật sự hiệu quả.

Vì vậy Symbian đã đưa ra một cơ chế quản lý lỗi cho riêng mình được biết dưới tên
gọi "leave". Do đó tuy Symbian sử dụng cú pháp C++ nhưng không hề có từ khóa try,
catch hay throw đâu, các bạn nên chú ý điều này.

Trong một môi trường mà tài nguyên hạn hẹp như Symbian thì một cơ chế bắt lỗi hiệu
quả và ít tốn kém sẽ rất cần thiết. "Leave" đã đáp ứng điều này. Cơ chế hoạt động
của "leave" như sau: "Khi xảy ra một lỗi nào đó (thiếu bộ nhớ để cấp phát, thiếu vùng
nhớ để ghi, lỗi trong truyền thông hay thiếu năng lượng cho các tài nguyên,...) thì
hàm đang hoạt động sẽ bị ngắt lại, quyền điều khiển sẽ được chuyển đến phần chỉ thị
sửa lỗi".

Xét về mặt cú pháp thì cơ chế "leave" này khá tương đồng với cơ chế của C++. Hàm
đang thực thi bị ngắt bởi một cuộc gọi đến hàm User::Leave() hay
User::LeaveIfError() khá giống với throw trong C++ còn 2 marco TRAP và TRAPD
trên Symbian thì tương đồng với try và catch trên C++.
Ví dụ:

TInt result;
TRAP(result, MyLeaveL());
if (KErrNone==result)
{
//Code
}
User::LeaveIfError(result);

2. Hàm leave
Như tôi đã nói trong phần quy ước trên Symbian, hàm có thể leave thì sẽ kết thúc
bằng chữ L. Một hàm có thể leave nếu nó:
- Gọi hàm có thể leave mà không được gọi kèm với các trap harness như TRAP hay
TRAPD.
- Gọi một trong các hàm hệ thống đảm nhận leave như User::Leave() hay
User::LeaveIfError(),...
- Có dùng toán tử new(Eleave).

Chắc có lẽ có nhiều bạn sẽ thắc mắc tại sao tôi lại quá chú trọng đến "leave" như vậy.
Thật ra "leave" là một khái niệm rất cơ bản trên Symbian bởi vì:
- Thứ nhất, nguồn tài nguyên trên Symbian khá hạn hẹp nên lỗi thiếu tài nguyên hay
xảy ra
- Thứ 2, nếu bạn không chú ý kỹ đến nó, nhất là phần thế nào là một hàm leave thì
bạn sẽ gặp phải lỗi rất lớn trong lập trình trên Symbian: gây ra "leak" bộ nhớ.

An toàn hơn với Cleanup stack

Khi hàm của bạn có leave xảy ra thì tại thời điểm leave, điều khiển sẽ được chuyển
đến phần xử lý lỗi, lúc này vùng stack cho hàm có leave này sẽ được giải phóng, các
biến khai báo cục bộ trong hàm này sẽ bị xóa đi. Đối với các biến khai báo kiểu T trên
stack thì không sao nhưng đối với các biến kiểu C khai báo trên heap hay các biến
kiểu R thì đây là vấn đề nghiêm trọng. Bởi lẽ theo đúng quy trình thực thi, nếu không
có gì xảy ra thì vào cuối hàm, chúng ta sẽ hủy vùng nhớ đối tượng trên heap qua toán
tử delete hay gọi hàm Close() cho các biến kiểu R nhưng nếu giữa chừng hàm bị ngắt
trước khi ta gọi các hàm hủy này thì rõ ràng các đối tượng này sẽ không được giải
phóng hoàn toàn, tạo ra leak (lỗ hổng) trên bộ nhớ.

Leak bộ nhớ là vùng nhớ trên heap thực sự không được sử dụng nhưng hệ điều hành
nghĩ là nó đang sử dụng và sẽ không sử dụng vùng nhớ này để cấp phát cho các đối
tượng khác. Leak bộ nhớ thường do bạn cấp phát động bộ nhớ (sử dụng hàm new, ...)
mà không giải phóng nó (hàm delete,...). Bị leak bộ nhớ sẽ gây ra sự lãng phí tài
nguyên bộ nhớ, đặc biệt trên thiết bị giới hạn về tài nguyên như di động thì là cả một
vấn đề.

Ví dụ:

void UnsafeFunctionL()
{
CExClass* test = CExClass::NewL(); //Hàm có thể leave
test->FunctionMayLeaveL();
delete test;
}

Điều gì xảy ra khi hàm FunctionMayLeaveL() bị leave, lúc này hàm


UnsafeFunctionL() sẽ bị ngắt, stack sẽ bị xóa, biến test bị bị xóa nhưng vùng nhớ
cấp cho nó trên heap qua hàm CExClass::NewL() thì vẫn còn và lúc này không ai
quản lý nó cả, nó bị "mồ côi" trên heap. Vùng nhớ cấp phát này sẽ tồn tại mà không
được giải phóng tại ra một lỗ hổng trong bộ nhớ.

Vậy bây giờ ta phải làm sao đây để luôn đảm bảo không bị lỗ hổng trên bộ nhớ khi có
leave xảy ra? Symbian đã đưa ra khái niệm mới là Cleanup Stack. Cleanup stack là
một ngăn xếp có nhiệm vụ giải phóng các vùng nhớ cấp cho các đối tượng được đưa
vào chúng trước đó khi leave xảy ra.
Ví dụ: Dùng hàm trên với cleanup stack:

void SafeFunctionL()
{
CExClass* test = CExClass::NewL(); //Hàm có thể leave
CleanupStack:: push(test);
test->FunctionMayLeaveL();
CleanupStack:: pop(test);
delete test;
}

Lúc này nếu có leave xảy ra thì chúng ta vẫn không sợ bị lủng bộ nhớ vì cleanup stack
đã giải phóng vùng nhớ cho biến test giùm chúng ta rồi nhờ hàm:
CleanupStack::push(test).

Một số lưu ý:
- Nếu leave không xảy ra thì chúng ta phải lấy đối tượng cần hủy ra khỏi cleanup
stack qua hàm CleanupStack::pop(test) (chúng ta có thể dùng nhiều cách pop khác
nhau, chi tiết các bạn xem qua lớp CleanupStack).
- Với những hàm kế thúc bằng LC, nghĩa là có thể leave và đã có push lên cleanup
stack rồi, nên sau khi gọi hàm này, bạn phải gọi hàm CleanupStack::pop() hoặc
CleanupStack::popAndDestroy() nếu không sẽ bị lỗi. Lỗi này tôi cũng đã nói trong
phần quy ước rồi, các bạn chú ý nhé, hay bị lỗi này lắm đó.
- Đối với các đối tượng kế thừa các lớp khác ngoài lớp C thì khi hủy cleanup stack
chỉ có thể hủy vùng nhớ mà không thể gọi destructor như đối với lớp C được nên
Sym bian đề xuất một số hàm push khác cho phù hợp: CleanupReleasePushL() để
chỉ giải phóng vùng nhớ (đối tượng lớp T), CleanupDeletePushL() để chỉ thực thi
destructor (đối tượng lớp M) hay CleanupClosrPushL() để giải phóng tài nguyên
cấp cho các đối tượng lớp R.

Leave và Cleanup stack là một cặp bài trùng tạo nên sự an toàn cho lập trình trên
Symbian. Đây là 2 khái niệm rất cơ bản, nếu không hiểu vè nó, trong khi lập trình có
thể bạn sẽ gặp lỗi mà không biết đường sửa hay có thể gặp vài lỗi rất ngớ ngẫn.

Khởi tạo hai pha (Two-phase construction) trong Symbian

Đến đây chắc bạn đã thấy là Symbian hỗ trợ quản lý bộ nhớ tốt như thế nào, đảm bảo
cả trong điều kiện lỗi vẫn không bị lủng bộ nhớ. Lý do khiến Symbian rất chú trọng
đến việc này là so với PC, điện thoại di động có bộ nhớ không lớn bằng hơn nữa các
ứng dụng trên điện thoại đôi khi phải chạy hàng tháng, thậm chí hàng năm (nếu ta
không tắt máy).

Từ bài cleanup stack, có người thắc mắc là có phải tất cả các lớp đều có hàm NewL()
và NewLC() để khởi tạo đối tượng không. Nhận thấy có một phần quan trọng đi liền
sau leave và cleanup stack là khởi tạo 2 pha (two-phase construction) nên tôi sẽ xin
nói về nó luôn.

1. Khởi tạo hai pha (Two-phase construction)


Theo đúng cú pháp C++, một đối tượng mới sẽ được cài đặt như sau:

CExam* exam = new (Eleave) CExam();

Hệ thống sẽ làm gì với code trên: "Đầu tiên, một vùng nhớ sẽ được cấp trên heap cho
đối tượng lớp CExam nhớ hàm new, rồi sau đó constructor của lớp CExam sẽ được
gọi để hoàn tất việc khởi tạo đối tượng. Điều gì sẽ xảy ra nếu hàm constructor của
lớp này bị leave, rõ ràng là bộ nhớ sẽ bị lủng do heap đã cấp một vùng cho đối tượng
foo rồi và như vậy vùng nhớ này sẽ bị "mồ côi" trên heap".
=> Lúc này ắt hẳn bạn sẽ nghĩ ngay đến cleanup stack, thế nhưng tiếc thay trong
trường hợp này lại không dùng được. Tai sao ư? Tại vì để làm được điều đó thì hàm
CleanupStack::push phải đặt trong lòng toán tử new, điều này có thể sao?!

Vì vậy Symbian đưa ra một luật là: "constructor không được phép leave". Nhưng đôi
khi trong phần khởi tạo của chúng ta lại có phần có thể leave thì sao, chẳng hạn như
cấp phát bộ nhớ hay tạo một session truy cập tài nguyên. Trong tình huống đó, khởi
tạo 2 pha (two-phase construction) sẽ giúp bạn:
- Pha 1: Phần constructor đơn giản, không leave. Phần này sẽ được gọi liền ngay sau
khi toán tử new được gọi.
- Pha 2: Một hàm khác sẽ đảm nhận việc hoàn tất khởi tạo, trên Symbian thường đặt
tên là ConstructL(), hàm này có thể leave.

CExam* exam = new (Eleave) CExam(); // Pha 1


CleanupStack::push(exam);
exam->ContructL(); // Pha 2
CleanupStack:: pop();

Và bây giờ thì ta đã yên tâm là bất cứ có gì xảy ra, bộ nhớ vẫn nguyên vẹn.

2. NewL() và NewLC()
Nhưng cách trên bất tiện ở chỗ là khi khởi tạo 1 đối tượng lại phải gọi 2 pha với 4
hàm, vừa bất tiện vừa dễ quên. Do đó Symbian tiếp tục đưa ra một khái niệm nữa để
giúp cho lập trình viên chúng ta tránh được sự hay quên và dài dòng này. Trong lớp
CExam, chúng ta tạo thêm 2 hàm tĩnh (có từ khóa static đằng trước phần khai báo
thông thường) NewL() và NewLC() như sau:

CExam* CExam::NewLC()
{
CExam* me = new (Eleave) CExam(); // Pha 1
CleanupStack::push(me);
exam->ContructL(); //Pha 2
return me;
}

CExam* CExam::NewL()
{
CExam* me = CExam::NewLC();
CleanupStack:: pop (me);
return me;
}

Và lúc này việc khai báo đối tượng của bạn sẽ vừa dễ dàng lại đảm bảo:

CExam* exam = CExam::NewL();

Lưu ý: Ở trên các bạn thấy tôi luôn xài Eleave sau toán tử new. Nhờ nó mà nếu
không cấp phát được, leave sẽ xảy ra. Nếu không có nó rõ ràng sau hàm new bạn phải
kiểm tra xem có cấp phát thành công không, rất mất công lại làm code phức tạp thêm.
CExam* exam = new CExam();
if (NULL != exam)
{
// Cấp phát thành công
}

Tóm lại: Nếu trong hàm constructor mà không có gì để gây ra leave cả, thì các bạn
cứ xài như đã từng xài trước đây, nghĩa là dùng code như C++ vậy. Nếu trong hàm
constructor này mà có leave thì bạn mới phải cần đến two-phase construction.

Sử dụng chuỗi (Descriptor) trên Symbian

+ Tổng quan về chuỗi (Descriptor) trên Symbian


Trên Symbian, chuỗi được gọi là descriptor bởi chúng tự mô tả: một descriptor chứa
kích thước, kiểu chuỗi bên cạnh nội dung chuỗi trong vùng nhớ. Đây là điều mà chuỗi
trong C/C++ hay Java không có.

Descriptor được xây dựng theo hướng tối ưu hóa vùng nhớ cấp phát, điều rất cần trên
các môi trường bộ nhớ thấp như Symbian. Nhưng cũng chính vì điều này mà
descriptor làm đau đầu khá nhiều lập trình viên về việc sử dụng chúng.

Từ version 5.0 trở về trước, Symbian chưa hỗ trợ Unicode nên descriptor chỉ hỗ trợ
chuỗi với ký tự 8 bit, nhưng từ v5.1 trở về sau thì descriptor trên Symbian hỗ trợ cả 8
bit và 16 bit (Unicode). Một điểm đặc biệt nữa là descriptor không sử dụng ký tự đặc
biệt để kết thúc chuỗi như trên C/C++ hay Java, nên dữ liệu chúng chứa có thể là dữ
liệu nhị phân. Việc sử dụng chung descriptor cho lưu trữ ký tự và nhị phân rõ ràng sẽ
làm nhỏ gọn lại Symbian và tiện lợi cho người dùng. Lưu ý là khi muốn lưu trữ nhị
phân, bạn phải khai báo descriptor ở dạng 8 bit.

Các descriptor có hai dạng:


- Descriptor hằng (Descriptor có dữ liệu không thể thay đổi được)
- Descriptor động (Descriptor có dữ liệu có thể thay đổi được)

Hình vẽ sau cho cho ta thấy được kiến trúc của Descriptor trên Symbian:

* Descriptor hằng TDesC


Tất cả các kiểu descriptor trên Symbian đều kế thừa TDesC (typedef của TDesC16,
xem e32std.h) và được định nghĩa trong e32des16.h (thư mục: epoc32\include). Bản
8 bit là TDesC8 (e32des8.h). Như đã giới thiệu: C kết thúc lớp báo hiệu đây là lớp
hằng, dữ liệu không đổi.

Trên vùng nhớ , một descriptor được lưu trữ như sau:
- 4 byte đầu lưu trữ chiều dài (thật ra chỉ 28 bit trong số 32 bit (4 byte) là lưu trữ
chiều dài chuỗi, do đó chuỗi tối đa mà ta có thể chứa trong descriptor là 2^28 byte,
256 MB, 4 bit cao còn lại chứa kích thước của đối tượng descriptor, đây là cơ sở để
xác định kiểu descriptor. Với 4 bit chúng ta có thể phân biệt 16 kiểu descriptor nhưng
trên Symbian chỉ có 5 loại).
- Phần tiếp theo sẽ lưu trữ dữ liệu descriptor hay con trỏ chỉ đến vùng lưu trữ thật sự.

Lấy chiều dài thông qua hàm Length(), trong khi truy cập dữ liệu thì nhở vào hàm
Ptr(). Đây là 2 hàm ảo quan trọng nhất, từ đây TDesC sẽ cài đặt tất cả các hàm liên
quan cho descriptor như truy cập dữ liệu, so sánh chuỗi, tìm kiếm, ...

Đây là lớp trừu tượng nên nó không thể khởi tạo, vì vậy khi sử dụng lớp này làm tham
số cho hàm bạn sử dụng cú pháp dạng const TDesC& để cung cấp các truy cập chỉ
đọc tới dữ liệu của Descriptor.

* Descriptor "động" TDes


"Động" ở đây mang nghĩa là dữ liệu chứa trong descriptor có thể thay đổi. Tất cả các
descriptor động đều kế thừa từ TDes. Về mặt lưu trữ, descriptor động giống như
descriptor hằng, chỉ có thêm thành phần chứa chiều dài tối đa của chuỗi dữ liệu. Hàm
MaxLength() sẽ trả về giá trị này.

TDes định nghĩa một loạt các phương thức phục vụ cho việc thay đổi dữ liệu như
thêm, chèn, hay định dạng dữ liệu,... Một đặc điểm đáng lưu ý là một khi đã khai báo
chiều dài tối đa thì không sao thay đổi được, do TDes và các lớp kế thừa từ nó đều
không có hàm phục vụ cho việc cấp thêm bộ nhớ nên khi thêm dữ liệu, bạn phải lưu ý
là dữ liệu thật sự lưu không bao giờ được vượt quá max length, nếu không bạn sẽ gặp
phải lỗi.

TDesC, TDes (TDes16, TDes8 - Hiện nay TDes luôn là TDes16). Đây là các lớp cơ
sở của tất cả các lớp descriptor khác, chúng chủ yếu cung cấp các hàm thao tác cho
các descriptor hằng cũng như động khác hơn là phục vụ cho mục đích lưu trữ dữ liệu
như các lớp dẫn xuất khác. Do đó, bạn sẽ thấy chúng chủ yếu ở vai trò làm tham số
hay kết quả trả về nhằm mục đích phục vụ cho lập trình tránh bị phụ thuộc loại
descriptor cụ thể (đặc điểm độc đáo của lập trình hướng đối tượng). Một điểm nữa
củng cố cho điều trên là bạn không thể khai báo trực tiếp một đối tượng kiểu TDesC
hay [color=#BF0000]TDes[/color] được do constructor của chúng là hàm protected.

+ Một số Descriptor cụ thể


* Xâu dạng Literals (Literal descriptor):
Đây là một loại descriptor hằng, gần giống với static char [] trên C. Chúng thường
được dùng dưới dạng các macro gồm 3 dạng _LIT, _L và _S (đây là 3 dạng trung
tính, thật sự là _LIT16, _L16, _S16, _LIT8, _L8, _S8 phục vụ chuỗi 16 và 8 bit), bạn
có thể tìm thấy các khai báo macro này ở e32def.h.

Literals cung cấp cách đơn giản để định nghĩa xâu trong chương trình. Chúng ta có
thể định nghĩa và sử dụng như sau:

_LIT(KMyName, "Helen");
TBuf<KMaxItemLength> myName;
myName.Append(KMaxItemLength);

Literals rất dễ thay đổi nếu bạn định nghĩa chúng ở đầu file .cpp, chúng chỉ đơn giản
như định nghĩa marco.

Cũng giống như ở trên, với khai báo _L, một vùng dữ liệu trên file chương trình được
dùng để chứa chuỗi khai báo, nhưng khác với ở trên, chúng không có tên, không có gì
để nắm giữ, điều khiển chúng.

myName.Append(_L("Helen"));

Trong trường hợp này, hệ thống sẽ tạo một pointer descriptor là TPtrC trên stack
(chúng ta sẽ nghiên cứu TPtr sau) tạm thời đảm nhận việc kiểm soát và xử lý chuỗi dữ
liệu này. . Ví dụ, một khi ứng dụng lỗi, để test xem có phải hàm này gây ra hay
không, trên UIQ chúng ta có thể làm theo cách sau: ngay sau hàm nghi ngờ, chúng ta
đặt hàm sau:

User::InfoPrint(_L("Pass"));

Nếu dòng chữ "Pass" hiện lên màn hình thì rõ ràng lỗi chắc chắn nằm sau hàm này.
Cách viết này dễ hơn rất nhiều so với:

_LIT(KPass, "Pass");
User::InfoPrint(KPass));

_S macro gần giống với _L, tuy nhiên nó không yêu cầu tạo đối tượng tạm TPtr mà
sẽ cho phép sử dụng chuỗi trực tiếp như trên C.

=> Hiện nay, Symbian đã đề xuất bỏ kiểu khai báo này do tốn thêm stack cho đối
tượng tạm TPtr và chi phí khởi tạo nó. Tuy nhiên nhờ ưu điểm là giảm code lại khỏi
phải đặt tên nên chúng vẫn thường được dùng với mục đích test. _LIT cần ít bộ nhớ
hơn nếu xâu được sử dụng nhiều hơn 1 lần và Symbian khuyến khích sử dụng _LIT
trong việc tạo các ứng dụng sử dụng hiệu quả vùng nhớ.
* Buffer descriptor:
Đây là descriptor mà chuỗi dữ liệu chứa trong đối tượng descriptor. Chúng được chia
làm 2 loại dữ trên vùng nhớ lưu trữ: stack và heap.
- Stack buffer descriptor: Chúng có thể là descriptor hằng hoặc động. Chúng kế
thừa từ TBufCBase và TBufBase gồm TBufC<n> và TBuf<n>. TBufC phục vụ cho
các descriptor hằng, còn TBuf cho descriptor động, n ở đây chính là khai báo cho
chiều dài tối đa của chuỗi. Chúng được so sánh với char [] trên C. Ví dụ:
Mã:
_LIT(KHello, "Hello World!");
TBufC<12> aHelloBufC(KHello);
TBuf<15> aHelloBuf(KHello);

Lưu ý: Do kích thước stack cấp cho một ứng dụng là rất nhỏ nên chuỗi cấp trên stack
cũng chỉ nên cấp cho các chuỗi nhỏ, thường dưới 128 byte, nếu lớn hơn nên cấp trên
heap.

- Heap buffer descriptor: Loại descriptor này ra đời để phục vụ cho các descriptor
có kích thước lớn, không thể lưu trên stack được, và đặc biệt hữu dụng trong các
trường hợp không biết rõ kích thước tại thời điểm biên dịch. Nó được dùng chủ yếu
cho mục đích lưu trữ dữ liệu trong các thao tác xử lý file hay trên các kênh truyền
thông: hồng ngoại hay bluetooth. Đại diện cho descriptor này là HBufC (HBufC16
hay HBufC8), nhưng thường được sử dụng thông qua con trỏ, HBufC*. Bạn có thể
dùng hàm NewL() để tạo các đối tượng descriptor, lưu ý là sau khi dùng xong phải
hủy chúng đi vì chúng được cấp trên heap. Ví dụ:
Mã:
_LIT(KHello, "Hello World!");
HBufC* iHelloBufC = HBufC::NewL(20);
*iHelloBufC = KHello; //Copy nội dung KHello vào iHellBufC

Kí tự ‘C’ xác định descriptor là hằng và không thể chỉnh sửa được. Nếu bạn cần
chỉnh sửa descriptor này bạn có thể sử dụng hàm Des(), hàm này khởi tạo một con trỏ
trỏ tới descriptor này, là con trỏ dạng TPtr và có thể chỉnh sửa được dữ liệu. Ví dụ:
Mã:
_LIT(KMessage, "Hello");
HBufC* aMessage = HBufC8::NewL(KMaxItemLength);
aMessage->Des().Append(KMessage);

Để có thể thao tác với xâu có kích thước lớn hơn, bạn có thể sử dụng ReAllocL() để
mở rộng kích thước của Desciptor nhưng khi đó có thể xảy ra lỗi tràn hay gây ra một
panic.
Mã:
aMessage = aMessage->ReAllocL(25);

Bình thường các descriptor cấp phát trên vùng heap có thể khởi tạo bằng New() hoặc
NewLC(). Nhưng nếu descriptor này đã tồn tại, thì có thể dùng các phương thức
Alloc(), AllocL(), AllocLC() để tạo descriptor mới.
Mã:
TBuf<KMaxLength> temp;
temp.Append(_L("Hello Mum"));
HBufC* hPtr;
hPtr = temp.AllocL();
* Pointer descriptor: Đây là con trỏ trỏ tới Descriptor khác. Chúng bao gồm 2 loại
hằng và động được so sánh với const char* và char*, được thể hiện qua 2 lớp TPtrC
và TPtr. Ví dụ:
Mã:
_LIT(KHello, "Hello World!");
TPtrC aHelloBufC(KHello);
TPtr aHelloBuf(KHello);

- TPtrC là kiểu hằng, kí tự ‘C’ trong TPtrC là viết tắt của từ Constant, có nghĩa là
không thể thay đổi. Con trỏ này có hai tham số là độ dài và địa chỉ:

- Trong khi đó TPtr có thêm tham số xác định độ dài tối đa Descriptor cho phép
chỉnh sửa (Max length)

+ Các phương thức thao tác với Descriptor


* Các phương thức cho descriptor hằng
Sau đây tôi xin giới thiệu một vài phương thức thông dụng sử dụng cho lớp TDesC
and TBufC. Các phương thức khác bạn có thể tham khảo thêm trong tài liệu SDK.
- Alloc(), AllocL() và AllocLC(): Tạo desciptor 16 bit trên vùng heap và chứa bản sao
của dữ liệu.
- Compare(), CompareC() và CompareF(): So sánh hai xâu và trả về giá trị một số
nguyên. Chú ý kí tự ‘C’ chỉ “collated comparisons” còn kí tự ‘F’ chỉ ‘folded
comparisons’. Giá trị trả về của phương thức so sánh:
0: Hai xâu bằng nhau.
Giá trị dương: Descriptor chính lớn hơn tham số.
Giá trị âm: Descriptor chính nhỏ hơn tham số.

Mã:
TBufC<KMaxLength> descriptorOne;
descriptorOne = _L("One");
TBufC<KMaxLength> descriptorTwo;
descriptorTwo = _L("Two");
TInt result;
result = descriptorOne.Compare(descriptorTwo);

Biến result có giá trị -5 nhưng hiếm khi ta quan tâm tới giá trị này mà ta chỉ quan tâm
tới dấu của giá trị trả về của hàm so sánh.
- Find(), FindC() và FindF(): Tìm kiếm xâu con. Giá trị trả về là một số nguyên xác
định offset tới thể hiện đầu tiên của xâu tìm được. Nếu không tìm thấy, giá trị
KErrNotFound được trả về. Các hàm này luôn tìm từ vị trí bắt đầu của xâu, để tìm từ
một vị trí khác bạn sử dụng thêm các hàm Left(), Mid(), Right().
- Left(TInt aLength): Trả về một TPtrC tới phần tận cùng bên trái của descriptor.
- Right(TInt aLength): Trả về một TPtrC tới phần tận cùng bên phải của descriptor.
- Mid(TInt aPos): Trả về một TPtrC tới một vùng xác định của xâu, bắt đầu từ vị trí
aPos và kết thúc ở cuối xâu.
- Mid(TInt aPos, TInt aLength): Trả về một TPtrC tới một vùng xác định của xâu, bắt
đầu từ vị trí aPos với độ dài xác định bởi aLength.
- Length(): Trả về số kí tự trong xâu.
- Size(): Trả về số byte của Descriptor.
- Locate(), LocateC() và LocateF(): Tương tự như phương thức Find() nhưng để tìm
kí tự đơn chứ không phải tìm một xâu. Vị trí kí tự đầu tiên chính là vị trí đầu của xâu.
Ví dụ sau trả về giá trị 1 trong biến result:

TBufC<KMaxLength>descriptorOne;
descriptorOne = _L("One");
TChar aChar;
aChar = 'n';
result = descriptorOne.Locate(aChar);

- LocateReverse(): Hàm này tương tự như Locate() nhưng tìm từ vị trí cuối xâu. Nếu
không tìm thấy thì giá trị KErrNotFound được trả về.
+ Toán tử !=, <, >, <=, >=, == và []: Giúp đơn giản trong so sánh xâu. Các toán tử này
được định nghĩa trong TDesC. Trong TBufC đưa ra toán tử =, mặc dù đây là lớp
hằng.

TBufC<5> descriptorOne;
descriptorOne = _L("One");
TBufC<5> descriptorTwo;
descriptorTwo = _L("Two");
if (descriptorOne <= descriptorTwo)
descriptorTwo = descriptorOne;

* Các phương thức thao tác với desciptor "động"


Descriptor TBuf và TPtr đưa ra các phương thức giúp bạn có thể chỉnh sửa được dữ
liệu.
- Append(): Giúp chèn thêm một kí tự hay một descriptor.

TBuf<KMaxLength> aDescriptor;
aDescriptor.Append(_L("Hello"));

Nếu độ dài của descriptor mới và dữ liệu hiện tại của aDescriptor mà lớn hơn
KMaxLength, ứng dụng sẽ sinh ra USER 11 PANIC.
- Capitalize(), UpperCase() và LowerCase(): Tên của hàm đã thể hiện rõ chức năng
của các hàm này.
- Copy(): Hàm này copy từ descriptor nguồn tới descriptor đích, và thay thế hết các
dữ liệu đã tồn tại.
- Delete(): Xóa các vùng con xác định của xâu. Ví dụ sau khi thực hiện lệnh sau thì
aDescriptor chứa “Hello Peace”:

TBuf<20> aDescriptor;
aDescriptor.Append(_L("Hello World Peace"));
TChar aChar = 'W';
TInt aPos = aDescriptor.Locate(aChar);
TInt aLength = 6;
aDescriptor.Delete(aPos, aLength);

- Fill(): Hàm này điền đầy descriptor bằng một kí tự nào đó hay là kí tự có mã 0.
- Format(): Sao chép dữ liệu đã được định dạng vào descriptor. Chi tiết xem thêm tài
liệu SDK.
Mã:
aDescriptor.Format(_L("%S\t%S"), aDes1, aDes2);

- Num(TInt aNum): Chuyển số nguyên sang xâu và đưa vào descriptor. Nếu
descriptor đã có dữ liệu thì nó thay thế dữ liệu đã tồn tại.

Một số thao tác vẽ cơ bản với đồ họa trong Symbian

Mỗi vùng màn hình chính là đối tượng CCoeControl, được xem như control mà bạn
có thể vẽ nội dung bất kỳ lên vùng màn hình mà nó chiếm. Phương thức Draw() kế
thừa từ CCoeControl cho phép truy cập tới vùng màn hình được định nghĩa bởi
TRect. Tất cả các lớp kế thừa từ CCoeControl đều có hàm Draw(), và bạn chồng
hàm này để thực hiện các thao tác vẽ.

Một control cần một cửa sổ để vẽ, và vùng cửa sổ này được thiết lập trong hàm
ConstructL(). Và để sử dụng CCoeControl bạn cũng cần include tệp “coecontrol.h”
và thêm link “cone.lib”. Ví dụ sau tạo một lớp kế thừa từ CCoeControl và thực hiện
vẽ lên vùng màn hình của nó:
Mã:
class CStartContainer : public CCoeControl
{
public:
CStartContainer();
CStartContainer();
private:
void ConstructL(const TRect& aRect);
// from CCoeControl
void Draw(const TRect& aRect) const;
};

Hàm ConstructL() cần truyền kích thước cửa sổ, control, vùng mà ứng dụng cần truy
cập. Bạn có thể sử dụng hàm ClientRect() để lấy kích thước vùng client của ứng
dụng, và hàm ApplicationRect() để lấy kích thước của vùng ứng dụng.
Bạn truyền AppUi()->ApplicationRect() trong hàm ConstructL() để truy cập tới
toàn bộ vùng màn hình. Dĩ nhiên bạn có thể tùy biến vùng kích thước này:
Mã:

void CStartContainer::ConstructL(const TRect& aRect)


{
CreateWindowL();
SetRect(aRect);
ActivateL();
}

- Hàm CreateWindowL() để tạo cửa sổ control


- SetRect() thiết lập phạm vi cửa sổ tới kích thước TRect.
- Hàm ActivateL() bảo cho framework biết rằng nó có thể truy cập vào màn hình.

Để vẽ lên màn hình, một ngữ cảnh đồ họa (Graphics Context - GC) được lấy ra. Và
tất cả việc vẽ đều thực hiện trong ngữ cảnh đồ họa này, CWindowGc.

void CStartContainer::Draw(const TRect& aRect) const
{
CWindowGc& gc = SystemGc();
gc.Clear(aRect);
...
}

- Hàm SystemGc() trả về ngữ cảnh đồ họa chuẩn cho biến gc. Và vùng control phải
được xóa trước khi bạn thực hiện thao tác vẽ trên nó.
- Hàm Draw(), nó là hằng chứ không phải là leave, vì thế bạn không thể tạo các phần
tử động trong hàm Draw() và bạn cũng không thể sửa trạng thái bất kỳ dữ liệu nào
trong hàm này. Sau khi bạn thay đổi trạng thái bạn muốn cập nhật lại vùng vẽ bạn nên
gọi hàm DrawNow(), hàm này sẽ tự động gọi hàm Draw() của chính nó.

Để thực hiện vẽ hình theo ý của bạn, bạn phải thiết lập các thông số bút vẽ trước khi
thực hiện vẽ. Dạng và kích thước của bút vẽ cũng có thể thay đổi và được sử dụng để
vẽ các hình. Đoạn mã sau thiết lập kiểu bút vẽ là dash và kích thước tới giá trị aSize:

gc.SetPenStyle(CGraphicsContext::EDashedPen);
gc.SetPenSize(aSize);

Kiểu liệt kê TPenStyle bao gồm:


- ENullPen
- ESolidPen
- EDottedPen
- EDashedPen
- EDotDashPen
- EDotDotDashPen
Đoạn mã sau sử dụng để vẽ đường thẳng và hình chữ nhật:
Mã:
gc.DrawLine(aPoint, bPoint);
gc.DrawRect(rect);

Bút vẽ sử dụng để vẽ các đường, còn chổi vẽ để tô các vùng. Kiểu liệt kê
TBrushStyle được sử dụng trong hàm SetBrushStyle. Màu của chổi vẽ được thiết
lập thông qua hàm SetBrushColor().

Các phương thức vẽ khác như DrawEllipse() và DrawPolygon(), chúng sử dụng chổi
vẽ để tô vùng và bút vẽ để vẽ đường biên.

Sử dụng font và vẽ chữ trên Symbian

+ Hiển thị chữ


Theo lý thuyết việc vẽ xâu văn bản lên màn hình là rất đẹp nhưng việc định dạng có
thể là một phiền toái.
Mã:
_LIT(KExampleText, "Chocolate Cake"));
gc.DrawText(KExampleText, TPoint(80,40));

Tham số vị trí là tương đối với Control vì vậy TPoint(0,0) là góc trên bên trái vùng
màn hình của control. Vị trí này có thể khác nhau trên các thiết bị khác nhau vì vậy
bạn phải rất cẩn thận. Nhưng thật may mắn có một số hàm như hàm
CGraphicsContext::ECenter giúp can lề giữa đoạn văn bản. Một vài hàm khác giúp
bạn thay đổi font và màu của văn bản.

Để thay đổi màu chữ khi vẽ bạn sử dụng hàm SetPenColor():

gc.SetPenColor(KRgbRed);

Có một số màu được định nghĩa trước có kiểu TRgb:

KRgbBlack
KRgbDarkGray
KRgbDarkRed
KRgbDarkGreen
KRgbDarkYellow
KRgbDarkBlue
KRgbDarkMagenta
KRgbDarkCyan
KRgbRed
KRgbGreen
KRgbYellow
KRgbBlue
KRgbMagenta
KRgbCyan
KRgbGray
KRgbWhite

Lớp TRgb cho phép bạn tạo màu theo ý mình:


- Hàm khởi tạo: Ví dụ như TRgb(0x00112387), TRgb(50, 50, 0)
- Hàm thay đổi màu: như SetRed() …
- Hàm thay đổi số lượng màu: như Gray16()

Để thay đổi font chữ bạn sử dụng đoạn mã:

const CFont* font = iEikonEnv->AnnotationFont();


gc.UseFont(font);

Có một số font được định nghĩa trước, bao gồm:

Để lấy font bình thường (mặc định) bạn sử dụng lệnh:

gc.UseFont(iCoeEnv->NormalFont());

Nhớ rằng một khi bạn hoàn thành việc sử dụng font, bạn phải gọi hàm:

gc.DiscardFont();

Việc bố trí các phần tử trên màn hình với các font khác nhau là công việc khá phức
tạp. Một lựa chọn đó là không sử dụng cách vẽ trực tiếp mà sử dụng lớp CRichText.
Lớp này giúp bạn dễ dàng định dạng chữ và chèn ảnh.

+ Lựa chọn font chữ trong Symbian OS


Để lựa chọn font chữ bạn làm các thao tác sau:
- Khởi tạo đối tượng CFont
- Thiết lập các thông số cho font (Thông qua cấu trúc TFontSpec)
- Gán CFont tới font sẵn có gần nhất để cấu trúc TFontSpec phù hợp với màn hình
thiết bị
- Thiết lập làm font hiện tại (Ngữ cảnh đồ hoạ sử dụng font này) để vẽ (Sử dụng hàm
UseFont)
- Vẽ text
- Loại bỏ font khỏi ngữ cảnh đồ hoạ
- Loại bỏ font bằng hàm CGraphicsDevice::ReleaseFont().

Sau đây là đoạn mã ví dụ về lựa chọn Font:

_LIT(KFontName, "Verdana");
CFont* iUseFont = NULL;
TFontSpec myFontSpec(KFontName, 18);
TFontStyle myFontStyle(EPostureUpright, EStrokeWeightNormal,
EPrintPosSubscript);
myFontSpec.iFontStyle = myFontStyle;
iCoeEnv->ScreenDevice()-
>GetNearestFontInPixels(iUseFont,myFontSpec);
iFbsGc->UseFont(iUseFont);
_LIT(KTextExample, "\x43h\xE0o m\x1EEBng \x62\x1EA1n th\x61m
\x64i\x1EC5n \x111\xE0n MyShop\x34Vn"); // Xâu "Chào mừng bạn
tham diễn đàn MyShop4Vn"
iFbsGc->DrawText(KTextExample, TPoint(5,80));
iFbsGc->DiscardFont();
iCoeEnv->ScreenDevice()->ReleaseFont(iUseFont);

Chú ý:
- Để có thể sử dụng font Verdana và hiển thị được tiếng việt bạn phải copy hai tệp
verdana.ttf và FREETYPE.DLL vào thư mục c\system\fonts.
- Hàm UseFont khá chậm nên bạn nên dùng nó ngay trong hàm khởi tạo. Khi sử dụng
hàm này, thì phải dùng thêm hàm DiscardFont() để loại bỏ font khỏi ngữ cảnh đồ
hoạ. Nếu bạn sử dụng nhiều font vẽ cùng lúc thì tốt nhất bạn nên sử dụng hàm
UseFontNoDuplicate (iFbsGc->UseFontNoDuplicate((const CFbsBitGcFont *)
iUseFont);), khi sử dụng hàm này bạn ko cần sử dụng hàm DiscardFont nữa.

+ Tự động load và unload tệp font *.gdr


Nhiều khi ứng dụng của bạn sử dụng bộ font chữ riêng *.gdr do bạn tự tạo, khi đó mỗi
khi khởi động ứng dụng bạn tự động load font này và mỗi khi kết thúc ứng dụng của
bạn, bạn phải unload nó. Giả sử bạn có font chữ với tên là "My Font" và tên tệp font
là "myfont.gdr" và bạn để nó trong thư mục "c:\data".

Đầu tiên bạn phải khai báo các biến cần dùng (nên là biến thành viên của lớp AppUi):

_LIT(KFontName, "My Font");


_LIT(KFontFile, "c:\\data\\myfont.gdr");
...
TInt iFontID;
CFont* iUseFont;

Khi khởi động bạn load font này lên bằng đoạn mã:
TFileName fntFilename(KFontFile);
TInt nRes = CEikonEnv::Static()->ScreenDevice()-
>AddFile(fntFilename, iFontID);
if (nRes==KErrNone)
{
// Load được font
// Tạo biến CFont để sử dụng font này
TFontSpec myFontSpec(KFontName, 12);
TFontStyle myFontStyle(EPostureUpright, EStrokeWeightNormal,
EPrintPosNormal);
myFontSpec.iFontStyle = myFontStyle;
iCoeEnv->ScreenDevice()-
>GetNearestFontInPixels(iUseFont,myFontSpec);
}
else
{
// Không load được tệp font
// ...
}

Bây giờ bạn có thể sử dụng font chữ này vẽ bình thường. Khi kết thúc ứng dụng, bạn
phải unload font này khỏi hệ thống bằng đoạn mã:

if (iUseFont)
iCoeEnv->ScreenDevice()->ReleaseFont(iUseFont);
if (iFontID!=0)
CEikonEnv::Static()->ScreenDevice()->RemoveFile(iFontID);

* Chú ý:
- Bạn có thể dùng tiện ích "Easy GDR Creator" để tạo font gdr.
- Bạn có thể dùng tiện ích "KVT Symbian Font Converter" để chuyển từ font *.ttf
sang *.gdr
- Các tiện ích này bạn tìm trên mạng, rất nhiều và rất dễ tìm
- Hiển thị bằng font *.gdr thực sự không được đẹp lắm
- Bạn có thể dùng đoạn mã trên để load và hiển thị font *.ttf. Tôi đã thử trên dòng
Symbian S60 3rd, chạy rất okie.

+ Liệt kê các font sẵn có trong hệ thống


Nhiều lúc bạn muốn xem hệ thống hiện tại đã có những font gì, hay bạn muốn xem
font của bạn đã thực sự được load vào hệ thống hày chưa. Như vậy bạn cần liệt kê
được các font sẵn có trong hệ thống. Đoạn mã sau giúp bạn làm điều này:

typedef TBuf<256> MyFontName;


TInt iFontNum = 0;
CArrayFix<MyFontName>* iArrFonts = NULL;

iFontNum = iCoeEnv->ScreenDevice()->NumTypefaces();
if (iFontNum>0)
iArrFonts = new(ELeave)CArrayFixFlat<MyFontName>(iFontNum);
if (iArrFonts)
{
MyFontName myFontName;
TTypefaceSupport myTypefaceSupport;
for(TInt i=0; i<iFontNum; i++)
{
iCoeEnv->ScreenDevice()-
>TypefaceSupport(myTypefaceSupport, i);
myFontName = myTypefaceSupport.iTypeface.iName.Des();
iArrFonts->AppendL(myFontName);
}
}

// Sử dụng mảng iArrFonts ở đây


// ...

// Xóa mảng cấp phát


if (iArrFonts)
delete iArrFonts;

Toàn bộ font tìm được sẽ lưu vào mảng iArrFonts để tiện cho việc sử dụng. Sau khi
liệt kê bạn có thể hiển thị bằng cách vẽ lên màn hình hoặc hiển thị bằng hộp thoại
Information.

Xử lý phím nhấn

Có nhiều cách khác nhau để tương tác với các ứng dụng trên điện thoại di động. Hình
sau thể hiện một số cách mà người dùng có thể tương tác với điện thoại di động:

Phương thức vào thông dụng là thông qua các phím trên điện thoại, bao gồm ba vùng
phím:
- Keypad: Các phím số từ 0 đến 9, phím *, #
- Soft keys
- Navigation key: Phím định hướng, bao gồm phím lên, xuống, trái, phải và phím OK

Ngoài ra người dùng có nhiều cách khác để tương tác với điện thoại. Vì điện thoại
được thiết kế với mục đích chính là đàm thoại, vì thế giọng nói là phương thức vào
chính. Gần đây các nhà phát triển quan tâm tới các ứng dụng khởi động bằng giọng
nói và chắc chắn các ứng dụng loại này sẽ trở thành thông dụng trong tương lai không
xa. Ngoài ra, sử dụng hồng ngoại cũng như Bluetooth cũng cho phép vào ra dữ liệu
với điện thoại. Ở đây chúng ta quan tâm tới phương thức vào thông dụng nhất, đó là
sử dụng phím.

Trong lớp CoeControl có cài đặt một phương thức ảo OfferKeyEventL() để điều
khiển sự kiện nhấn phím, nó được framework gọi mỗi khi sự kiện nhấn phím xảy ra.
Vì vậy để bắt sự kiện phím nhấn, bạn phải kế thừa từ lớp CoeControl và định nghĩa
phương thức OfferKeyEventL().

#include <coecntrl.h>
class CKeyPressContainer : public CCoeControl,
MCoeControlObserver
{
...
TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,
TEventCode aType);
...
}

Khi sự kiện phím xảy ra, phương thức OfferKeyEventL() của tất cả các điều khiển
trong stack được gọi. Khi phương thức này được gọi, nó có thể xử lý sự kiện hoặc bỏ
qua nó thông qua giá trị trả về của hàm này: Trả về TKeyResponse:
EKeyWasNotConsumed nếu sự kiện bỏ qua và trả về EKeyWasConsumed nếu sự
kiện được xử lý.

Khi sự kiện phím xảy ra, đầu tiên bạn phải kiểm tra xem đó là sự kiện phím nào: phím
nhấn (Key down), phím nhả (Key up) hay sự kiện nhấn phím (Key press). Nếu bạn
chỉ quan tâm tới sự kiện phím nhấn, bạn có thể sử dụng đoạn mã:

TKeyResponse CKeyPressContainer::OfferKeyEventL(const
TKeyEvent& aKeyEvent, TEventCode aType)
{
if (aType != EEventKey)
return EKeyWasNotConsumed;
else
{
if (aKeyEvent.iCode == '5')
{
...
return EKeyWasConsumed;
}
}
}

Tiếp theo bạn phải kiểm tra các tham số của sự kiện phím trả về thông qua tham số có
kiểu TKeyEvent, đây là cấu trúc gồm 4 trường:
- iCode (định nghĩa trong TKeyCode): Mã của phím nhấn trong sự kiện Key Press.
- iModifiers (định nghĩa trong TEventModifier): Trạng thái phím sửa và thiết bị trỏ
- iScanCode (định nghĩa trong TStdScanCode): Mã scan của phím, sử dụng trong
các sự kiện Key Down và Key Up.
Bạn cũng có thể sinh ra một sự kiện phím bằng cách gán các trường trong cấu trúc
TKeyEvent, sau đó gọi hàm OfferKeyEventL() để giả sự kiện:

TKeyEvent myEvent;
myEvent.iCode = EEventKeyDown;
myEvent.iModifiers = EAllModifiers;
myEvent.iRepeats = 0;
myEvent.iScanCode = EAllModifiers;
OfferKeyEventL(myEvent, EEventKey);

Sự kiện cho Menu

Phương thức HandleCommandL() được sử dụng để điều khiển menu chọn và các
lệnh khác được định nghĩa trong tệp resource. Nó là hàm ảo định nghĩa trong
CEikAppUi (hoặc CAknAppUi trong Symbian S60 3rd) và được gọi bởi phương
thức ProcessCommandL() trong Framework. Nơi thường được sử dụng để cài đặt
phương thức này là trong lớp AppUi (kế thừa từ CEikAppUi hoặc CAknAppUi).

void CKeyPressAppUi::HandleCommandL(TInt aCommand)


{
switch (aCommand)
{
case EEikCmdExit:
{
Exit();
break;
}
case EAknSoftkeyBack:
{
ActivateLocalViewL(KMainViewId);
break;
}
case EStartGame:
{
iContainer->StartGame();
break;
}
}
}

Còn nhiều phần quan trọng khác tôi không thể nói hết ở đây, bạn xem thêm cuốn
"Wiley S60 Programming - A Tutorial Guide (Apr - 2007).pdf" mà tôi giới thiệu
ngay ở bài viết đầu tiên.

Một số lỗi liên quan đến lập trình và cài đặt trên Symbian
Khi lập trình hay cài đặt phần mềm đôi khi bạn gặp những rắc rối, mà bạn không biết
tại sao. Sẽ mất thời gian để bạn phải tìm hiểu nguyên nhân, hay serch trên mạng để
tìm cách giải quyết. Khi lập trình và cài đặt các phần mềm trên Symbian tôi cũng gặp
nhiều rắc rối, xin đưa ra để nếu các bạn có gặp phải sẽ không phải mất nhiều thời gian
để giải quyết.

* Một vài chú ý khi lâp trình với Symbian OS


- Nếu bạn muốn sử dụng thư viện toán học math.h trong thư viện chuẩn thì bạn phải
thêm dòng
SYSTEMINCLUDE \epoc32\include \epoc32\include\libc trong tệp mmp (Chú ý
phải cập nhật lại tệp này trước khi build).
- Khi build GCCE, nếu báo lỗi "undefined reference to `atan2`", lỗi này là do bạn
sử dụng thư viện toán học nhưng lại chưa đưa lib “estlib.lib”. Nếu bạn không khai
báo thư viện này thì vẫn chạy được máy ảo nhưng build cho máy thật lại bị lỗi. Sửa
lỗi này đơn giản là bạn khai báo thêm thư viện “estlib.lib”.
- Trong Symbian S60 3rd, nếu bạn nhận được thông báo “*** missing separator.
Stop.” tại một dòng nào đó trong tệp *.GCCE thì lỗi này do bạn chưa cài “CSL Arm
Toolchain” hoặc đường dẫn khai báo tới chương trình này trong biến môi trường
PATH không đúng. Đường dẫn mặc định của chương trình này là “C:\Program
Files\CSL Arm Toolchain\bin”. Sau khi cài xong, bạn đóng Project và mở lại, dịch
Release là được.
- Khi bạn dịch trên Symbian S60 3rd mà gặp lỗi báo: “error PRJ0019: A tool
returned an error code from "Building help file"”. Thì lỗi này là do bạn cài Perl
phiên bản không đúng (chẳng hạn như ActivePerl-5.8.8), bạn remove phiên bản Perl
này và cài lại phiên bản ActivePerl-5.6.1
- Thiết lập bàn phím QWERTY cho máy ảo trên Symbian 9: Hiện nay các con
E61 hay E90 có hỗ trợ bàn phím Qwerty. Để tiện lợi cho việc test ứng dụng trên con
này, bạn có thể thiết lập bàn phím Qwerty cho máy ảo. Để thiết lập, rất đơn giản bạn
chỉ cần vào thư mục “\Symbian\9.1\S60_3rd\Epoc32\Data”, edit tệp “epoc.ini” rồi
thêm dòng “configuration epoc_352x416_qwerty.ini”, bạn save lại, rồi bật máy ảo
là okie.
- Khi dịch Project Symbian S60 1st sử dụng VC nếu báo lỗi “This project does not
support platform or program "VC6””, lỗi này là do bạn cài SDK cho S60 1st rồi
lại cài SDK cho S60 3rd. Để khắc phục lỗi này bạn remove SDK S60 1st và cài lại
nó, sau đó [b]Restart[/b] lại. Để không gặp lỗi này khi cài đặt tốt nhất là bạn cài đặt
SDK S60 3rd trước rồi sau đó mới cài đặt SDK S60 1st.
- Khi build xong bạn ấn Ctrl+F5 mà máy ảo không nên được là do đường dẫn tới file
“EpocWrapper.exe” (trên S60 3rd) hoặc "epoc.exe" (trên S60 1st) không đúng.
Trên S60 3rd, bạn vào Properties của Project, ở mục Debugging, thay đổi lại đường
dẫn chính xác trong trường Command. Còn trên S60 1st, bạn chỉ đến đúng tệp
"epoc.exe" là okie (chú ý debug và release thì bạn trỏ tương ứng nhé)

* Một số lỗi khi cài đặt phần mềm (đặc biệt trên Symbian S60
3rd)
- Certificate may not yet be valid , is expired or phone's date setting may be
incorrect: Lỗi này thường gặp khi bạn mới format máy khi gặp lỗi này các bạn chỉ
cần chỉnh lại ngày giờ cho chính xác với thời điểm hiện tại.
- not support: Lỗi nó là phần mềm bạn cài ko phải là của s60v3 mà của s60v2 hay là
phần mềm của của các loại symbian khác (uiq, s90..), nhưng cũng có trường hợp do
các files có tên quá dài chấm nhiều chấm quá nên máy ko hiểu và báo lỗi, nếu báo lỗi
này thì trước tiên các bạn thử đổi tên nó ngắn gọn lại nhé và cài thử lại xem có được
ko.
+ File Corupted: Là files đó đã bị lỗi bạn nên tải lại files khác.
+ error update: Symbian OS 9.1 ko cho bạn update vesion của soft đã CR, bạn nên
xóa bỏ soft trong máy và cài lại sẽ ok, muốn update phải là ứng dụng ko CR (đăng ký
= chìa khóa).
+ Certificate error contact the application supplier: Lỗi này báo là pm này chưa
được signsis (đăng ký).
+ Certificate exprired: Lỗi "chứng chỉ hết hạn" này xảy ra đối với 1 số dòng máy
mới & firmware mới, bạn có thể đổi ngày giở lui lại khoảng 1 vài tháng, nhưng cũng
có 1 số soft ko cài được -> chỉ có cách là đợi vesion mới của soft được CR mà cài.
Đối với dòng E (E65, E61 ....): thì bạn vào app manager -> software intall -> chọn
all la ok.

You might also like