电脑技术学习

编写安全的Symbian C++游戏代码

dn001

  Symbian游戏是运行在手机上的游戏,它不能干扰手机正常的通讯功能,对操作系统和其它应用程序必须友善。而在首次编写Symbian C++游戏时,我遇到了无数奇怪的问题,其中大部分问题出在内存极端不足,打开太多应用程序,屏幕保护探出,接到短信、电话等特殊情况下。

本文献给使用Nokia Symbian 60 SDK各个版本开发游戏软件的程序员。虽然本文主要是针对游戏软件,但是大部分内容对一般应用软件也同样适用。

1.1.;声明

为了避免良心的谴责,首先我必须承认一点,我本人并不是靠Symbian C++糊口。除了forum.nokia.com上的文章和SDK,我也没有看过任何关于Symbian的书籍。只是偶然的,我在天津猛犸游戏公司(www.mammothworld.com)认识并接触了Symbian。我从零起步,写出了一个蹩脚的Symbian游戏引擎并在3650、7650上开发了一些游戏。所以我对Symbian的掌握完全是出于自己的猜测和理解,虽然本文缺乏权威,但至少都是经验之谈,容易理解。

1.2.;概述

Symbian游戏是运行在手机上的游戏,它不能干扰手机正常的通讯功能,对操作系统和其它应用程序必须友善。而在首次编写Symbian C++游戏时,我遇到了无数奇怪的问题,其中大部分问题出在内存极端不足,打开太多应用程序,屏幕保护探出,接到短信、电话等特殊情况下。
其实如果养成严谨的代码风格,进行足够的错误处理,大部分问题本可以避免。为了解决它们,我很是花了一番功夫,所以在此把我的一些教训、经验写出,希望大家能避免犯同样的错误。
如果你不是业余爱好者,而是为一个认真的开发商工作,特别是如果你的产品需要通过Symbian Signed认证(www.symbiansigned.com),你就必须更加小心的对待本文提出的问题。
Symbian Signed是一个针对Symbian应用程序的认证,想要通过它,你的应用程序必须通过一系列严格的测试。认证对应用程序的文件管理、内存使用、系统事件响应、网络、资费和私人数据等都有一定的要求。如果想了解Symbian Signed认证的详细内容,可以去它们的网站下载白皮书。

1.3.;异常处理

虽然我们都知道任何一个new (ELeave)或者带有L后缀的函数都可能抛出异常,但是很多业余的爱好者还是会忽视Symbian C++中异常处理的重要性。虽然有些函数只有在极其罕见的情况下才会抛出异常。但不是危言耸听,如果你不写代码捕捉并处理它们,应用程序就会遇到"系统错误"。
普通C++使用throw抛出异常。异常抛出后,栈会不停回滚,直到遇到最近一层catch为止。Symbian C++中的异常处理不使用try-catch和throw。但是它的处理机制和标准C++很是类似,区别仅仅是它只能抛出一个整数错误码,而不是一个任意对象。
我将从异常的抛出、捕捉、处理三方面讲解这部分内容。

1.3.1.;抛出异常
Symbian C++中,有下面几种情况下会抛出异常:
;;使用静态函数User::Leave抛出异常。这个函数就是最基本的异常产生函数。下面讲的其它抛出方式都可以转化为User::Leave。
;;使用静态函数User::LeaveIfError把错误码转化为异常。有些函数比如CFbsBitmap::Create( )有一个TInt的返回值。如果遇到错误,这些函数就会返回非KErrNone的错误值。此时,可以使用LeaveIfError把这个返回值转化为异常。比如:User::LeaveIfError(bmp->Create(iSize, EColor4k); 其实LeaveIfError就是if (returnValue != KErrNone) User::Leave(returnValue);
使用new (ELeave)申请内存。如果没有足够内存可用,此操作产生一个KErrNoMemory异常。比如TText8* p = new (ELeave) TText8[20]; 相当于TText8* p = new TText8[20]; if (p == NULL) User::Leave(KErrNoMemory);
;;调用带有L后缀的函数。Symbian系统的命名规范中要求,每一个可能Leave的函数都要有后缀L。包含有带L的内层函数调用的外层函数也必须加上L。这类函数中最常见的就是NewL,NewLC和ConstructL。这个规范比你想象的要重要。因为它给其他程序员一个暗示,提示他们对这些函数进行保护。

1.3.2.;捕捉异常
类似标准C++的catch语句,Symbian C++的TRAP关键字可以对一个可能产生异常的函数进行保护,并且捕获到异常值。比如:
TInt errorCode;
TRAP(errorCode, SomeDangerousFuncL( ) );;// 保护执行SomeDangerousFuncL( )函数
if (errorCode != KErrNone)
{
;// 捕捉到了一个异常,在这里添加处理异常的代码
}
类似的TRAPD省去了你声明一个局部变量的麻烦。头两行代码可以简写成:
TRAPD(errorCode,SomeDangerousFuncL( ) );

1.3.3.;处理异常
对于不同的异常当然有不同的处理方法(废话:-))。我们以最常见的捕获到代表内存不足的KErrNoMemory异常为例讲解。
注意在Container,AppUi等类的构造过程中,你不需要加入对内存不足的保护。因为这一切系统已经为你做好了。系统会弹出一个对话框报告内存不足。根据你的操作系统版本不同,这可能是中文的,也可能是英文或者其它语言的。如果你不信,可以在AppUi或者Container的ConstructL中写一行User::Leave(KErrNoMemory)试试看。;