symbian 2nd如何繞過程序管理器的限制
北京理工大學 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 <e32base.h> #include <e32std.h> #include <f32file.h> // RFs and RFile #include <zipfile.h> // CZipFile #include <apacmdln.h> // CApaCommandLine #include <EikDll.h> // EDll::StartApp(...) #include <apgcli.h> #include <apgtask.h> #include <s32file.h> // 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<BUF_SIZE> 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