Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

如何在安装时备份sis文件

From Wiki
Jump to: navigation, search
Article Metadata
Tested with
Devices(s): S60
Compatibility
Platform(s): Symbian
S60 5th Edition
Article
Keywords: TOpenFileScan
Created: chenziteng (19 Jan 2010)
Last edited: hamishwillee (26 Jul 2011)

Contents

概述

有多款免费或商用S60应用程序支持“蓝牙分享”功能, 例如广受欢迎的免费软件Paint Pad就有一个“分享应用程序”菜单项,当选择该菜单项时它能通过蓝牙向配对设备发送一份安装包。通过观察能发现,该应用程序在安装过程中将安装包备份到了系统盘(C:盘)。本文(附以一个完整的示例程序)展示如何实现这个功能。

方案

总的方案可以用一句话阐明:我们需要开发一个在安装时执行的程序,该程序可以找到包含它的sis文件并将该文件拷贝到指定位置。这句话的前半部分可以通过包文件的高级选项 FR、RI和RW等实现。(注意:这些选项对于自签名的安装包不起作用),所以我们将注意力放在后半部分上。

在讨论如何查找正在被安装的sis文件之前,让我们先提两个假设,以简化我们的调查过程。

假设一:sis文件在被安装的过程中处于打开状态

该假设可以通过以下测试用例确认,

第1步:使用S60文件管理器打开一个sis文件

第2步:使用另一个文件管理器删除该文件,删除失败并报错“已被使用!”,这表示sis文件处于打开状态(即正在被安装程序使用)

假设二:同一时间只能安装一个sis文件

该假设可以通过以下测试用例确认,

第1步:使用S60文件管理器打开一个sis文件,并按安装程序的提示操作,直到出现盘符选择对话框

第2步:使用另一个文件管理器打开该文件,操作失败并报错“安装程序已经在运行,无法再次启动”,这说明安装程序是单实例的。

在这两个假设下,刚才的问题可以被简化成:如何查找一个打开的sis文件?(当然可以用sis文件的其它特性进一步确认,例如文件的UID)。一个显而易见的答案是遍历整个文件系统逐一对比查找文件,它可以作为备选方案一,

备选方案一:通过遍历整个文件系统查找所需要的文件。

这个方案可以用File Server client APIs实现,例如TFindFileCDirScan,但是在系统盘和大容量存储器上可能有几十个目录和几百个文件,为了避免安装时长时间的等待,最好能减小文件查找的范围。值得庆幸的是,有一个系统APITOpenFileScan能获取所有处于打开状态的文件 - 这正是我们想要的!

备选方案二:使用TOpenFileScan,通过遍历所有打开的文件查找所需要的文件。

在使用这个方案时小心两个“陷阱”:一是每次调用TOpenFileScan::NextL()只会创建某个特定的文件服务器会话打开的文件列表,所以必须循环调用该函数,只到它返回NULL或者找到所需要的文件;二是通过这个API获取的文件名不带盘符,例如是"\\data\\HelloWorld.sis"而不是"C:\\data\\HelloWorld.sis"。

...
_LIT(KExtSis, ".sis");
_LIT(KExtSisx, ".sisx");
RFs fs;
TInt err = fs.Connect();
User::LeaveIfError(err);
CleanupClosePushL(fs);
TFileName filename;
TOpenFileScan ofs(fs);
TBool done = EFalse;
while(!done)
{
CFileList* fl = NULL;
ofs.NextL(fl);s
if (fl==NULL) /** NextL()应该被反复调用,直到它返回NULL */
{
done = ETrue;
}
else
{
CleanupStack::PushL(fl);
TInt count = fl->Count();
for (TInt i= 0; (i<count)&&(!done); i++) /** 对于每一个处于打开状态的文件 */
{
TEntry entry = (*fl)[i];
TParsePtrC parse(entry.iName);
if((parse.Ext()==KExtSis)||(parse.Ext()==KExtSisx)) /** 查找第一个类型是sis的文件 */
{
filename = entry.iName;
done = ETrue;
}
}
CleanupStack::PopAndDestroy(fl);
}
}
if(filename!=KNullDesC)
{
/** 找到了!注意:文件名不含盘符 */
}
CleanupStack::PopAndDestroy(&fs);
...

文件名找到了,但在支持Data caging的系统中程序并不总能在所有目录之间拷贝文件,因此在执行拷贝操作之前让我们看一下文件可能保存在哪些目录中,

目录 说明
信息应用程序的私有目录 即文件是通过蓝牙、红外或其它协议发送到手机上的,这种情况下必须使用Messaging APIs获取文件
其它私有目录 不太可能,因此忽略这种情况
\resource目录 不太可能,而且任何应用程序都能读取资源文件下的文件
\sys目录 不太可能,因此忽略这种情况


