现在的位置: 首页 > 综合 > 正文

Symbian经验小总结

2013年09月08日 ⁄ 综合 ⁄ 共 31578字 ⁄ 字号 评论关闭

本文转自网络,是诺基亚手机操作系统Symbian的入门资料,有兴趣搞手机开发的可以看一看。

Symbian是一个操作系统,C++是其原生编程语言。它为软件开发人员提供了操作系统的全面功能。当然对C++的熟悉程度直接影响了开发人员对其的使用,因为Symbian OS下的开发会广泛运用C++的高级特征和面向对象设计。我们暂且认为大家对C++都有了相应的了解,而且已经亲身经历了某些项目开发,因此,以后文中如使用到C++范畴中的概念词汇,不会对其做特殊解释说明。

1. 了解Symbian OS
目前,市场上的Symbian Os手机都是基于3种针对C++程序员开发的用户界面:Nokia Series 80、60以及UIQ(当然还有几年之前的当红明星s40)所有这些都支持Java编程,因此,C++也就不显得那么孤单。基于Series 60平台的机器像Nokia 3650、7650,Nokia 9210则使用的是S80平台,这类手机显著的特点就是具有完整的手机键盘,完全可以模拟pc操作;UIQ平台的手机一般屏幕比较大,而且可配备一数字小键盘,在移动时可使用触笔交互,像Sony Ericsson P800。说这么多,其实无论是什么手机平台,关键之处就是了解Symbian下开发的主要特征。以下慢慢逐一道来
2. 准备工作
下载相应的sdk:可以从Nokai的开发论坛上下载http://forum.nokia.com.cn/sch/index.html
可以根据自己的需要选择下载sdk的版本。
使用Symbian OS还需要两个工具就是Java的运行库和ActivePerl,这两个工具也有版本的区别,需要你在下载之前了解相应的版本需求。
我所使用的如下(下面文中如未加特殊声明都是在此环境下开发)
window 2000 sp4、vc++6.0
------------------------------
Java2Run Time.SE                             v1.3.1                         v1.4.1_02
ActivePerl                                       build  518                       5.6.1
Series 60 SDK                                    2.1                               2.2Beta
------------------------------
注意:安装顺序本人一般采用Java runtime/ActivePerl/Series 60sdk,而且需要注意的是ActivePerl这个编译工具最好装在C:/下(一般我采用的就是不论系统盘是什么,所有的都装在C:/下,一来比较容易管理,二来省去了很多不必要的麻烦)!

安装完毕,接着,我们就来熟悉Symbian的各个目录吧。
首先在使用过程中,感觉Symbian OS Sdk处理盘符和目录时实在不便,所以在开发过程中很多麻烦都来源于盘符和目录问题。因为我采用的开发工具是VC++6.0 英文版,所以为了开发方便,可以这么做:将目录../Symbian/7.0s/Series60_v21_C/Series60Tools/appwizard中的文件拷贝到VC的安装目录../Microsoft Visual Studio/Common/MSDev98/Template中,这样做的好处就是在你使用VC时,当使用new命令新建一个项目时,Series 60就会出现在Appwizard对话框的项目栏中。

下面我们来了解一下一个Symbian OS项目的目录结构:
大家首先要熟悉的是BUILD目录,这个目录是专门为存放各种不同平台的 工程文件的。大家执行了abld makefile vc6或者abld makefile cw_ide 之后就会相应的在这个目录下生成ms vc6或者cw的工程文件,注意,cw的 工程文件是一个xml。要在cw里面通过import...导入。

第二个目录是data目录。这个目录存储编译之后UI程序的资源文件rss。 Series 70 SDK一般把rss拷贝到/epoc32/data/z/system/apps/工程目录名 中。大家如果想使用MMC卡测试你开发好的程序,应该从上述目录中拷贝 *.rss与你的*.app一同放到MMC卡中的/system/apps/工程目录名 中。

第三个目录是gcc这个目录下存放的大多都是系统需要的资源,像一些.lib和.h,这个一般我们不需要过多关心。

第四个目录是include这个目录下多数都是系统的头文件,当然,系统在build UI程序的时候会把xxx.rsg文件放到这个目录下, 这个文件中大家用UE看一下就知道了。还有一个子目录就是 include 下面的libc。这里面封装了标准C的兼容函数库。

第五个目录是release。这个目录主要存放各个平台相关的模拟器和库。Windows下模拟器的位置:
/epoc32/release/wins/udeb/epoc.exe
以及
/epoc32/release/wins/urel/epoc.exe
同时编译之后的可执行文件也会被存放在armi,thumb的目录下。

第六个目录是wins下面一般只有c一个目录。这个目录表示手机的存储器。S70 的 MMC 卡 目录是d。可是你就算建了个d目录,你的S90 模拟器也认不出来,你还需要 改data目录下的一个epoc.ini文件。加入下面这行:
_epoc_drive_d /epoc32/wins/d ,然后S70的模拟器就可以认出d盘,也就是MMC卡了。

接下来是工程目录,Symbian OS下工程目录和VC里工程的目录结构不同,看下面的示例及介绍:一般说来项目中一般有如下folder
helloworldbasic
├─group              通常放组件定义文件、项目定义文件,编译时一定来这里
├─inc                  工程项目的头文件
├─sis                   .pkg,用于制作安装包
└─src                  工程项目的源文件,.cpp
对Symbian OS下工程的管理一般都是在上述文件夹中完成的。比如:
编译工程:首先在vc里新建一个symbian s60项目,假设是c:/sample,打开命令提示符,进入到目录c:/sample/group中,键入命令
bldmake bldfiles
这个命令会在group目录下生成一个abld.bat的批处理文件,并且会在../Symbian/7.0s/Series60_v21_C/Epoc32/BUILD下生成相应工程的文件夹里面还有很多make文件,接着键入
abld build wins udeb
../Symbian/7.0s/Series60_v21_C/Epoc32/Release/wins/UDEB目录下生成我们的sample,当运行模拟器时,我们的工程就显示出来了。这个命令里wins代表的是windows系统下模拟应用,如果是真实Symbian设备,这个参数还可以是armi或thumb;最后一个udeb代表Unicode-debug版当然urel就是Unicode-release版了。
最后键入命令
abld makefile vc6
这个命令会产生vc6使用的.dsw和.dsp文件,这样我们就可以在vc++6.0下进行编码了。
-----------------------------
说了一堆,感觉要说的实在太多,由于比较零乱,所以大家要自己认真尝试理解。一次也没有办法说完,以后我会陆续添上余下部分。请多多支持!
                                                     
                                        --风小云原创,转载请标示出处

