北京理工大学 20981 陈罡
在symbian开发中我们经常会用到手机系统自带的“程序管理”这个软件。
这个软件的功能在于它会忠实地把程序的安装操作记录下来,在删除
程序的时候它也会忠实地把程序给删除。这种做法对于symbian来说,
无疑是最好的选用第三方软件的选择,既可以安装到手机上,又可以
无条件的将软件卸载掉。
但是这个所谓的“程序管理”,也有很多弊端。例如,每次都需要把程序
的安装包拷贝到“手机存储”上去,这样安装包一旦大于2M,对于多数s60
手机来说,这个程序极有可能引起“手机内存不足,请关闭一些程序”这样
的错误提示,最终导致安装失败。再有就是程序的升级,有的时候不需要
整个将sis包重新安装一遍,只是覆盖掉几个dll文件即可,但是很多情况
下,开发者都是选择将程序全部重新安装一遍,如果打包的时候升级过sis
包的版本,那么将在“程序管理”中看到多个安装记录。另外目前很多公司
都在寻找手机程序预装的方法,其实从其本质来说就是希望程序安装到
用户的手机上以后,无法被用户使用普通的“程序管理”程序卸载。
在这里我要讨论一种方式,可以绕过程序管理器的限制,在“程序管理”器
里面没有记录,也无法把程序从程序管理器里面卸载,这种方式虽然比较
有效,但是程序的卸载操作就需要使用其它的单独的卸载程序才能卸载了。
绕过程序管理器的方法其实很简单,自己编写一个程序管理器即可。很多
朋友会想编写一个程序管理器多么多么的复杂,需要熟悉sis文件格式之类
的内容。其实一点也不复杂,sis本身就是s60自带的程序管理器支持的文件
格式,只有nokia的程序管理器才能够识别。既然我们自己编写程序管理器
的话,就不必拘泥于sis格式了。自己的程序可以读取的文件格式就可以自己
说得算了。
我们知道在创建pkg文件的时候,在需要安装后立即运行的程序后面通常只要
指定一个RI,FI,就可以保证程序在安装后可以立即执行,例如:
"marm\myapp.exe"-"!:\System\Apps\MyApp\Myapp.exe",RI,FI
这样就提供了一个方法摆脱当前的程序管理器的方法,大致的思路如下:
编写一个exe文件,该文件可以将某个.zip或者.dat的包解压缩到手机的
e:\system\apps目录下面即可。
整体的写下来,大概是这个样子的:
"marm\my.zip"-"!:\System\Apps\MyApp\my.zip"
"marm\myapp.exe"-"!:\System\Apps\MyApp\myapp.exe",RI,FI
在myapp.exe运行的时候,可以给它加入适当的1秒至3秒的延时,用来确保
手机的程序管理器已经把zip包释放到某个目录下面去了,然后再开始运行
myapp.exe,它的作用在于直接把my.zip包中的文件解压缩到system\apps目录
或者希望可以开机自动运行的可以把相关的mdl文件考入c:\system\recogs目录。
注意:
利用mdl在手机启动的时候自动载入的特性实现的所谓“开机自动运行”,在
symbian 3rd平台中已经不再适用,以下是官方的issues说明:
这样一来,只有my.zip和myapp.exe这两个文件是纳入手机的应用程序管理器
的控制范畴的,可以很容易地通过应用程序管理器删除,但是通过myapp.exe
从my.zip中解压缩出来的,放入system\apps里面的目录则成功的逃脱了程序
管理器的限制,保留了下来。这样就基本上实现了,程序脱离应用程序管理器
的限制了。如果在my.zip中加入自动运行的mdl,然后调用自动登陆、下载zip、
自动解压缩的程序的话,就可以实现程序的自动更新了。每次让开机自动运行
的程序,在收到更新短信时启动,联网,下载更新包,然后解压缩安装。这
一切都是以后台的方式运行的,不会对用户产生困扰,也不需要用户每次都通过
nokia pc套件来下载,安装程序的繁琐过程。
对用户来说,只是某天突然发现不知道什么时候手机中多了一个应用程序的
图标 :),或者发现某个程序的图标不见了(可以通过网络自动删除手机中的某个
无效的应用)。相当于自己实现了一个OTA了。
相关实验我已经完全测试成功,呵呵。但是这种OTA是一把双刃剑
不希望落入某些居心不良的人的手中,扰乱这个技术的发展。所以就暂时不开放
代码了,只是把解压缩my.zip的myapp.exe代码开放一下,希望对有兴趣的朋友
有用:
// --------------------------------------------------------------------------
// zagzip.cpp
//
// programmer : wayne
// (1)get zip file exactly pathname
// (2)set path where package need to be extracted
// (3)check whether the object directory exists
// (4)if directory exists, perform extract operaion
// (5)if not exists, create one, then, goto step (4)
// (6)call outer command, delete zip file and quit smoothly
// --------------------------------------------------------------------------
#include
#include
#include // RFs and RFile
#include // CZipFile
#include // CApaCommandLine
#include // EDll::StartApp(...)
#include
#include
#include // RFileReadStream, RFileWriteStream
// use 4k buffer size
#define BUF_SIZE 1024 * 4
// the specified zip config file name
#ifndef __WINS__
_LIT(KZipPathnameC, "c:\\my.zip") ;
_LIT(KZipPathnameE, "e:\\my.zip") ;
_LIT(KExtractPath, "c:\\system\\apps\\abc\\") ;
_LIT(KSrcPathname, "c:\\system\\apps\\abc\\abc.mdl") ;
_LIT(KObjPathname, "c:\\system\\recogs\\abc.mdl") ;
_LIT(KOutCmd, "c:\\system\\apps\\abc\\abc.app") ;
#else
_LIT(KZipPathnameC, "c:\\my.zip") ;
_LIT(KZipPathnameE, "c:\\my.zip") ;
_LIT(KExtractPath, "c:\\system\\apps\\abc\\") ;
_LIT(KSrcPathname, "c:\\system\\apps\\abc\\abc.mdl") ;
_LIT(KObjPathname, "c:\\recogs\\abc.mdl") ;
_LIT(KOutCmd, "c:\\system\\apps\\abc\\abc.app") ;
#endif
TBuf8 g_buf ;
TBuf<100> g_zip_pathname ;
TBuf<100> g_target_path ;
TBuf<100> g_copy_src_pathname ;
TBuf<100> g_copy_obj_pathname ;
TBuf<100> g_run_command ;
// Constants
LOCAL_C TBool check_file_exist(const TDesC & path_name) ;
LOCAL_C TBool check_dir_exist(const TDesC & dir_name) ;
LOCAL_C TBool extract_zipfile(const TDesC & zip_pathname, const TDesC & target_path) ;
LOCAL_C TBool extract_single(RFs& fs,
CZipFile * zip_file,
const TDesC& target_path,
const TDesC& file_name) ;
LOCAL_C TBool run_command(TDesC& preset_command) ;
LOCAL_C TBool copy_file(TDesC& obj_pathname, TDesC& src_pathname) ;
LOCAL_C TBool get_const_string(TDes & res_str,const TDesC & const_str) ;
LOCAL_C TBool main_proc() ;
//检查文件是否存在
LOCAL_C TBool check_file_exist(const TDesC & path_name)
{
RFs fs ;
RFile f ;
TInt res ;
User::LeaveIfError(fs.Connect()) ;
res = f.Open(fs, path_name, EFileRead) ;
f.Close() ;
fs.Close() ;
return (res == KErrNone) ? ETrue : EFalse ;
}
// 检查目录是否存在
LOCAL_C TBool check_dir_exist(const TDesC & dir_name)
{
RFs fs ;
RDir dir ;
TInt res ;
User::LeaveIfError(fs.Connect()) ;
res = dir.Open(fs, dir_name, KEntryAttNormal) ;
dir.Close() ;
fs.Close() ;
return (res == KErrNone) ? ETrue : EFalse ;
}
// 解压缩zip包了
LOCAL_C TBool extract_zipfile(const TDesC& zip_pathname, const TDesC& target_path)
{
// Connect to the file server.
RFs fs ;
User::LeaveIfError(fs.Connect()) ;
// Create an instance of CZipFile.
CZipFile* zip_file = CZipFile::NewL(fs, zip_pathname) ;
CleanupStack::PushL(zip_file) ;
// Iterate all the files inside the .zip file and then decompress it
CZipFileMemberIterator* members = zip_file->GetMembersL();
CZipFileMember* member = NULL ;
CleanupStack::PushL(members);
// 这里是确保解压缩的目的目录存在,如果不存在就创建一个
if(!check_dir_exist(target_path)) {
// target path doesn't exist, create one
fs.MkDir(target_path) ;
}
// iterator one by one
while ((member = members->NextL()) != 0) {
// extract the compressed file into the specified directory
if(check_file_exist(*member->Name())) {
TParse parse ;
parse.Set(*member->Name(), NULL, NULL) ;
// 如果有被占用的rsc,则跳过,继续运行
// 这一点主要针对程序正在运行中的情况,rsc不可写
if(parse.Ext().Find(_L("rsc")) != KErrNotFound) continue ;
}
extract_single(fs, zip_file, target_path, *member->Name()) ;
delete member;
}
CleanupStack::PopAndDestroy(); // members
CleanupStack::PopAndDestroy(); // zip_file
fs.Close();
return 0 ;
}
// 解压缩一个文件
LOCAL_C TBool extract_single(RFs& fs,
CZipFile * zip_file,
const TDesC& target_path,
const TDesC& file_name)
{
TInt total_size = 0 ;
TUint uncompressed_size = 0 ;
// Get the input stream of aFileName.
CZipFileMember* member = zip_file->CaseInsensitiveMemberL(file_name);
CleanupStack::PushL(member);
RZipFileMemberReaderStream* stream;
zip_file->GetInputStreamL(member, stream);
CleanupStack::PushL(stream);
// Extracts file_name to a buffer.
TFileName target_pathname ;
RFile file ;
target_pathname.Append(target_path) ;
target_pathname.Append(file_name) ;
User::LeaveIfError(file.Replace(fs, target_pathname, EFileWrite));
CleanupClosePushL(file);
total_size = member->UncompressedSize() ;
while(total_size > 0) {
// if the file is quite huge, then read the file in streaming mode.
// use 4KB buffer and save binary raw data into uncompressed file
// 这里使用了4K的缓冲区去分段解压缩大的zip文件
g_buf.SetLength(0) ;
if(total_size >= BUF_SIZE) uncompressed_size = BUF_SIZE ;
else uncompressed_size = total_size ;
User::LeaveIfError(stream->Read(g_buf, uncompressed_size)) ;
User::LeaveIfError(file.Write(g_buf)) ;
total_size -= uncompressed_size ;
}
// Release all the resources.
file.Flush() ;
CleanupStack::PopAndDestroy(3); // file, stream, member
return 0 ;
}
// 这是执行外部命令了
LOCAL_C TBool run_command(TDesC& preset_command)
{
if(check_file_exist(preset_command)) {
CApaCommandLine * command_line = CApaCommandLine::NewLC();
command_line->SetLibraryNameL(preset_command) ;
command_line->SetCommandL(EApaCommandRun);
User::LeaveIfError(EikDll::StartAppL(*command_line));
CleanupStack::PopAndDestroy(command_line) ;
return ETrue ;
}
return EFalse ;
}
// 复制文件,貌似应该有更好的方法,这里自己写了一个了
// 应对recogs目录不存在的情况
LOCAL_C TBool copy_file(TDesC& obj_pathname, TDesC& src_pathname)
{
RFs fs ;
RFile fsrc ;
RFile fobj ;
TInt total_bytes ;
TInt used_bytes ;
TParse pathname_parse ;
TBuf<50> copy_dir ;
User::LeaveIfError(fs.Connect()) ;
// check whether the object dir is exist
pathname_parse.Set(obj_pathname, NULL, NULL) ;
copy_dir = pathname_parse.DriveAndPath() ;
if(!check_dir_exist(copy_dir)) {
fs.MkDir(copy_dir) ;
}
fsrc.Open(fs, src_pathname, EFileStream | EFileRead) ;
fobj.Replace(fs, obj_pathname, EFileStream | EFileWrite) ;
fsrc.Size(total_bytes) ;
while(total_bytes > 0) {
if(total_bytes >= BUF_SIZE) used_bytes = BUF_SIZE ;
else used_bytes = total_bytes ;
fsrc.Read(g_buf) ;
fobj.Write(g_buf) ;
total_bytes -= used_bytes ;
}
fs.Close() ;
return ETrue;
}
LOCAL_C TBool get_const_string(TDes & res_str,const TDesC & const_str)
{
res_str.SetLength(0) ;
if(check_file_exist(const_str)) {
res_str.Copy(const_str) ;
return ETrue ;
}
return EFalse ;
}
LOCAL_C TBool main_proc()
{
TBool has_running_app = EFalse ;
RFs fs ;
RFile f ;
TBuf8<10> s ;
// 检查文件
if(check_file_exist(KOuterCmd)) has_running_app = ETrue ;
User::LeaveIfError(fs.Connect()) ;
f.Replace(fs, KQuitFile, EFileWrite) ;
s.Format(_L8("quit")) ;
f.Write(s) ;
f.Flush() ;
f.Close() ;
fs.Close() ;
// 确定zip文件存在在C盘还是E盘,把路径存入g_zip_pathname
if(!get_const_string(g_zip_pathname, KZipPathnameE)) {
get_const_string(g_zip_pathname, KZipPathnameC) ;
}
// 解压缩后文件的目标存放路径
g_target_path.Copy(KExtractPath) ;
// 解压缩后mdl文件的存放路径
g_copy_src_pathname.Copy(KSrcPathname) ;
// 解压缩后将mdl文件拷贝到的目标路径
g_copy_obj_pathname.Copy(KObjPathname) ;
// 这是都执行完毕后需要运行的外部命令,类似FI,RI的功能
g_run_command.Copy(KOuterCmd) ;
// 这就是解压的过程了
extract_zipfile(g_zip_pathname, g_target_path) ;
// 这里主要是为了把mdl文件拷贝到c:\\system\\recogs这个目录下而加入的
copy_file(g_copy_obj_pathname, g_copy_src_pathname) ;
// 最后运行常驻内存的那个exe或app
if(!has_running_app) run_command(g_run_command) ;
// 删除zip文件和mdl文件
User::LeaveIfError(fs.Connect()) ;
fs.Delete(g_zip_pathname) ;
fs.Delete(g_copy_src_pathname) ;
fs.Close() ;
return 0 ;
}
// 从这里跑到自己定义的那个函数里面去
LOCAL_C void MainL(const TDesC& /*aArgs*/)
{
main_proc() ;
}
LOCAL_C void DoStartL()
{
// 没法子,在exe中要使用活动对象,只能自己创建调度器
CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
CleanupStack::PushL(scheduler);
CActiveScheduler::Install(scheduler);
// 调用MainL函数,开始解压缩
TBuf<256> cmdLine;
RProcess().CommandLine(cmdLine);
MainL(cmdLine);
// 删除调度器
CleanupStack::PopAndDestroy(scheduler);
}
// 这个是整个程序的入口点了
GLDEF_C TInt E32Main()
{
// 连异常处理栈都要自己创建
__UHEAP_MARK;
CTrapCleanup* cleanup = CTrapCleanup::New();
// Run application code inside TRAP harness, wait keypress when terminated
TRAPD(mainError, DoStartL());
delete cleanup;
__UHEAP_MARKEND;
return KErrNone;
}
// End of File
稍后我会发布一个关于symbian下面使用zip类的封装。
这篇文章只是一个探讨,希望能够抛砖引玉,得到更好的思路,谢谢!