了解了这些情况就能继续编码了。如果文件位于信息应用程序的私有目录,那么必须使用Messaging API获取它。Messaging API的使用不是本文的重点,因此这里不再赘述。本文的示例程序能够提取处理蓝牙信息的附件,如果感兴趣的话可以看一下CSmsHandler::ExportInboxToFileL()的实现。

如之前提到的,我们获取的文件名没有盘符,因此我们必须使用RFs::DriveList()列出所有可用的盘符,然后逐一检查文件是否存在,如果存在还要进一步检查该文件是否处于打开状态(即无法被修改)。如果一切顺利的话最终我们能用盘符补全文件名,然后就能将文件拷贝到指定位置(例如本文的示例程序将文件拷贝到c:\sisback目录)

...
_LIT(KSisBackup, "c:\\sisbackup\\backup.sis");
_LIT(KMessagingPrivateFolder, "\\private\\1000484b\\");
_LIT(KPrivateFolder, "\\private\\");
_LIT(KSysFolder, "\\sys\\");
...
if(filename!=KNullDesC)
{
/** 续上,找到了! */
 
BaflUtils::EnsurePathExistsL(fs, KSisBackup); /** 确保目标目录存在 */
 
filename.LowerCase(); /** 将所有字符转换成小写以方便比较 */
 
if(filename.Find(KMessagingPrivateFolder)==0)
{
/** 文件位于信息应用程序的私有目录,无法直接获取,但可以尝试用Messaging API获取 */
}
else if(filename.Find(KPrivateFolder)==0)
{
/** 文件位于其它私有目录,无法直接获取 */
}
else if(filename.Find(KSysFolder)==0)
{
/** 文件位于系统目录,无法直接获取 */
}
else
{
/** 文件位于其它目录(包括资源文件),可以直接获取 */
 
_LIT(KDriveC, "c:");
filename.Insert(0, KDriveC); /** 文件名无盘符,因此添加占位符 */
TBool found = EFalse;
TDriveList driveList;
err = fs.DriveList(driveList);
User::LeaveIfError(err);
for(TInt driveNumber=EDriveA; (driveNumber<EDriveZ)&&(!found); driveNumber++)
{
if (driveList[driveNumber]) /** 遍历所有可用盘符 */
{
TChar driveLetter;
err = fs.DriveToChar(driveNumber,driveLetter);
User::LeaveIfError(err);
filename[0] = driveLetter;
TEntry entry;
err = fs.Entry(filename, entry);
if(err==KErrNone) /** 检查文件是否在该盘上 */
{
err = fs.SetModified(filename, entry.iModified); /** 如果存在则检查它是否打开 */
if(err!=KErrNone) /** KErrInUse。 或KErrPermissionDenied,如果文件位于资源文件 */
{
found = ETrue;
}
}
}
}
if(found)
{
/** 终于找到了!将文件拷贝到指定位置 */
err = BaflUtils::CopyFile(fs, filename, KSisBackup);
User::LeaveIfError(err);
}
}
}
...

看上去“任务”完成了,但在结束讨论之前还必须考虑以下两个问题:

(1) 在卸载时要删除备份文件。这一点可以通过在.pkg文件中添加一行带“FN”标记的指令实现。

...
"" - "c:\sisbackup\backup.sis", FN
...

(2) 如果安装被中途撤销则不应该有备份文件。本文的示例程序没能正确地处理这一点,希望有人解决这个问题并更新wiki。

示例

完整的示例程序: HelloWorld(SisBackup).zip

该示例程序在S60 5th Edition SDK上构建通过,在一台S60 5th Edition手机上测试通过。

如何使用:

1. 为目标设备构建\SisBackup程序

2. 为目标设备构建\HelloWorld应用程序,并制做sis文件

3. 用SymbianSigned签名签署该sis文件

4. 将签名后的sis文件拷贝到手机上(或通过蓝牙发送到手机上)

5. 安装签名后的sis文件,然后启动HelloWorld应用程序,然后选择“选项”->“Share”,如果能看到显示“c:\sisbackup\backup.sis”的提示就说明sis文件已经成功地被备份到系统盘上了。

参考

Advanced Package File Options

TSS000718 - Using RUNREMOVE and RUNINSTALL options in SIS packages

Scanning folders & subfolders in Symbian devices

Find Files

How to get a sis file name while this sis file is installing?

How to get Drive Information

How to get the attachments from an EMail

This page was last modified on 26 July 2011, at 02:53.
129 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×