新建Symbian project后,项目文件夹中您会看见很多陌生后缀:.mmp、.hrh... ...编程过程中有会遇到很多陌生前缀:
R-、T-... ...这里会对他们一一讲解。                                 --风小云
---------------------------------------
正如接触篇中所述,Symbian Os下新建项目包含多个文件夹,具体作用参照《Symbian入门指南-接触篇》。下面主要对几个重要的文件做一下介绍:

1. ../group/*.mmp(项目定义文件)
Symbian Os下开发项目要针对不同环境来生成程序,如果运行于仿真器(X86指令集),我们使用调试生成,因此可以进行相关调试;如果针对目标手机,则使用ARM指令集,因此发布的代码当然有所不同。因为每种类型的生成均需要不同的项目文件。为了简化,就建立了一个一般性的项目定义文件,然后针对不同的生成环境,将该文件翻译成相应的项目文件。下面有个简单的sample:
//hello.mmp
TARGET             Hello.exe
TARGETTYPE       exe
SOURCEPATH         .
UID                           0
SOURCE             hello.cpp
USERINCLUDE        .
SYSTEMINCLUDE   /epco32/include
LIBRARY                 euser.lib
上面信息足够定义了整个项目
TARGET 指定生成的可执行文件
TARGETTYPE确认该文件是exe      
SOURCEPATH项目源文件的位置
UID于.exe无关,主要用于GUI程序
SOURCE源文件信息
USERINCLUDE       
SYSTEMINCLUDE 上面两个参数指定用户搜索目录  
LIBRARY需要链接的库信息,运行时需要调用的

2. ../group/bld.inf (组件定义文件)
Symbian Os生成工具还需要提交一个组件定义文件,这个文件的名字是bld.inf,其中包含所有项目定义文件的列表,多数情况下只有一个文件。有些时候还会包含更多与生成相关的信息。
//BLD.INF
PRJ_MMPFILES
hello.mmp

3. ../inc/*.hrh
扩展名为.hrh的文件被设计用来包含在C++程序和资源脚本中,C++程序需要它们来标示被处理的命令,而资源脚本需要它们来指示发出的命令。它包含应用程序命令的枚举常量。
enum THelloIds
    {
    EHelloCommand1 = 1  // start value must not be 0
    };

4. ../group/*.rss (资源文件)
用来定义资源,比如字符串R_HELLO_TEXT_ITEM1,枚举变量EHelloCmd1,UI的菜单以及快捷键等。
让我们看看整个资源文件.rss:

NAME HELL

#include <eikon.rh>
#include <avkon.rh>
#include <avkon.rsg>
#include "Hello.hrh"

NAME语句中提供的名字用于生成文件中每个资源的部分ID。如果应用程序使用多个资源文件,则必须确保名字不同,不致于使不同文件的资源间产生ID冲突。
#include语句载入资源文件内使用的结构和常量的定义。

定义快捷键和菜单
应用程序快捷键在HOTKEYS资源中定义,由符号ID r_hellogui_hotkeys标示,它与EIK_APP_INFO资源中给出的符号ID配合使用。
(一般不会为UIQ或S60应用程序指定快捷键,因为这些设备没有键盘)

菜单例程如下:
RESOURCE MENU_BAR r_hello_menubar
    {
    titles =
        {
        MENU_TITLE
            {
            menu_pane = r_hello_menu;
            }
        };
    }

RESOURCE MENU_PANE r_hello_menu
    {
    items =
        {
        MENU_ITEM
            {
            command = EHelloCommand1;
            txt = "Hello";
            },
        MENU_ITEM
            {
            command = EAknSoftkeyExit;         //命令ID
            txt = "Exit";                      //命令文本
            }
        };
    }
每个菜单项与某条命令和某些文本相关联,如果相添加相应菜单项,只需照上面的代码新加MENU_ITEM即可。

字符串资源
RESOURCE TBUF r_default_document_name
    {
    buf="";
    }
正确构建Symbian OS应用程序应当让它的所有可移植字符串资源位于资源文件中,使它们不必更改C++代码就可以移植字符串。

5. ../sis/*.pkg (包文件)
用于生成安装包,输入命令
makesys *.pkg
即可创建*.sis,可以将这个文件安装到手机上。
----------------------------------------------------
接下来讲述一下Symbian OS中应用程序开发的命名规范,说是规范其实说习惯更合适,像C++中全局变量使用m_..、宏变量和const常量多采用大写字母类似,在Symbian OS中开发应用程序也有一套常用的命名规范,而且比C++中更为严格。

类名
T类
TDesc、TPoint..
T类没有析构函数。它们的行为类似于内置类型。这是为什么所有内置类型的typedef都以T开头的原因。T类可以作为自动变量而分配、作为其他类的成员而分配,或位于堆上
C类
CActive、CBase..
从CBase派生的所有类。C类总是分配在默认堆上。分配对象时,CBase的operator new()将所有成员数据初始化为零。CBase也包括虚析构函数,因此通过对CBase*指针调用delete,正确析构它指向的任何对象
R类
RFile、RTimer..
拥有资源而不是位于默认堆上的所有类。通常分配为成员变量或自动变量;在少数情况,可分配在堆上。大部分R类使用Close()来释放资源
M类、接口
MEikmenuObserver..
由纯虚函数组成,不包含成员数据。
静态类
User、Math..
完全不能实例化的静态函数组成。这种类是库函数的容器

数据名
枚举常量
EMonday..
枚举中的常量,枚举本身应该具有T前缀,而枚举常量是枚举的成员;#defined常量有时也使用E前缀
常量
KRgbRed..
#defined类型或const TInt类型的常量
成员变量
iDevice、iX..
任意非静态成员变量
参数
aDevice..
声明为参数的任意变量,"a"代表参数
自动变量
device、X..
声明为自动变量的任意变量

函数名
Symbian OS里,首字母的意义不如末字母重大。
_L()
尾字母为L,表示异常退出函数,可能需要分配内存、打开文件等。一般来说,要进行一些由于资源不足或其他与环境相关的条件而可能失败的操作。调用这类函数时,总需要考虑异常退出时发生的情况。
_LC()
和L结尾的函数基本一致,只是分配对象后,将它推入清理栈(这个概念以后文章会讲述),如果函数失败,则异常退出。

还有一些函数的命名要求不是十分严格,可以仿照C++中的命名规范。
有关上述内容,读者可以自行在开发过程中去体会它们的实际用处,这里因为篇幅有限,没有办法很详细的一一论述,剩下的部分需要你自己去认真总结。

摘要:错误处理和清理工作对于Symbian OS环境下的开发显得尤为重要。错误处理(error handle)实际上是为了编写出更可靠的程序,而清理(cleanup)框架则是Symbian OS的编程基础,正因为如此,必须保证错误处理和清理非常有效且易于实现。

OOM(out-of-memory)错误在Symbian OS环境下则不得不谈。目前的台式机内存的容量越来越大,加上更大硬盘上创建的虚拟内存,而且使用者可能进行经常性的重启。在这样的情况下,内存耗尽很少发生。而在Symbian OS的环境下,RAM少到只有4MB,一般也不会超过16MB,使用时经常要求不必重启。所以我们在Symbian OS环境下开发要注意以下问题:

必须高效编程,使程序不会浪费RAM。
必须尽快释放资源,因为不可能不释放资源,让运行程序消耗越来越多的RAM。
必须处理内存不足错误。在Symbian OS中这类错误可以随机发生。
若出现内存不足错误,导致某些操作停止,一定不能丢失用户数据。
若某个要分配若干个资源的操作中途发生了内存不足,必须清理所有这些资源。

实际上Symbian OS的错误处理和清理框架并不只对OOM错误有效,很多其他操作因为其环境条件可能失败,比如读写文件、打开文件,在通信会话中发送和接受数据。也可以使用错误处理和清理框架来处理。

下面列举一些用于处理内存不足错误和测试OOM的工具:

最普通的调试键
    常用的Ctrl+Alt+Shift+(A使用堆单元、B文件服务器资源、C窗口服务器资源)
堆检查工具,用来检查函数分配的资源是否释放
C++的析构函数来销毁对象
堆失败工具,故意产生错误
异常退出机制,用来指示错误。其中一个基本函数User::Leave()
清理栈
异常捕获装置:用陷阱捕获异常退出的过程,类似与C++中的try-catch机制
CBase类,所有C类的基类,由清理栈来识别,包含一个C++析构函数
双阶段构造函数
命名约定

以上所述的工具接下来部分会做一介绍:
堆检查:
主要是使用宏_UHEAP_MARK和_UHEAP_MARKEND。
宏_UHEAP_MARKEND主要在析构函数中调用,此时在程序中任何位置如果包含了宏_UHEAP_MARK,则可以检查中间的堆操作是否平衡,就是说如果堆单元的数目和调用_UHEAP_MARK时数量不同,程序就会出错。
析构函数
想大家都不陌生,只是简单提一下
程序中使用过的对象,并不是所有的都要在析构函数中去释放,所销毁的只是自己所拥有的对象,也就不包括临时生成的局部变量。
堆失败工具
这个工具主要用来处理内存不足的错误,有个名为memorymagic的应用程序就是这个作用的,但不是所有的symbian系统都有相应的这个应用
具体使用方法可以到symbian的官方网站上下载这个东东,里边有相应的使用说明。
异常退出机制
当然就是使用User::Leave()来根据不同的错误码(请参照本blog中的Symbian错误码)异常退出,User::Leave()函数可以让活动函数的运行终止,并且接着终止所有调用函数。
清理栈
清理栈解决如下问题:清理栈在堆上分配的,但是指向它的唯一指针却是自动变量的对象。如果分配对象的函数异常退出,则需要清理对象。例程
case EMagicCmd:
{
    CX* x = new (ELeave) CX;
    CleanupStack::PushL(x);
    x->UseL();
    CleanupStack::PopAndDestroy(x);
}
上述例程无论UseL()运行是否异常,程序都可以正常退出,因为作为异常处理的一部分,弹出并销毁清理栈上的所有对象是必须完成的。当然你也可以这么做
case EMagicCmd:
{
    CX* x = new (ELeave) CX;
    TRAPD(error,x->UseL());
    if(error)
    {delete x;
     User::Leave(error);
    }
    delete x;
}

注意:没有必要不要使用清理栈
我们只需要使用清理栈来阻止越过对象的析构函数,如果该对象的析构函数一定会被调用,那么就一定不要使用清理栈。
一般来说类的成员变量往往都可以被类自身的析构函数销毁,所以绝对不应该把成员变量推入清理栈,避免二次删除!

两阶段构造
清理栈用于保存指向基于堆的对象的指针,以便发生异常退出时进行清理。这意味着,必须有机会来把对象推入清理栈。这个问题就可以利用两阶段构造来解决。

首先请记住C++的构造函数不应该包含任何可能异常退出的函数。例程:
class CY : public CBase
{
public:
CY();
~CY();
public:
CX* ix;
CY:CY()
{
ix = new(ELeave) CX;
}
CY:~CY()
{
delete ix;
}
}
调用
CY* y = new(ELeave) CY;
CleanupStack::PushL(y);
...
CleanupStack::PopAndDestroy(y);
这个时候在分配CY的过程中,同时要分配一个CX,由于构造函数CY()没有异常处理机制,因此一旦分配CX失败,则得到内存泄漏错误,因此需要利用一个完全独立的函数来实现这个。于是就定义了ConstructL()
class CY : public CBase
{
public:
~CY();
static CY* NewL();
static CY* NewLC();
void ConstructL();
public:
CX* ix;
CY:~CY()
{
delete ix;
}
}

void CY:ConstructL()
{
ix = new(ELeave)CX;
}
调用改为:
CY* y = new(ELeave) CY;
CleanupStack::PushL(y);
y->ConstructL();
...
CleanupStack::PopAndDestroy(y);
很显然这时ConstructL()能够安全清理。

一些经验小节:
对象不可分配两次, 不可删除两次。
delete ib;  //加上
ib = NULL;  //加上
ib = new CB;
不要删除非拥有对象
决不要从C++构造函数中异常退出。
使用好命名规范,帮助自己理解程序。
用好清理栈。
用好宏TRAPD()同User:Leave()。
-----------------------------------------------------------
总之,Symbian OS下的错误处理和清理是异常复杂和难以琢磨的,只有用我们细心加耐心去慢慢克服。

                                                                        --风小云原创

Symbian OS应用开发--文件和目录
在入门指南系列,我们介绍了有关Symbian OS的基础知识,接下来,我们将介绍更多理解如何编写程序的知识。
本篇文章的主要内容是介绍基于Symbian OS的手机,如何去操作有关文件和目录。也就是手机内的一些主要信息,比如:通讯录、记事和任务、sms信息、图片文件、声音文件...在介绍上述内容之前,首先,我们先简要了解一下Symbian OS的硬件系统,这些硬件直接关系到我们对其数据的具体操作。

与典型的台式计算机相比较,Symbian OS机器的相关硬件显然有更多的限制,给我们的软件开发带来了很多的不便,但同时也增加了更多的市场机会。
Symbian OS手机的主要部件:
一个CPU:Symbian Os的CPU是32bit的,运行的速度较低,现有的主要是基于190MHz和206MHz的strongARM CPU,有些则是基于ARM9。
一个ROM:ROM在我们的PC机上同样存在,但PC机上的ROM只包含一个很小的启动载入程序和BIOS,而在Symbian OS中ROM被映射成z:盘。ROM中的所有内容既可以作为z:盘上的文件夹来访问,也可以直接从ROM读取数据。Symbian OS上ROM大约是20MB的空间。
系统RAM:RAM主要用于活动程序和系统内核,另外作为用户使用的磁盘空间,作为c:盘访问,但一般机器的RAM也只不过8MB或16MB左右,所以很可能会耗尽RAM资源。
I/O设备:可能是手写输入的显示屏,紧凑式的键盘,或是内存卡(作为d:盘访问),或是其他连接端口,红外线和蓝牙...
电源:包括主电池以及外部电源。

有了上面Symbian OS部件的大致了解之后,就要谈下我们将要操作的信息数据:通讯录、记事和任务、sms信息、图片文件、声音文件...
这些信息数据主要都是存储在手机的RAM中,因此访问的时候可以通过访问c:盘的方式来进行。

下面我们集中探讨一下有关目录和文件的操作方式:
首先介绍一个类:TParse
用过的朋友都知道,这个类的主要功能就是处理路径,先看一段代码:
----------------------------------
_LIT(KPath,"c://new//meeting.wrd");
...
TParse  p;
p.Set(KPath,NULL,NULL);
p.Name();//gives "meeting"
p.NameAndExt();//gives "meeting.wrd"
...
----------------------------------
通过上面的例子,对TPase的功能应该有了一定的了解,对了这个类就是用来处理有关路径的各种信息采集的。当然,它还有合并两个路径的功能,如下:
----------------------------------
_LIT(KSpec,"A:file1");
_LIT(KRelated,"c://path1//related.xxx");
TParse  fp;
fp.Set(KSpec,&KRelated,NULL);
----------------------------------
上面操作的结果就是A:/path1/file1.xxx
提示:当我们需要提取指定路径的某些必要信息时,就需要我们使用TParse类来操作这个路径,从而达到目的,在处理路径时,这个类会经常使用!!

操作文件夹:
指定一个绝对路径
比如:指定_LIT(KBitmapFolder, "c://nokia//Images//Pictures//");
这个路径就可以代表手机存储图片文件的文件夹。
这种方式简单易用,而且在大多数nokia的手机上,往往路径都是一定的,所以这种方式是完全可行的。但是为了增强程序的可移植性,还是不要用绝对路径的好。因为我们有了第二种方法:
PathInfo类:
我们可以通过这个类获取当前设备的存储器路径。
例如:
根目录:
PathInfo::PhoneMemoryRootPath()
存储图片文件目录:
PathInfo::ImagesPath()
存储安装SIS文件目录:
PathInfo::InstallsPath()
存储声音文件目录:
PathInfo::SoundsPath()
如果想要定位MMC卡这种外加的存储器应该这样:
#include <PathInfo.h>
TFileName path = PathInfo::MemoryCardRootPath();
这种方式的效果更好,但是PathInfo这个类是在S60 2.0平台的,Symbian 6.1也就是S60 1.0平台却用不了!大家可以选择使用。

因为我们这篇文章涉及到的文件主要是图片和声音,因此只需要使用PathInfo::ImagesPath()和PathInfo::SoundsPath()来获得相应文件夹的地址。随后我们也就可以通过这个地址来操作相应的文件了。

操作文件:
Symbian OS下去操作文件主要是通过枚举所有特定文件夹下的文件以及查找指定的文件来完成的。
枚举某个文件夹下的文件:
-----------------------------------
TBuf<50>  path,tpath;
TBuf<256> filename;
RFs iSessionRFs;
CDir* dirList;
// Number, name and file size
_LIT(KStringSize,"%S%S");
User::LeaveIfError(iSessionRFs.Connect());
path = PathInfo::PhoneMemoryRootPath();
path.Append(PathInfo::PicturesPath());          //picture的文件夹
User::LeaveIfError(iSessionRFs.GetDir(path,     //读出文件夹下文件信息
 KEntryAttMaskSupported,
 ESortByName,
        dirList));
TInt j = dirList->Count();                      //文件数目
for (TInt i = 0;i<j;i++)
{       //添加你的操作
      filename.Format(KStringSize,&path,&(*dirList)[0].iName);
     CAknInformationNote* informationNote;
 informationNote = new (ELeave) CAknInformationNote;
 informationNote->ExecuteLD(filename);
}
delete dirList;
------------------------------------
通过上面的例程可以看出枚举文件夹中的文件主要是通过GetDir()函数来完成的,这个函数的介绍如下:
--------------------------------------------------------------------------------
GetDir()
TInt GetDir(const TDesC& aName,TUint anEntryAttMask,TUint anEntrySortKey, CDir*& anEntryList) const;
Description
Gets a filtered list of a directory's contents. The bitmask determines which file and directory entry types should be listed. The sort key determines the order in which they are listed.

Notes:

If sorting by UID (ESortByUid is OR'ed with the entry sort key), then UID information will be included in the listing whether or not KEntryAttAllowUid is specified in anEntryAttMask.

The function sets anEntryList to NULL, then allocates memory for it before appending entries to the list. Therefore, anEntryList should have no memory allocated to it before this function is called, otherwise this memory will become orphaned.

The caller of this function is responsible for deleting anEntryList after the function has returned.

Parameters
const TDesC& aName Name of the directory for which a listing is required. Wildcards may be used to specify particular files.
 
TUint anEntryAttMask Bitmask indicating the attributes of interest. Only files and directories whose attributes match those specified here can be included in the listing. For more information see KEntryAttMatchMask and the other directory entry details. Also see KEntryAttNormal and the other file or directory attributes.
 
TUint anEntrySortKey Flag indicating the order in which the entries are to be sorted. This flag is defined in TEntryKey.
 
CDir*& anEntryList On return contains a list of directory and file entries.

Return value
TInt KErrNone if successful, otherwise another of the system-wide error codes.
--------------------------------------------------------------------------------
上面介绍摘自Series 60 2.1 help。
主要使用方式就是用户给定一个path,也就是第一个参数const TDesC& aName,通过调用,该文件夹中的内容就会返回到第四个参数CDir*& anEntryList中,接下来这个list中存储的内容就是该文件夹下的文件信息,至于中间两个参数可以设置一些屏蔽信息,使得用户可以取出自己需要的文件信息。

查找相应文件:
-------------------------------------
    TFindFile file_finder(aSession); // 1
    CDir* file_list; // 2
    TInt err = file_finder.FindWildByDir(aWildName,aScanDir, file_list); // 3
    while (err==KErrNone)
        {
        TInt i;
        for (i=0; i<file_list->Count(); i++) // 4
            {
            TParse fullentry;
            fullentry.Set((*file_list)[i].iName,& file_finder.File(),NULL); // 5,6,7
            // Do something with the full Symbian OS filename
            DoOneFile(aSession, fullentry.FullName()); // 8
            }
        delete file_list; // 9
        err=file_finder.FindWild(file_list); // 10
        }
-------------------------------------
具体操作
1. 生成一个TFindFile对象.
2. 在赋值前FindWildByDir()将file_list初始化,所以这时list并没有分配空间.
3. 开始查找指定文件. 注意aWildName和aScanDir的使用,aWildName一般是完整文件名(例如, *.gdr),aScanDir则是相应目录,并不需要一个具体的盘符(例如, /System/Fonts/).
4. list中文件数目.
5. (*file_list)[i].iName表示找到文件的完整名字(e.g. Eon.gdr).
6. file_finder.File()表示文件的具体盘符以及路径(for example Z:/System/Fonts/).
7. TParse::Set()可以完成合并.
8. TParse::FullName()返回该文件的盘符、路径及完整名字.函数DoOneFile()可以是用户自己定义做相应的操作, (因为DoOneFile() 需要访问文件服务器, 所以RFs对象必须作为参数传递).
9. FindWildByDir()和FindWildByPath()都对CDir的对象分配了相应的空间,因此使用完必须删除CDir的对象.
10. 最后,你还可以使用TFindFile::FildWild()继续查询下一个盘符.
--------------------------------------------------------------------------------

有了上面的介绍,你一定对Symbian OS 中的操作有了一定的了解,更多更详细的使用方法还是需要你自己阅读Symbian OS sdk相应的帮助文件。以上给出的例程在vc++ 6.0/s60 sdk 2.1下调试通过。

你也可以阅读本blog中的《文件应该存储在手机的什么地方?》,该文章也有一些例程。
                                                                          祝你新年愉快!:)

                                                                                   --风小云
下一讲:《Symbian OS应用开发--玩转通讯录》

 

在上一篇文章中,我们介绍了有关文件和目录的操作方法,本篇文章我们来讨论有关手机中通信录的使用。

首先在文件和目录篇中我们介绍了系统RAM主要用于活动程序和系统内核,另外可作为用户使用的磁盘空间c:盘访问。其实,通信录中的数据就是存在RAM中的,至于操作这些数据的众多功能则是由通信录模型(Contacts Model)提供的。

接下来我们就来简单的讲述一下通信录模型的结构,它是基于一个通信录数据库,数据库中含有一些通信录项。每个通信录都有一些域,诸如姓名、地址,以及电话号码等。某台移动电话可能有好几个通信录数据库,但总会有一个默认的数据库。在我工作的环境中(S60 Sdk 2.1) ../Symbian/7.0s/Series60_v21_C/Epoc32/wins/c/system/data/Contacts.cdb
就是模拟器默认的数据库。
我们知道在手机应用开发中经常会遇到有关OBEX协议的问题,其实在通信录开发中也遵循这个协议,通信录中的数据是存在一个名叫vCard的载体里。vCard是一类电子名片,得到许多电子设备(如PDA和移动电话等)的支持。vCard的目的是:在这些设备之间用某些协议实现方便的通信录数据传递。可以将vCard编码成MIME规范(RFC 1521)所定义的明码文本。这种编码确保了各种vCard与限制为7位字符集(如在SMS消息中使用的编码)的传递编码的完全兼容。
一张vCard被格式化如下:
BEGIN:VCARD
VERSION:2.1
N:Wilkinson;Steve
FN:Steve Wilkinson
ORG:EMCC Software Ltd.
TEL;WORK;VOICE:01617779700
ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;108 Manchester Rd.=0D=0ACarrington;Manchester;UK;M31 4BD;United Kingdom LABEL;WORK;ENCODING=QUOTED-PRINTABLE:108 Manchester Rd.=0D=0ACarrington=0D=0AManchester, UK M31 4BD=0D=0AUnited K= ingdom
EMAIL;PREF;INTERNET:steve.wilkinson@emccsoft.com
REV:20030909T164330Z
END:VCARD
通信录模型中的许多功能都与vCard的处理有关,以保证Symbian应用开发伙伴们能方便地编制符合电子名片及通讯录交换方面的工业标准的代码。

具体实践之前先来简单介绍几个类:
CContactDatabase类用于创建一个新的数据库或打开一个现有的数据库。还提供了访问各通信录项的功能。既可以读取也可以打开通信录项。如果希望编辑或删除该项,就必须打开它,尽管一个应用在某个时间点只能打开一个特定的通信录项。当完成对通信录项的修改后,即把它们提交给数据库保存。当然这个类还提供了很多其他的功能,例如排序、查找等等。这个类是通信录操作中很重要的一个类。

通信录模型提供了MContactDbObserver类,某个应用可以继承自这个类,用于接收由某个特定的通信录数据库所产生的修订提示。使用该观察者(observer)的那些应用会接收事件提示,这些事件可以是向数据库中添加一个新通信录、修改或删除一个现存的通信录,以及一定范围内的其他有用提示。

所有通信录项都继承自抽象类CContactItem,对其访问是通过前面讲过的CContactDatabase类管理的。可以用CContactItem::Id()函数(该函数返回一个TContactItemId)来获取每个通信录项的唯一ID。CContactItem类也含有对通信录项内部的域进行操作的功能。可以添加及插入新域,也可以删除项目中的现有域。

某个通信录项内部的一些域由CContactItemField类所代表。为方便起见,位于某个特定项内部的所有域都被储存在一个结构内部,该结构被称为一个域集(CContactItemFieldSet)。每个通信录域储存了实际的域数据,以及针对该数据的一个特定储存类型。这可以是一个文本串、一个数据/时间值、一个商店,或者一个代理,它由TStorageType类代表。一个代理域基本上是对另一个通信录的链接,如,Series 60 Platform就是这样实现的:将其他通信录项的ID联系到该通信录域。请注意:这里并不支持数字表示的通信录域。所有数字信息,如电话号码和传真号码,都被作为文本保存。
接下来我们的实践会用到上面的部分类,所以上述的类大家需要认真熟悉一下,更多的细节也可以查找相关sdk的help文件。

打开、关闭数据库
CContactDatabase::OpenL()函数有两个重载函数。如果该函数没有给出一个参数,就打开默认的数据库。另一种情况是,应用设计师也可以传递一个有关数据库的路径和文件名,规定打开另一个数据库。
-------------------------------------------
// Open the default contacts database.
CContactDatabase* contactsDb = CContactDatabase::OpenL(); //打开默认数据库
CleanupStack::PushL(contactsDb);
// Count how many contacts are in the default database and then
// delete the CContactDatabase object.
// Note that all resources owned by the contacts database are freed
// upon deletion.
TInt numberOfContacts = contactsDb->CountL(); //所有数据项数目
CleanupStack::PopAndDestroy(contactsDb);//销毁
-------------------------------------------
要注意的是:某个通信录数据库并不具有Close()函数或类似的函数。

新建数据库
CContactDatabase::CreateL()函数与CContactDatabase::ReplaceL()函数之间的唯一差别就是:如果该数据库已经存在,前者会以KErrAlreadyExists退出。如前所述,如果没有定义参数,这些函数将创建一个默认的数据库。CContactDatabase::FindContactFile()函数给出了一个描述符,如果不存在默认数据库的话,该描述符就会返回该默认数据库的位置。
-------------------------------------------
// If one is found, replace it with a new empty default database.
// If no default database is found, create a new one.
TFileName contactDbFilePath;
CContactDatabase* newDefaultContactDb; if(CContactDatabase::FindContactFile(contactDbFilePath)) //是否存在默认数据库
{ newDefaultContactDb = CContactDatabase::ReplaceL();
}
else
{ newDefaultContactDb = CContactDatabase::CreateL();
}

CleanupStack::PushL(newDefaultContactDb);
// 添加自己功能代码
CleanupStack::PopAndDestroy(newDefaultContactDb);
-------------------------------------------

遍历通信录项
可以用TContactIter类来遍历一个通信录数据库。这个类提供了一整套的函数,用于遍历所有的通信录项。所有的函数都用通信录项ID (TContactItemId) 进行操作,该ID 用于访问某个特定的通信录项。
-------------------------------------------
// Open the default contacts database:
CContactDatabase* contactsDb = CContactDatabase::OpenL(); CleanupStack::PushL(contactsDb);

TContactIter iter(*contactsDb); //类似游标
TContactItemId cardId;

while( ( cardId = iter.NextL() ) != KNullContactId ) //循环遍历
{
CContactItem* card = contactsDb->ReadContactL(cardId); //读取相应项
CleanupStack::PushL(card);

//添加自己功能代码

contactsDb->CloseContactL(card->Id());
CleanupStack::PopAndDestroy(); // card
}
CleanupStack::PopAndDestroy(); // contactsDb
-------------------------------------------

导出所有通信项到文件
主要使用类CContactDatabase中ExportSelectedContactsL函数。s60 2.1help中定义如下:
-------------------------------------------
void ExportSelectedContactsL(const TUid& aFormat,const CContactIdArray& aSelectedContactIds,RWriteStream& aWriteStream,TInt aOption,TBool aExportPrivateFields=ETrue);
void ExportSelectedContactsL(const TUid& aFormat,const CContactIdArray& aSelectedContactIds,RWriteStream& aWriteStream,TInt aOption,const CVersitParser::TVersitCharSet aCharSet, TBool aExportPrivateFields=ETrue);

Supported from 6.0

Description:
Exports an array of contact items to a write stream — overloaded function. The items are converted into vCards before being written to the stream.

Parameters:
const TUid& aFormat:Indicates the format for imported and exported contacts. Must have a value of KVersitEntityUidVCard.
 
const CContactIdArray& aSelectedContactIds:Array of contact IDs to export.
 
RWriteStream& aWriteStream:The stream to write to.
 
TInt aOption:Indicates the options for import and export. See the TOptions enum.
 
const CVersitParser::TVersitCharSet aCharSet:The character format. If not specified, defaults to CVersitParser::EUTF8CharSet.
 
TBool aExportPrivateFields=ETrue:ETrue exports fields marked as private. EFalse does not export fields marked as private. See CContactItemField::SetPrivate().

Leave codes:
KErrNotSupported aFormat.iUid is not KVersitEntityUidVCard.
KErrNotFound One or more of the contact items does not exist in the database.
-------------------------------------------
例程如下:
-------------------------------------------
RFs fileSession;
User::LeaveIfError(fileSession.Connect());//连接文件服务器
CleanupClosePushL(fileSession);
CContactDatabase* contactDb = CContactDatabase::OpenL();//打开默认数据库
CleanupStack::PushL(contactDb);
CContactIdArray* exportContact = CContactIdArray::NewL();//记录数据项数组
CleanupStack::PushL(exportContact);

RFile file;
file.Replace(fileSession,aFileName,EFileWrite);//新建文件,aFileName是文件名字
CleanupClosePushL(file);
RFileWriteStream outputStream(file);//声明文件流
CleanupClosePushL(outputStream);

exportContact = iContacts;//iContacts是已初始化了相应导出属性的数组,需用类CCntFilter
     //如:
 //CCntFilter* filter = CCntFilter::NewLC();
 //filter->SetContactFilterTypeALL(EFalse);
 //filter->SetContactFilterTypeCard(ETrue);//按vCard格式导出
        //contactDb->FilterDatabaseL(*filter);  //按照该filter属性取出满足条件的相应项
TUid id;
id.iUid = KVersitEntityUidVCard;
contactDb->ExportSelectedContactsL(id,*exportContact,                   //导出到文件
      aWriteStream,
      CContactDatabase::EExcludeUid);
 
CleanupStack::PopAndDestroy(5);
-------------------------------------------

删除一个指定通信录项
这里可以使用类CPbkContactEngine,这个类可以很方便的完成各种操作,它相当于操作手机通信录的一个快速引擎
使用时:
CPbkContactEngine* iPbkContactEngine = CPbkContactEngine::NewL();
这样它就会建立一个默认通信录的引擎对象。
然后:
-------------------------------------------
IMPORT_C void CPbkContactEngine::DeleteContactL  (  TContactItemId  aContactId, 
  TBool  aImmediateNotify = EFalse
 ) 
IMPORT_C void CPbkContactEngine::DeleteContactsL  (  const CContactIdArray &  aContactIds, 
  TBool  aImmediateNotify = EFalse
 )
-------------------------------------------
上述两个函数都可以完成删除操作,只是一个是每次删除一个特定Id的通信录项,另一个是删除一个满足条件的通信录项数组。
可以简单的调用实现:
iPbkContactEngine->DeleteContactL(id);
删除后最好调用iPbkContactEngine->CompressL();将数据库进行下压缩,因为压缩比较消耗时间,所以不要每次删除都进行。

当然有了上面的类CPbkContactEngine,我们还可以实现新建和修改通信录项的功能,其实我们在文章开始时讲述的几个类也可以完成同样的功能,但相对于这个类,操作相对复杂一点,究竟选用什么类,只是你自己的爱好。
下面举例说明实现新建和修改操作:
新建
-------------------------------------------
//省略部分声明代码(与上面重复)
...
CPbkContactItem* contact = engine->CreateEmptyContactL();//新建一空通信录项
CleanupStack::PushL(contact);
_LIT(KFName,"King");
_LIT(KLName,"Chai");
_LIT(KNumber,"13777777777");
//修改first name
TPbkContactItemField* field = contact->FindField(EPbkFieldIdFirstName);
field->TextStorage()->SetTextL(KFName);
//修改last name
field = contact->FindField(EPbkFieldIdLastName);
field->TextStorage()->SetTextL(KLName);
//修改手机号码
field = contact->FindField(EPbkFieldIdPhoneNumberMobile);
field->TextStorage()->SetTextL(KNumber);
...//可以添加其他值
//修改后结果添加到数据库中,并返回这个通信录项的id,可以以后使用
TContactItemId Id = engine->AddNewContactL(*contact);

CleanupStack::PopAndDestroy(contact);
-------------------------------------------
实现修改和新建的代码类似,不同是你需要找到你要修改的field,然后修改,最后导入数据库
修改
-------------------------------------------
CPbkContactItem* contact = engine->OpenContactLCX(aContactId);
CleanupStack::PushL(contact);
//找到需要修改的field
TPbkContactItemField* field = contact->FindField(EPbkFieldIdPhoneNumberMobile);
CleanupStack::PushL(field);

_LIT(number,"13500000000");
TBuf<11> phonenumber(number);
//设置并确认修改
field->TextStorage()->SetTextL(phonenumber);
engine->CommitContactL(*contact);

CleanupStack::PopAndDestroy(2);
-------------------------------------------

上述内容主要介绍了手机中phonebook的各种操作,由于篇幅有限,只能列举一些常用的操作实现,大家可以将上面提到的几个类进行更深入的了解,一定会达到预期的效果。希望我的总结对你有所帮助!
最后祝大家新年愉快!
                                                                           --风小云

摘要:SMS(短消息服务)是GSM规范的一部分。Symbian OS SMS实现基于SMS规范的ETSI GSM 03.40 v7.4.0 Technical实现。(可从http://www.etsi.org中下载该规范)因为我们的主要工作主要是读取、新建、修改、删除等操作,所以我们本篇文章的重点放在这些操作上,在文章的结束部分,我们也会简单的谈及有关SMS的发送。

首先和以前各篇文章一样,我们先简要介绍几个重要的类:
CMsvSession
该类代表客户端(客户端MTM、用户接口MTM或者客户端消息应用程序)与消息服务器端的通讯通道。每一个客户端线程对应一个该类的实例,CMsvSession提供客户端能及时获取消息服务端消息的有效方式。一个消息客户端应用必须在正常使用任何MTM或CMsvEntry对象前,使用OpenSyncL()或者OpenASyncL()来新建一个session对象。
CClientMtmRegistry
Registry掌握了客户端所有目前可用的MTM有关的细节,消息客户端可以使用该类获得从CBaseMtm继承de对象。
CBaseMtm
这个类主要用来操作sms的内容,比如可以新建、修改sms;具体是使用方法下面将会借助代码说明,更详细的内容你也可以查看具体sdk的help。
CMsvEntry
相当于一个特定消息服务器的入口,当前entry与其的具体内容相关联。CMsvEntry包含两个部分的功能:一是可以允许访问与这个entry关联的,不同类型的数据;而是运行访问它的子entry。该类只在客户端使用,服务器端使用CMsvServerEntry。
TMsvEntry
用于代表消息服务器的一个入口,主要用于sms的新建。

下面还是看几段例程吧:)
TMsvSelectionOrdering sort;
sort.SetShowInvisibleEntries(ETrue); //全部内容排序,包括隐藏
//设置入口为outbox,也就是发信箱
CMsvEntry* entry = CMsvEntry::NewL(*iSession,KMsvGlobalOutBoxIndexEntryId,sort);
CleanupStack::PushL(entry);
//选择全部内容
CMsvEntrySelection* entries = entry->ChildrenL();
CleanupStack::PushL(entries);

TTime time;
//得到首信息的时间,At(0)代表首信息,取其他的可以给出相应的index
time = entry->ChildDataL(entries->At(0)).iDate;
//弹出对话框,有首信息接收人的号码信息
CAknInformationNote* informationNote = new (ELeave) CAknInformationNote;
informationNote->ExecuteLD(entry->ChildDataL(entries->At(0)).iDetails);

CleanupStack::PopAndDestroy(2);
 

------------------------------------------
注意:一些变量没有做介绍,比如iSession,这是因为前面的文章有提到过!
------------------------------------------
上面这段例程的作用就是让大家了解一下如何获取并操作sms。
简单说一下:首先定义一个消息服务器的入口,关联着outbox;然后取出outbox中所有的短信内容,并存储到CMsvEntrySelection型指针指向的list中,操作list便可方便的操作outbox中的sms。
下面讲述了如果取单个sms的具体内容,这些都是公共变量,可以利用它们来获取sms的具体细节:
------------------------------------------
TTime iDate (类型和名称)
Time (描述)短信时间
--------------------------------------------------------------------------------
TPtrC iDescription
Description 短信内容
--------------------------------------------------------------------------------
TPtrC iDetails
Details 发送或接受人号码
--------------------------------------------------------------------------------
TInt32 iError
Error
--------------------------------------------------------------------------------
TUid iMtm
MTM
--------------------------------------------------------------------------------
TInt32 iMtmData1
MTM data 1: this can be used for any purpose by an MTM.
--------------------------------------------------------------------------------
TInt32 iMtmData2
MTM data 2: this can be used for any purpose by an MTM.
--------------------------------------------------------------------------------
TInt32 iMtmData3
MTM data 3: this can be used for any purpose by an MTM.
--------------------------------------------------------------------------------
TMsvId iRelatedId
Related folder ID.
--------------------------------------------------------------------------------
TMsvId iServiceId
Service ID.
--------------------------------------------------------------------------------
TInt32 iSize
Size 短信大小
--------------------------------------------------------------------------------
TUid iType
Entry type
--------------------------------------------------------------------------------
TInt32 iWdpPortNumber
Port number
--------------------------------------------------------------------------------
TInt32 iBioType
BIO message type
------------------------------------------
通过使用上述变量就可以取得sms中的所有信息,我只对经常使用的几个做了介绍,剩下的当大家使用到时可以详细研究一下。上面的一段例程很简单、也很清楚,使用起来也会比较方便,接下来使用上面的方法我们可以同样实现delete操作:
TMsvSelectionOrdering sort;
sort.SetShowInvisibleEntries(ETrue);
CMsvEntry* entry = CMsvEntry::NewL(*iSession,KMsvDraftEntryId,sort);
CleanupStack::PushL(entry);

CMsvEntrySelection* entries = entry->ChildrenL();
CleanupStack::PushL(entries);
TInt i = entries->Count();
for(TInt ncount=0;ncount<i;ncount++)
entry->DeleteL(entries->At(ncount));
// information to the user
iEikonEnv->InfoMsg(_L("DeleteAll Done!"));
CleanupStack::PopAndDestroy(2); 

如果你已经理解了上面的读取sms信息的操作,这个全部删除也就不难理解了。所不同的只是调用了一个DeleteL()函数,这个函数是在类CMsvEntry中定义的,它可以删除固定index位置的sms。详情可以查看相关sdk help

有了上面的了解,不难看出sms的操作其实和我们上一讲所说的vCard有类似之处,下面我们来看看如何将sms的内容导出到文件。
这里会用到类CBaseMtm,看下面例程:
iSmsMtm->SwitchCurrentEntryL(aEntryId);
iSmsMtm->LoadMessageL(); // load the message

CRichText& body = iSmsMtm->Body(); //sms的内容存到CRichText控件对象中
TPtrC msg(body.Read(0));
WriteToFileL(msg);  

iSmsMtm是CBaseMtm类型的指针变量,它当然需要初始化,如下方式:
1.iSession = CMsvSession::OpenAsyncL(*this);
// 该函数的参数应该是从MMsvSessionObserver继承过来的任何类.....它会和 session library 间建立一个异步连接...
//然后你可以在函数HandleSessionEventL中收到事件EMsvServerReady...
//事件到达表明同服务器间的通话已建立... 接着去得到 Mtm Registry,并返回 SMS mtm...
2.iMtmReg = CClientMtmRegistry::NewL(*iSession);
3.iMtmSms = static_cast<CSmsClientMtm *> (iMtmReg->NewMtmL(KUidMsgTypeSMS));

下面是WriteToFileL()函数的具体实现:
void WriteToFileL(const TPtrC &aMsg)
{ //设置存储路径以及文件
_LIT(KDirName,"//system//apps//MyApp//Data");
_LIT(KFileName,"//system//apps//MyApp//Data//MsgBody.txt");
//连接文件服务器并生成相应文件夹
RFs fileSession;
fileSession.Connect();
fileSession.MkDirAll(KDirName);

RFileWriteStream writer;
writer.PushL();
User::LeaveIfError(writer.Replace(fileSession,
KFileName, EFileWrite));
//写入文件并确认
writer << aMsg;
writer.CommitL();

CleanupStack::PopAndDestroy();
fileSession.Close();
}
//以下头文件会使用到
#include <smsclnt.h>
#include <smut.h>  

上面的步骤清晰明了,无需多说,大家要注意一下CBaseMtm这个类的用法,因为在接下来,我们看到的新建sms同样会重点使用到这个类,而且使用的次数会更多。

新建sms的步骤较为复杂,我们列出一些主要步骤:
TMsvEntry newEntry;
newEntry.iMtm
newEntry.iType
newEntry.iServiceId
newEntry.iDate.HomeTime();//一般设置为当前时间
newEntry.SetInPreparation(ETrue);//设置为true 

设置好上面的参数之后,接下来就可以使用CBaseMtm类来完成新建操作了
CBaseMtm* iMtmSms;
...
iMtmSms->SwitchCurrentEntryL(KMsvGlobalInBoxIndexEntryId);//设置为收信箱
iMtmSms->Entry().CreateL(newEntry);

long smsId = newEntry.Id();//得到新sms的id
iMtmSms->SwitchCurrentEntryL(smsId);
//设置sms的详细内容
_LIT(KSMSBody,"Hello World!");
CRichText& body = iMtmSms->Body();
body.Reset();
body.InsertL(0,KSMSBody);
newEntry.iDescription.Set(KSMSBody);
//设置sms的收信或发信人手机号码
iMtmSms->AddAddresseeL(_L("13500000000"));
newEntry.iDetails.Set(_L("13500000000"));
iMtmSms->Entry().ChangeL(newEntry);
//别忘了保存
iMtmSms->SaveMessageL(); 

如此一来,你就可以新建一条sms了,当然有了新建sms的经验,实现修改sms的信息也就不困难,主要步骤可分为三步:
1. 给定一些需要修改sms的信息,比如id、index、所在位置或其他
2. 通过搜索找到满足条件的sms,并暂存这些结果
3. 利用搜索结果进行sms信息修改,最后确认变化即可
这里对sms的修改就不做详细的分析,大家可以自己尝试一下!

下一节内容,我会和大家一起继续讨论有关sms的内容,只不过重点放在发送上,期待您的关注!

文件应该存储在手机的什么地方?

我需要把一个文件存在手机里,pkg文件这样写的:
"/Symbian/6.1/Series60/epoc32/release/wins/UDEB/Z/SYSTEM/DATA/data.dat"-"!:/system/apps/test/data.DAT"

在模拟器中运行时,程序里读入绝对路径:
z://system//data//data.DAT
在手机里运行时,程序读入路径为:
e://system//apps//test//data.DAT

在模拟器上运行正常,安装在手机的卡上,运行不了,不知道为什么

这个路径可以写死也可以运行时获得:
方法一:

code:

_LIT(KBitmapFilename, "c://system//apps//test//xxx.mbm");
const TUint16 KMMCDriveLetter = 'e';
const TUint16 KROMDriveLetter = 'z';
// try 3 locations - application directory in C,E (memory card), and Z drive
TFileName imageLocation(KBitmapFilename);
TInt loadErr = iImageFlag->Load(imageLocation,useFullScreen);
// not found on C, try E & Z
if(loadErr != KErrNone)
        {
        imageLocation[0] = KMMCDriveLetter;   
        loadErr = iImageFlag->Load(imageLocation,useFullScreen);
        if(loadErr != KErrNone)
            {
            imageLocation[0] = KROMDriveLetter; // in emulator, apps usually reside on z
            loadErr = iImageFlag->Load(imageLocation,useFullScreen);
            }
        }

方法二:

code:

void GetFullPathName(TDes& aFileName)
{
// Get default drive and path
TParse parse;
TFileName appPath;
TBuf<5>  appDrive;
parse.Set(CEikonEnv::Static()->EikAppUi()->Application()->AppFullName(), NULL, NULL);
appPath.Copy(parse.DriveAndPath());
appDrive.Copy(parse.Drive());

// Parse the file name
parse.Set(aFileName, NULL, NULL);
if (parse.Drive().Length() == 0)
{
if (parse.Path().Length() == 0)
{ // Use default path and drive
aFileName.Insert(0, appPath);
}
else
{ // Use default drive
aFileName.Insert(0, appDrive);
}
}
}

 

Regards
Maxying

 

为了增强程序的可移植性,还是不要用绝对路径的好。我们可以在操作前获取当前设备的存储器路径。

例如:
根目录:
PathInfo::PhoneMemoryRootPath()
存储图片文件目录:
PathInfo::ImagesPath()
存储安装SIS文件目录:
PathInfo::InstallsPath()
存储声音文件目录:
PathInfo::SoundsPath()

如果想要定位MMC卡这种外加的存储器应该这样:

#include <PathInfo.h>
TFileName path = PathInfo::MemoryCardRootPath();

r60600

PathInfo这个类是在S60 2.0平台的,楼主的Symbian 6.1也就是S60 1.0平台用不了的! 

抱歉!评论已关闭.