• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Khan's Notebook GCC/GNU/Linux Delphi/Window Java/Anywhere

            路漫漫,長修遠,我們不能沒有錢
            隨筆 - 173, 文章 - 0, 評論 - 257, 引用 - 0
            數據加載中……

            Symbian 開發知識,瑣碎篇

            Symbian 開發知識,瑣碎篇

            General Hints / 一般提示

            學習S2,我的方法無非是:
            ·每天coding,多少小時你自己掌握,看你有多少時間了。

            ·coding的同時,打開SDK Help,查里面的函數庫。

            ·資源管理器的“搜索”功能不能少,你可以在你的安裝目錄\series60ex里面找到Series60的范例代碼,\example這里找到Symbian的范例代碼

            ·范例代碼我是使用UltraEdit來察看并卻穇閱的,不要畏懼Nokia長長的example,他只是為了在一個例子里面展示能牽扯到的所有的功能,你所需要的可能只是一個類的幾行使用方法,找到那一段,把它copy出來,用在你的.cpp里面理解就可以了。

            前面是網下的,下面說說網上的。
            ·每天來這里看看首頁更新,有document就下載下來,是你所需要的就馬上看。

            ·看看DiscussionBoard,這里,Chinese Application Development > Symbian ,有別人的問題,你可以記住并且以后參考。

            ·自己遇到的問題,就在這里用AdvancedSearch找一下。

            ·NewLC www.newlc.com 是一個好網站,我發現的除了Forum Nokia之外唯一的S2站點。它里面的Tutior教學部分真的有很多好東西,我建議都給他離線瀏覽下載下來,我就是這么做的。不過它里面的論壇,我還 沒有看,主要是外國人太多,好像也都是提問的,真正解決問題的沒有這里那么好吧。

            其它的,也沒有什么了,你要是想看市場,還有www.symbian.com和www.series60.com 。



            中文問題


            在模擬器或者真機上面顯示中文,這是一個不難的問題。
            先說說模擬器。因為我很久很久沒有用過0.9中文版的模擬器了,所以對其情況也忘記的差不多了。我想如果是0.9中文版的模擬器,顯示中文應該沒有什么大 問題了,不過SDK V0.9中不全的header&lib,這個會對你開發程序造成影響吧,所以我還是不建議現在仍然在使用SDK V0.9。
            那么使用英文版模擬器要如何顯示中文呢?其實是不可以顯示中文的。英文版的SDK中主要是說他的模擬器沒有制作成中文版,所以如果你在菜單中寫了中文,他 是無法顯示的。但是有一個例外,就是如果你在程序中實現Draw函數的時候使用了DrawText或者DrawTextVertical,并且事先把中文 版模擬器中的中文字體復制到了模擬器中,那么模擬器是可以顯示中文的。注意這其實是等同于“用字體在屏幕上面‘劃*’字”,其中的實現機理我想我還沒有什 么研究,扯遠了肯定會出錯,所以大家如果有需要的話,在英文版模擬器上面Draw中文,可以復制一下中文版中的字體文件到英文版SDK的
            epoc32\release\wins\udeb\z\system\fonts(大概是這里,我現在手邊沒有SDK來參考)
            然后在DrawText之前調用一下字體就可以了。
            *因為論壇中文系統的訽因,我無法輸入那個關鍵的“籣”字,一橫一個田下面一個半包圍,所以暫時用這個錯別字代替了,我目的就是說明它是Draw上去的。

            在真機上面,大家顯示中文無非就是按照上面那篇文檔說的,一步一步來就可以了。
            大體上,在程序中顯示中文分為固定的用戶界面中文字符串和程序運行時生成字符串。第一類使用較多,而且也基本上可以代替第二類,所以我還是主張大家不要在你的.cpp里面寫中文字符串,雖然它是可以實現的。
            用戶界面中的字符串,Symbian系統是使用資源文件來保存并管理的。我們所需要知道的便是:在你的Project文件夾的data目錄下面,編糭你 的.RSS文件。這個文件中,你可以通過定義TBUF資源對象,來定義字符串。你定義的字符串,最好還是寫成一個假名,比如

            RESOURCE TBUF r_myprj_str_somestring
                    {
                    buf=qtn_myprj_str_somestring;
                    }

            而這個qtn_myprj_str_somestring,你最好寫在myprj.loc里面,這樣:

            #define qtn_myprj_str_somestring "Some String"

            他的一個好處就是,你便于將你的程序作本地化/國際化處理。如果你的程序要做成很多語言的版本,那么你可以把你的用戶界面字符串寫成
            myprj.en.loc
            myprj.cn.loc
            myprj.fr.loc
            然后在編譯的時候,只需要在你的rss文件中include不同的loc就可以了。這個就是他系統實現機制的考慮所在。
            準備好了.RSS文件和.LOC文件并不是萬事大吉了,很多朋友在這里沒有試驗成功的一個訽因就是在.RSS文件的頭部,忘了加上
            注意在RSS文件重要加入
            CHARACTER_SET UTF8
            或者是忘記了把.LOC文件給轉換成UTF-8的格式。
            我目前使用的是Windows NotePad來轉換UTF-8格式的,需要注意是如果你使用的是英文版的Windows,那么它會在LOC文件的頭部添加3個前導標示字符,你需要再用UltraEdit[非UTF-8自動辨認模式]來刪除他們。


            Descriptor/String 字符串相關

            我們寫程序,幾乎很少不跟字符串打交道的,所以字符串這一塊兒自然成了一個平臺的很重要一塊兒。

            因為Symbian系統是面向移動設備,用設計者的話說就是:資源受限的智能設備,另外它是基于ROM和RAM操作的,所以他們對于字符串的處理也是采用 了一套自己獨創的方式來進行。這樣的機制,初學者乍看起來會很不習慣,摸不到頭腦,不知道他為什么要這么做。不過可以這樣說,跟她的內存資源處理機制相 比,字符串方面Symbian所作的改動還只是相當于換了一個名稱而已,對于ISV級的開發者來說,它是在眾多Symbian系統中眾多獨有特性中最好掌 握的幾種之一。

            在Symbian系統中,字符串被稱為Descriptor,你不用知道為什么,就把它當作你熟悉的string也就好了。因為要對字符串進行操作,所以 高級一些的平臺,都把字符串寫成了一個獨立的類,當作對象來對他們處理,而不像是C中,字符串是char[],然后有一批函數來處理它。又考慮到 Symbian系統所處理的字符串有在RAM中的也有在ROM中的,而RAM又是十分寶貴的,Symbian處理字符串提供了不止一個類,就在這里有了一 點點不是那么直接的地方。
            下面開始具體講解一下我的理解,

            在Symbian中,字符串的抽象類是TDes,它可以是TDes16業可以是TDes8,取決于你的程序是否處理Unicode。我們可以這樣理解, TDes就是char[],不過它不以'\0'結尾,而是把長度信息保存在了頭部,并且含有一個內存地址來表示他的位置。正如前面所述當今高級的平臺構 架,都會把字符串給分裝成一個類,我們是把字符串當作一個對象來處理的,所以抽象類我們是不能直接定義一個實例來使用的,抽象類的作用,在于傳遞函數的參 數,在這個時候我們可以把函數傳遞的參數當作最基本的抽象類來進行處理。那么要直接使用一個字符串,我們該使用什么呢?

            情況分為兩種,如果我們的字符串是比較短的,并且字符串的長度是相對已知的話,我們須要使用的是TBuf<len>,其中len表示長度。同 樣,TBuf是一個Unicode相關的類,他表示TBuf16或者TBuf8。定義這樣類型的一個字符串,我們使用這樣的語句
            TBuf<100> buf100;
            就可以定義一個字符串了。然后,參看TBuf類在SDK文檔中的參考說明,我們就可以使用這樣的字符串了。注意,使用TBuf是事先知道他的大概長度的, 使用的時候不能溢出,否則會出現程序錯誤而導致退出。另外,據說TBuf是被分配在了很寶貴的地方,所以盡量不要分配很長的TBuf,我想如果你的 TBuf長度超過了2000,哪怕是已知長度也最好換下面的第二種方式來分配好了。模擬器里面大家試驗東西的時候可以分配很大很大的,沒有問題,只不過用 在真機上面的時候就要小心了。

            另外一種,叫做HBufC,它是被分配在了Heap里面,可以在運行時才決定他的大小,不過也不能過大。定義一個HBufC我們可以使用下面的語句
            HBufC* heapBuf=HBufC::NewL(100);
            這樣我們就定義了一個長度為100的heap字符串,需要注意的有幾點。
            1,使用它地抽象類部分,也就是把它當作字符串使用,需要用他的Des函數來返回他的TDes類,比如
            heapBuf->Des()
            ,這樣來調用HBufC的字符串部分。
            2,因為Heap字符串是一個新分配的對象,所以你必須在使用完之后立即手動刪除它。我使用的是delete heapBuf;來刪除的,不過我覺得如果能夠使用Symbian系統中提供的CleanupStack庫來進行刪除的話,可能會更好。我因為對這一方面 還沒有什么研究,所以不敢在這里舉例子。
            3,他的大小仍然是需要注意的,我的程序中分配過兩個0x8000長的Heap字符串,沒什么大問題。很早很早年少無知的時候,曾綺在0.9SDK的模擬 器中一下子分配了1MegaByte的HBufC,沒有任何問題。但是真機上面?_?_,我沒有試驗過,大家小心嘗試~~~~

            最后還有一種叫做TPtrC這樣的東西,我們可以全當他是一個指針,指向一個TDes,其實可以當作是節省heapBuf->Des()的鍵盤消耗,我們 可以定義一個TPtrC pBuf;然后pBuf=heapBuf->Des(); 從此我們就可以把pBuf當作一個TDes來處理了,我很少用TPtrC,所以也不太熟悉,不敢多說了。

            需要注意的,
            ·在Symbian中,其實字符串都是按照Unicode編碼保存的TDes16。
            ·我不知道為何在有的時候,定義'\n'是管用的,可是在EDWIN中,我卻需要在TDes后面追價數值為0x2029的字符才可以換行。
            ·論壇中,或者其它網站[比如www.newlc.com],有介紹TDes16和TDes8之間的互轉,請注意如果他們不是中國人,pure chinese,請注意他們是否會忽略中文處理,僅僅是簡單的拋棄了高位為0的字節。如果是這樣方法,我們最好還是考慮一下。三思而后行。


            平臺號及產品號-pkg打包

            pkg file定義了安裝文件(sis)的內容,它包括應用程序的UID,一個支持的語言列表,目標產品的UID和打包在sis的一組文件:
            ; MyGame.pkg
            ; Specifies an installation file for MyGame
            ;Languages
            &EN
            ;Header
            #,(0x1000ABCD),1,0,0
            ; Required line for Series 60 devices. Defines the target product
            ; UID.
            (0x101F6F88), 0, 0, 0,
            “\epoc32\release\thumb\urel\MyGame.app”-“!:\system\apps\MyGame\MyGame.app”
            “\epoc32\release\thumb\urel\MyGame.rsc”-“!:\system\apps\MyGame\MyGame.rsc”
            “\epoc32\release\thumb\urel\MyGame.mbm”-“!:\system\apps\MyGame\MyGame.mbm”
            “\epoc32\release\thumb\urel\MyGame.aif”-“!:\system\apps\MyGame\MyGame.aif”
            “..\MyGame\MyGameSample.wav”-“!:\system\apps\MyGame\MyGameSample.wav”

            Product UID定義了應用程序的目標環境,大部分的s60版本是向下兼容的。
            參見下表:
            Nokia 7650 0x101F6F87
            Nokia 3650 0x101F7962
            Nokia 9210/9290 0x10005E33
            Nokia N-gage 0x101F8A64
            Siemens SX1 0x101F9071

            Series 60 Platform v0.9 0x101F6F88
            Series 60 Platform v1.0 0x101F795F
            Series 60 Platform v1.1 0x101F8201
            Series 60 Platform v1.2 0x101F8202
            Series 60 Platform v2.0 0x101F7960


            如果程序需要依據各不同的平臺來進行安裝,那就可以使用條件語句塊來處理,這時pkg里的語句如下:
            ;
            ; Files to install
            ;
            IF MachineUID=0x101fb3dd
            ; Nokia 6600 specific files
            “..\MyFiles\FileFor6600.dat”-“!:\system\apps\MyGame\MyData.dat”
            ELSEIF MachineUID=0x101f466a
            ; Nokia 3650 specific files
            “..\MyFiles\FileFor3650.dat”-“!:\system\apps\MyGame\MyData.dat”
            ELSE
            ; Files for other devices
            “..\MyFiles\FileForOthers.dat”-“!:\system\apps\MyGame\MyData.dat”
            ENDIF

            如上的使用你就可以生成一個支持多平臺的安裝文件,除了機器UID外,還有很多屬性,如內存和CPU的標識:

            注意,機器UID和Product UID是不同的,見下:
            Nokia 7650 0x101F4FC3
            Nokia 3650 0x101F466A
            Nokia 6600 0x101FB3DD
            Nokia 9210/9290 0x10005E33
            Nokia N-Gage 0x101F8C19
            Win32 Emulator 0x10005F62

            可以使用如下的代碼來找出該設備的機器UID:
            #include <hal.h> //and link with hal.lib
            TInt machineUid = 0;
            HAL::Get(HALData::EmachineUid, machineUid);


            File Manipulatating/文件操作

            其實這方面的操作相對于Symbian的某些其他部分來說還是跟其他平臺比較類似的,并無太大的不同。從最訽始的Standard C中的FILE結構到C++中的stream,再到目前的各種各樣的流行的語言、腳本什么的,對文件的操作無非是打開一個用字符串指定文件名的文件,給出 打開方式(Binary or Text, Read or Write),然后獲得“文件對象”,你可以當作這個文件的實例或者句柄什么的(Instance or Handle),知道意思就行了,反正就是這么一回事;p?_?_在我們目前的面向對象程序中,如果要對文件進行讀取或者寫入等操作,無非就是調用在這個 文件對象各種過程,比如寫入,讀取,Seek,之類的,等到用完了文件再用Close之類的過程關閉它以釋放資源。這個就是我們編成的時候大致的操作文件 的抽象描述。Symbian在這方面與其他平臺并無太大差別。

            下面結合Code說說具體的操作:
            上面說了Symbian在文件操作方面與其他平臺差別不大,但是全無差別也是不可能的。因為Symbian系統是為資源受限設備設計的,這一點我相信大家 都聽得耳朵起糨子了-_-,所以他的文件資源也是由系統管理的,我們要通過建立FileServer來與系統的文件服務通信,以建立客戶端--服務器模式 來訪問文件。說了那么玄,其實很簡單地,也就是說我們在純粹的使用文件部分的時候,之前要Connet一下FileServer,之后要Close一下 FileServer,就這么簡單。
            如:

            Code:

            RFs fs;
            User::LeaveIfError(fs.Connect());
            /*
            * Your File Manipulatating Code Here...
            */
            fs.Close();

            我的Code其實并不好,如果是Symbian老手,我相信即使是簡單的fs.Connect();也會有隨后馬上進行的CleanupStack類的操作,只是我目前還不熟悉,不敢妄自誤導大眾~~
            下面的就是文件了,我們在Symbian中使用的比較“低級”或者說“底層”的訽始文件對象是通過RFile類來實現的,大家只要參考一下 DeveloperLibrary的這個部分就清楚了,位置是:(» Developer Library » API Reference » C++ API reference » File Server Client Side » RFile)
            下面給兩個例子,來說明一下文件的打開,關閉,讀,寫,Seek等操作。

            Code:

            RFs fs;
            User::LeaveIfError(fs.Connect());
            RFile file
            User::LeaveIfError(file.Open(fs, _L("C:\\file.foo"), EFileWrite));
            TBuf8<256> buf;
            file.Read(buf, 256);
            file.Seek(ESeekStart, 911);
            file.Write(_L8("Some thing you wanna write..."));
            file.Close();
            fs.Close();

            注:以上代碼沒有綺過測試,沒有綺過編譯檢查,但是綺過了對照SDK DeveloperLibrary的檢查,技術上應該不會有什么問題。有兩點要說明的,在程序中寫死(HardCode)變量長度和位置什么東西還是最好 用MACRO代替,這可能是所有programmer的常識了,我并不是在教大家而是指名我的實例代碼中的不足;另一點就是_L8(), _L()這兩個MACRO是Symbian不支持大家再使用的了,大家最好用_LIT()或者_LIT8()來代替好了,此處僅僅為了方便使用了一下。
            大家可以很容易從DeveloperLibrary看出,RFile支持的讀寫只有TDes8這種類型,也就是說它只能以byte,或byte數組的形式 寫入或者讀出數據。如果大家想使用文本文件或者二進制數據文件的話,就要借助更高級的文件類了,他們的使用方法仍然是大同小異。
            TFileText提供了文本文件的讀寫,從他的API Reference中很容易看出來,在我們建立好的TFileText對象上,只要Set一個RFile到它本身上面,我們就可以用這個 TFileText對象來很容易的對文件(就是底層的RFile)來進行文本文件的讀寫了。
            與此類似,RFileReadStream對象,在構造的時候只需指明構建在哪個RFile對象上,我們就可以方便的在這個RFile對象上進行數據的操 作,比如讀出一個32bit的整數,讀出一個64比特實型數。RFileWriteStream進行的是寫入方面的操作,操作方式與讀入類相同,就不贅述 了。

            從上面的例子和說明我們可以看出,Symbian系統的文件操作幾乎與其他平臺沒什么太大的不同,也是符合所有的抽象文件操作要求的。對文件的操作只要我們看看DeveloperLibrary,記住幾個常用的過程,就沒有什么困難的了。

            希望我寫的文字能給大家帶來些幫助!


            為 kcomex 補充兩點:
            1、symbian的所有文件名中最好不好含有空格等特殊字符,負責編譯可能出錯,我用0.9SDK是這樣的。
            2、如果編譯的時候出現了你不知道的文件夾,請將mmp文件所在的文件夾中的.bat和.inf文件刪掉,用.mmp重新生成這兩個文件。我一般都是使用 mmpclick這個工具來生成的,因為命令行畢竟不方便。關于mmpclick的使用,在SDK附帶的工具中有詳細的說明。

            如何給模擬器發短信

            建議從諾基亞論壇上下載安裝"Nokia Connectivity Framework"。我不知道它是否支持在真機和仿真器之間互發短信,但我用它在兩個仿真器之間互發過短信,下面簡要說一下當時的配置過程。

            1. 安裝SDK
            安裝S60 SDK 2.2和S60 SDK 2.3
            安裝"Nokia Connectivity Framework 1.2"

            2. 配置環境
            打開"Nokia Connectivity Framework",將左側窗口中的"Projects"->"Terminal SDKs"->"Series 60 2nd Ed. SDK for Symbian OS FP2 ...",和"Projects"->"Terminal SDKs"->"Series 60 2nd Ed. SDK for Symbian OS FP3 ..."拖到右側的窗口中,此時右側窗口中出現兩個仿真器的圖標,圖標下側列出了該仿真器的藍牙地址和MMS/SMS號碼。
            然后用鼠標從其中一個圖標向另一個圖標引一條連線。

            3. 啟動環境
            分別在兩個仿真器圖標上單擊右鍵,然后從彈出式菜單中選擇"Start Product"啟動仿真器。

            4. 發送短消息
            進入某個仿真器的"Messages"應用程序,編寫一條短信,其中"to:"域填寫另一個仿真器的SMS號碼,然后發送該短信。首次發送時會彈出對話框要求設置服務中心的號碼,隨便填個數(我填的1),點OK就行了。

            5. 接收短消息
            打開另一個仿真器的"Messages"應用程序,就能看到"Inbox"收到了剛才發的短信。


            symbian下面制作DLL 的流程

            首先咱們假設要封裝一個叫做CMyClass的東西。先叢工程文件入手:

            MyClass.mmp
            -------------------
            代碼:

            TARGET MyClass.dll
            TARGETTYPE  dll
            UID 0x1000008d 0x10004268 //注意,這里換上你的UID
            SOURCEPATH   ..\src
            SOURCE   MyClass.cpp
            SYSTEMINCLUDE   .
            SYSTEMINCLUDE   \epoc32\include
            SYSTEMINCLUDE   \epoc32\include\libc

            LIBRARY euser.lib
            #if defined(WINS)
            deffile .\MyClassWINS.def
            #else if defined(ARM)
            deffile .\MyClassARM.def
            #endif

            NOSTRICTDEF
            EXPORTUNFROZEN



            好,一半已經搞定了,再堅持一下。

            MyClass.h
            --------------
            代碼:

            class CMyClass : public CBase
            {
            public: // 這些IMPORT_C開頭的家伙就是我們可以從別的程序中調用的函數

            IMPORT_C static CMyClass* NewL();
            IMPORT_C static CMyClass* NewLC();
            IMPORT_C ~CMyClass();
            public:
            IMPORT_C void DoSomething();
            private:
            CMyClass();
            void ConstructL();
            };



            很晚了,我偷個懶,上面的代碼只寫了重要的部分,那些個#include什么的麻煩看官們自己補齊吧。

            MyClass.cpp
            -----------------
            代碼:

            // DLL Entry Point。這是最重要的東西,別忘了哦,每個DLL都需要它。
            GLDEF_C TInt E32Dll(TDllReason /*aReason*/)
            {
            return(KErrNone);
            }

            EXPORT_C void CMyClass::DoSomething()
            {
              // 好,我承認,這個函數也許應該叫做DoNothing才對。:-)
              return ETrue;
            }

            EXPORT_C CMyClass* CMyClass::NewL()
            {
               CMyClass* self = NewLC();
               CleanupStack::Pop(self);
               return self;
            }
            ...


            其實大家注意到了,問題關鍵就在這對IMPORT_C...EXPORT_C上。這些函數就是你的DLL所定義的API接口!

            好了,基本完成了,我們編譯它!abld build wins udeb

            編譯結束后,你會在epoc32的那堆目錄下找到一個MyClass.lib以及生成的DLL! 是的,就這么簡單![/code]


            應用程序總是中斷的分析


            檢查內存泄漏的方法:

            __UHEAP_MARK;

            你得代碼中需要檢查的部分

            __UHEAP_MARKEND;


            無原因退出主要有以下幾個原因:
            1、沒有處理Leave異常,也就是有沒有被Trap的Leave函數。所有的Leave函數必須在程序的某個地方被TRAP, TRAPD, 或TRAP_IGNORE涵蓋到。這是最初要的原因
            2、訪問了非法的內存區域
            3、修改了非法的內存區域導致系統服務出錯。這個問題可以在Symbian 9以后實現Platform Security之后解決
            4、沒有找到需要的庫。缺少所需要的DLL文件等等
            5、錯誤的資源文件,RSC文件的版本和程序中調用所需的資源文件不一致
            6、調用了系統不支持的功能。例如類似調用Camera功能的時候要考慮到不同手機產品相機的差別,需要安裝相應的FP (Feature Pack)



            如何運行app和exe程序

            Symbian有2種類型的本地程序:
            APP是有GUI的程序,因此能夠被終端用戶使用
            EXE通常是服務端或命令行程序,通常隱蔽的運行。沒有GUI,不能直接從主菜單運行

            運行指南

            如果你是一個終端用戶想運行APP:它會在你的電話菜單中列出如果它已經安裝

            當一個EXE程序在主菜單不可見時不能直接運行EXE程序。試著從INBOX運行它(如果你通過紅外或藍牙下載過它,它可能存儲在INBOX里)會導致一 個安全錯誤。首先,你需要安裝一個文件管理器(比如FileMan或FExplorer),瀏覽它存儲的位置(在我的3650上INBOX的目錄在E:\ system\Mail\xxx),然后運行它。

            運行程序
            當你知道運行APP或EXE程序使用哪個API后是非常簡單的事。

            運行EXE程序:
            #include
            ...
            _LIT(KMyAppName, "c:\\system\\Apps\\MyApp\\MyApp.exe");
            EikDll::StartExeL(KMyAppName);

            下面的代碼運行APP比較復雜但允許執行特定的文檔
            #include
            #include
            ...
            _LIT(KMyAppName, "c:\\system\\Apps\\MyApp\\MyApp.app");
            _LIT(KMyDocName, "c:\\Documents\\MyApp.dat");

            CApaCommandLine * cmd=CApaCommandLine::NewL();
            cmd->SetLibraryNameL(KMyAppName);
            cmd->SetDocumentNameL(KMyDocName);
            cmd->SetCommandL(EApaCommandRun);
            EikDll::StartAppL(*cmd);

            運行瀏覽其他的NOKIA程序
            如果你打算開始基于Series 60的ROM 程序,在NOKIA論壇查找關于外部程序查看文檔的問答可以給你帶來收獲

            下面的代碼將開始瀏覽特定的頁:
            #include // apgrfx.lib

            void NNewLCUtils::StartBrowser(const TDesC& aUrl)
            {
              HBufC* param = HBufC::NewLC( 256 );
              param->Des().Format( _L( "4 %S" ),&aUrl );

              // Wap Browser's constants UId
              const TInt KWmlBrowserUid = 0x10008D39;
              TUid id( TUid::Uid( KWmlBrowserUid ) );

              TApaTaskList taskList( CEikonEnv::Static()->WsSession() );
              TApaTask task = taskList.FindApp( id );
              if ( task.Exists() )
              {
                HBufC8* param8 = HBufC8::NewLC( param->Length() );
                param8->Des().Append( *param );
                task.SendMessage( TUid::Uid( 0 ), *param8 ); // Uid is not used
                CleanupStack::PopAndDestroy(); // param8
              } else{
                RApaLsSession appArcSession;
                User::LeaveIfError(appArcSession.Connect()); // connect to AppArc server
                TThreadId id;
                appArcSession.StartDocument( *param, TUid::Uid( KWmlBrowserUid ), id );
                appArcSession.Close();
              }
              CleanupStack::PopAndDestroy(); // param
            }




            將你的程序帶到前臺或后臺

            這篇文章將向你展示如何在你的程序得到或失去屏幕焦點的時候控制它們和怎樣控制它們。

            在焦點改變的時候開始。Series 60系列的框架將在程序得到或失去屏幕焦點的時候通過CAknAppUi::HandleForegroundEventL(TBool aForeground)發出通知。當你的程序得到焦點的時候參數aForeground為ETrue,失去焦點的時候為EFalse。

            如果你需要做一些特定的操作,你需要重載這個函數。這有一個不失去焦點的例子
            void CMyAppUi::HandleForegroundEventL(TBool aForeground)
            {
              // Call Base class method
              CAknAppUi::HandleForegroundEventL(aForeground);

              if(aForeground) {
                // We have gained the focus
                ...
              } else {
                // We have lost the focus
                ...
              }
            }


            改變焦點。你總是能夠請求改變你程序的焦點使用命令TApaTask::SendToBackground() and TApaTask::BringToForeground()。下面代碼片段顯示怎樣從AppUi使用它們:
            void CMyAppUi::BringToForeground()
            {
            // Construct en empty TApaTask object
            // giving it a reference to the Window Server session
            TApaTask task(iEikonEnv->WsSession( ));

            // Initialise the object with the window group id of
            // our application (so that it represent our app)
            task.SetWgId(CEikonEnv::Static()->RootWin().Identifier());

            // Request window server to bring our application
            // to foreground
            task.BringToForeground();
            }

            我沒有測試下面的代碼,但你可能可以使用下面的代碼控制其他的程序:

            // Bring the application "theApp" to background
            TApaTaskList tasklist(iCoeEnv->WsSession());
            TApaTask task(tasklist.FindApp(_L("theApp")));
            task.SendToBackground(); // or BringToForeground()


            Symbian 程序編譯的過程


            1. C++ BuilderX
            Symbian的開發環境似乎不是那么容易配置.不過Borland的C++ Builder對Symbian的支持比較好,里面還有專門針對Symbian開發的工具選項呢.不過由于Microsoft Visual C++我用得比較熟悉,而且,有Visual Assist這樣強大的工具支持,所以我覺得還是在Microsoft Visual C++上開發比較適合我.
            2. 命令行的編譯
            命令行的編譯應該是SDK的文檔中有講解的.針對Symbian SDK中的Series60Ex的Graphics的例子還說吧.
            a) 首先我們得用命令行到Series60Ex\Graphics\Group這個目錄.然后需要設置VC的環境變量,最好的辦法就是直接運行\ Microsoft Visual Studio .NET 2003\Vc7\bin\vcvars.bat這個批處理文件.我的做法就是Group這個目錄建立一個command.bat文件,里面的內容如下
            PATH=%PATH%;D:\Microsoft Visual Studio .NET 2003\Vc7\bin
            Cmd
            然后,我只要在Windows里雙擊這個文件,就會自動到這個目錄的命令提示符下,然后輸入vcvars32 + 回車,就設置好了VC的環境變量.
            b) 輸入bldmake bldfiles.  是基于perl語言的操作,所以說一定要裝Perl才用使用SDK.
            c) 如果是編譯生成模擬器運行的,那么使用
              abld build wins udeb
                如果是編譯生成機器上跑的,那么就使用
              abld build armi urel
            編譯后,有兩個目錄是它給你生成的.如果你想重新生成,可以通過手動刪除這兩個目錄.

            工程編譯目錄:
            \Symbian\6.1\Series60\Epoc32\BUILD\SYMBIAN\6.1\SERIES60\SERIES60EX\GRAPHICS\GROUP
            保存的編譯的MAKE文件, OBJ文件等等.

            程序在模擬器的目錄
            \Symbian\6.1\Series60\Epoc32\Release\wins\UDEB\Z\SYSTEM\apps\graphics
            保存了模擬器上運行的App文件,只要刪除這個目錄,模擬器上就沒有這個程序了.

            3. 全局非靜態變量引起的錯誤
            我在view的cpp的增加了一個Tint x;在模擬器上運行沒有任何問題.當我用armi方式編譯到目標機器上的時候,出現了一個奇怪又經典的錯誤.
            PETRAN - PE file preprocessor V01.00 (Build 183)
            Copyright (c) 1996-2001 Symbian Ltd.

            ERROR: Dll 'Graphics[10005BBE].APP' has initialised data.
            make[1]: *** [..\..\..\EPOC32\RELEASE\ARMI\UREL\GRAPHICSAPP] Error -2
            make: *** [TARGETGRAPHICS] Error 2
            make -r -f "\Symbian\6.1\Series60\EPOC32\BUILD\SYMBIAN\6.1S\SERIES60\SERIES60EX\BMPMANIP\GROUP\ARMI.make" FINAL CFG=UREL VERBOSE=-s

            解決辦法: 1.把全局非靜態變量變成全局靜態變量,static
                2.把全局變量編成類里面的成員變量

            這樣個問題估計是Symbian的書上說的那樣,編譯成ARMI最后目標的時候,編譯器要對代碼中的內存使用進行嚴格檢查所造成的.
            http://discussion.forum.nokia.com/forum/showthread.php?s=&threadid=27446


            從Symbian 9設備的文件管理器運行EXE文件

            Symbian 9 設備將所有的Exe文件定位與“/sys/bin”文件夾下,大多數情況下,我們不能從文件管理器來訪問它。在這里有一個很好的方式來實現如何從一個S60 3rd的設備上啟動exe文件。

            當我們初次拿到S60 3rd設備,準備移植程序到這個平臺時,我們多次發現一些問題。在PKG文件腫,會發現一些錯誤,exe文件被寫入錯誤的文件夾。在之前的版本中,exe 文件放在“/sys/bin“目錄下,使用我們的SysExplorer(Eric Bustarret開發的文件管理系統),我們嘗試啟動那些可見的exe文件,我們驚奇的發現,它啟動了,相當與在“/sys/bin”下。

            因此,如果你真的需要啟動exe文件:
            1。拷貝“/sys/bin”下的exe文件
            2。復制它到一個可見目錄
            3。使用SysExplorer,到該目錄運行它



            CCoeControl類中Draw()函數的調試

            自定義控件往往通過重載CCoeControl:: Draw() 方法來實現它的填充。然而,當你單步調試代碼的時候,每個指令沒有立即顯示到屏幕上,因為它是通過window服務實現的。

            慶幸的是,你可以改變它,你可以將每次畫圖都立即顯示到屏幕上,添加這段代碼到你的AppUi::ConstructL()中:
            CODE:
            #ifdef _DEBUG
            iEikonEnv->WsSession().SetAutoFlush(ETrue);
            #endif

            這段代碼將強制每個圖形上下文繪制命令,立即顯示出來,這樣可以避免window服務緩沖后刷新。

            這意味著,你可以看到每次畫圖代碼而產生的效果,到發布版中,記住去掉它,否則他將影響程序的運行效率。


            在S60 3rd的手機上顯示所有已安裝程序的Uid

            一個應用程序它可以顯示設備上所有已安裝程序的Uid。

            該程序從S60 2.x 移植到 S60 3.x

            S60 2.x的源代碼可以在Forum Nokia 找到,下面的代碼是S60 3.0的。

            概要:

            以下代碼將顯示手機上安裝的應用程序的Uid,它使用了下面的類。
            CODE:
                    RApaLsSession iLsSession;
                    MAppUidObserver& iObserver;
                    RArray<TAppInfo> iApps;



                   class TAppInfo
                    {
                             public:
                      TInt32 iAppUid;
                      TApaAppCaption iAppCaption;               
                    };

                     void CAppUidViewerEngine::AppsToUiL()
                    {
                    TApaAppInfo apaAppInfo;
                    TAppInfo appInfo;
                    iApps.Reset();

                    // Get info on all apps, then iterate through each app
                    User::LeaveIfError(iLsSession.GetAllApps());
                    while(iLsSession.GetNextApp(apaAppInfo) == KErrNone)
                            {
                            appInfo.iAppCaption = apaAppInfo.iCaption;
                            appInfo.iAppUid = apaAppInfo.iUid.iUid;
                            User::LeaveIfError(iApps.Append(appInfo));
                            }
                           
                    //  iObserver.AppsFoundL(iApps);
                    }

            以上主要代碼。

            這里是源代碼文件: [attach]531[/attach]



            在運行時為S60程序提供多國語言支持

            我準備示范,如何使應用程序在運行時支持多國語言。下面的例子中,我使用了3種語言:英語、法語和德語。

            修改MMP文件

            第一步是修改MMP文件。在MMP文件中你可以看到這樣一行:


            CODE:
            LANG                SC

            修改成:


            CODE:
            LANG                01 02 03

            創建3種語言文件

            我們將支持3種語言,英語、法語、德語,本地文件的擴展名分別為:l01,l02和l03。你可以在本地化使用所有的字符。

            修改LOC文件

            現在你需要修改成下面的LOC文件


            CODE:
            // 01 = (British) English
            #ifdef LANGUAGE_01
                    #include "MultiLang.l01"
            #endif

            // 02 = French
            #ifdef LANGUAGE_02
                    #include "MultiLang.l02"
            #endif

            // 03 = German
            #ifdef LANGUAGE_03
                    #include "MultiLang.l03"
            #endif

            修改YourAppAif.rss文件

            YourAppaif.rss 文件中需要像這樣本地化你的應用程序標題:


            CODE:
            RESOURCE AIF_DATA
                {
                caption_list=
                            {
                            CAPTION { code=01; caption="MultiLang"; }, // Eglish
                            CAPTION { code=02; caption="MultiLang"; }, // French
                            CAPTION { code=03; caption="MultiLang"; }  // German
                            };
                           
                app_uid=0x02a5dd83;
                num_icons=2;
                embeddability=KAppNotEmbeddable;
                newfile=KAppDoesNotSupportNewFile;
                }

            修改由CAknApplication繼承來的類

            重寫CEikApplication 中的 ResourceFileName() 函數到你的CYourApplicationApp 類.


            CODE:
            TFileName CMultiLangApp::ResourceFileName() const
                    {
                    return TFileName();
                    }

            修改AppUi 類

            首先,你需要為AppUi 類中的 ConstructL()函數的BaseConstructL() 中傳遞ENonStandardResourceFile 參數:


            CODE:
            void CMultiLangAppUi::ConstructL()
                {
                BaseConstructL( ENonStandardResourceFile );
                //...
                }

            接著,在你的AppUi類中創建下列新的函數:


            CODE:
            void CMultiLangAppUi::ChooseLanguageL(TInt aLanguageIndex)
                    {
                    _LIT(KResourceFileName, "MultiLang.r%02d");
                    TFileName resFileName;

                    resFileName.Format(KResourceFileName, aLanguageIndex);

            #if !defined(__WINS__) && !defined(__WINSCW__)
                    // Device
                    CompleteWithAppPath(resFileName);
            #else
                    // Emulator
                    resFileName.Insert(0, KEmulatorPath);
            #endif

                    if (iOffset) iCoeEnv->DeleteResourceFile(iOffset);
                    iOffset = iCoeEnv->AddResourceFileL(resFileName);
                    }

            現在你可以調用 ChooseLanguageL([LanguageIndex]) 函數來實現語言的切換了。

            修改PKG文件

            添加以下幾行到你的打包文件中。
            CODE:
            "\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\MultiLang\MultiLang.r01"         -"!:\system\apps\MultiLang\MultiLang.r01"
            "\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\MultiLang\MultiLang.r02"         -"!:\system\apps\MultiLang\MultiLang.r02"
            "\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\MultiLang\MultiLang.r03"         -"!:\system\apps\MultiLang\MultiLang.r03"
            "\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\MultiLang\MultiLang_caption.r01" -"!:\system\apps\MultiLang\MultiLang_caption.r01"
            "\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\MultiLang\MultiLang_caption.r02" -"!:\system\apps\MultiLang\MultiLang_caption.r02"
            "\Symbian\7.0s\Series60_v21\Epoc32\data\z\system\apps\MultiLang\MultiLang_caption.r03" -"!:\system\apps\MultiLang\MultiLang_caption.r03"


            如何判斷程序是否被自動啟動或者被用戶啟動

            有時候能夠檢查出在系統啟動的時候哪些應用程序被“Startup List Api(只有S60 3rd可以使用)”或者用戶啟動,和通過“Symbian Signed”,管理啟動程序的應用是一個很通用的程序,這篇文章將告訴你如何做到。
            首先,你必須修改程序注冊文件,并在APP_REGISTRATION_INFO資源中添加一些opaque_data字段,內容和直并不是那么重要,你只需要按下面的方式指定:
            CODE:

            #include <appinfo.rh>
            #include <uikon.rh>   

            RESOURCE APP_REGISTRATION_INFO
            {
              ...
              opaque_data = r_startup_detect;
            }      

               
            RESOURCE NUMBER_INT8 r_startup_detect
            {
            value = 1;
            }

            當應用程序運行時,opaque_data和其他運行參數將被忽略。因此,判斷是否在運行或者不允許(區分是否應用程序被允許在啟動的時候或者被用戶啟動)。

            這樣,你可以重載AppUI中的ProcessCommandParametersL()函數
            T
            CODE:

            Bool CMyAppUi::ProcessCommandParametersL( CApaCommandLine &aCommandLine )
            {
              if(aCommandLine.OpaqueData().Length() > 0)
              {
                  // Opaque data exists, app. has been manually started from the menu
              }
              else
              {
                  // App. has been auto-started -> exit if auto-start in settings is OFF
              }
               return CEikAppUi::ProcessCommandParametersL( aCommandLine );
            }
            你也要注意,通過Symbian Signed需要你的應用程序有一個允許禁止自動啟動的屬性的選項設置。你需要加入操作代碼來實現它,在 Forum Nokia Technical Library中描敘得比較好。

            這篇文章僅適用于 S60 3RD,接下來的版本也將支持這個屬性


            如何在SYMBIAN60中編寫DLL

            Prerequisites
            Knowledge of C++ and COM.
            Knowledge of Symbian Programming idioms
            Introduction
            Writing a polymorphic DLL involves the following steps:

            Design and write interfaces.
            Implement the interface.
            Lets explore these in more detail:

            Design and write interface consisting of method declarations.
            An interface is essentially an abstract class. An example would clarify it:

            class ICalculator {public:virtual TInt Sum(TInt, TInt) = 0;};
            Above snippet declares a class ICalculator with one method Sum which would take 2 TInt parameters and would return a TInt value. Take a look at the name given to the class. This is done to follow a convention i.e. an interface name should start with an 'I'. Now if one wishes to implement interface ICalculator, he/she has to implement the method Sum.

            COM programmers would be wondering that how it can be an interface without a UUID or something like that. Yes! in Symbian too, an interface requires a UUID. While writing an interface this is how UUID is given to it.

            static const TUid KCalculator_DLLUID = {0x100039CE};static const TUid KCalculator_DLLUID_Ver1 = {0x0352D96B};class ICalculator {public:virtual TInt Sum(TInt, TInt) = 0;};
            Your polymorphic interface is ready. You have to write it in a .h file so that it can be implemented in other .cpp files.

            Implement the interface.
            Writing a polymorphic DLL is half done when interface declaration is done. Now one would require to implement it. Implementing an interface is as simple as inheriting abstract classes. You have to inherit from an interface and implement pure virtual methods of the interface.

            Again an example would help us understand this:

            class CImpCalculator: public ICalculator {public:EXPORT_C CImpCalculator* NewImpClass();//function would be exposed through Function tableTInt Sum(TInt num1,TInt num2);};GLDEF_C TInt E32Dll(TDllReason) {return KErrNone;}EXPORT_C CImpCalculator* CImpCalculator::NewImpClass() {return new (ELeave) CImpCalculator;}TInt CImpCalculator::Sum(TInt num1,TInt num2) {return num1 + num2;}
            The example above does something more than just implementing the interface. The class CImpCalculator has a method of its own, NewImpClass. This method has a qualifier EXPORT_C. Clearly this is the method which is exposed from the implementation class. In Symbian it is not required to export or expose all the methods from an interface. Only method which needs to be exposed is which returns the object of the interface (or should I say the implementor class?). Rest of the methods can be accessed from function table. The NewImpClass method returns a pointer to the newly created object.

            E32Dll method has to be written in every Polymorohic DLL as it is required by the Symbian run time. You would get linking errors in case you forget to write it. This method is responsible to allocate thread local store for the DLL instance.

            Now you are ready to compile and build your own DLL. Build it and start using it.

            Lastly these DLLs are called polymorphic because one can write many implementations of the same interface. Further all the Symbian GUI applications are Polymorphic DLLs. Check the GUI SDK examples, you will find E32Dll method implemented.

            That's all you would require to write a DLL on Symbian!! Note that I have tested the code on NOKIA Series 60 emulator.

            定義類的問題

            問題:
            我使用CAknView定義了一個View類,
            class CCameraAppView : public CAknView.....
            并且在這個類中添加了一個自定義的CCoeControl的子類成員用來繪圖等。
            CCoeControl的子類定義如下:

            class CCameraAppBaseContainer : public CCoeControl, MCoeControlObserver, public MAknTabObserver,MConverterController
            其中MConverterController是一個自定義的純虛類,用做接口用。

            開始的時候我是將MConverterController寫在了MAknTabObserver的前面,即寫成了:
            class CCameraAppBaseContainer : public CCoeControl, MCoeControlObserver, public MConverterController,MAknTabObserver
            這時候在使用winscw udeb方式編譯的時候不會出現任何錯誤,但是當使用gcce urel方式編譯的時候卻會出現如下錯誤:
            Link Error.rodata+0xf4):undefined reference to 'non-virtual thunk to CCameraAppBaseContainer::TabChangedl(int)'
            當時研究了很久也沒弄清楚是怎么回事,后來將MConverterController和MAknTabObserver的順序改過來,就沒有錯誤了。

            這個錯誤的產生到底是什么原因呢?

            解決:
            MAknTabObserver類只有一個公有的成員函數TabChangedL(),第一種寫法因為沒有寫明從MAknTabObserver的繼承方 法,所以是private繼承,也就是所有的成員函數是派生類的私有函數,所以外部無法訪問TabChangedL()這個私有函數.聲明派生類時明確指 出派生方式(public,private,protected)是一個好習慣.


            如何才能實現這個非同期處理

            問:
            我要實現如下3個函數處理
            GetData(TInt aRequestId) //取得數據,之后NotifyResult要使用這個requestID去通知處理結果
            NotifyResult(TInt aRequestId) //通知取得的結果,這個requestID要和GetData的相一致
            CancelRequest(TInt aRequestId)//根據requestID取消之前的某個請求

            現在處理流程是這樣的,有很多的請求可以同時發出,那么我該如何根據不同的requestID去實現Cancel呢?
            GetDataA
            GetDataB
            GetDataC
            NotifyResultA
            NotifyResultB
            NotifyResultC
            ---------------------------------------------

            答:
            同時發出多個導步請求需要創建相應數量的活動對象實例,當然也可以創建一個支持請求隊列的活動對象來實現這個需求。

            問:
            我起初也想針對每一個請求創建一個活動對象。
            不過不清楚用什么異步請求去激發每個活動對象。
            初步設想是
            class CMsgItem::Public CActive
            CMsgItem::RunL
            {deal with the request();
            NotifyResult();
            }

            CSample::CreatMsgItem
            {
            a = New CMsgItem;
            //★這里用什么去發出異步請求呢?原來想用Timer.After,但是覺得不妥。
            a.SetActive();
            }

            后來又打算使用消息隊列去保存請求,然后在CIdle長線任務處理中提取msg,去處理。

            不過我還是希望用第一種方法去做,
            關于如何發出異步請求請指點一下,除了timer還能使用別的嗎?
            創建出來了這些活動對象,在什么時機刪除呢?能否在Runl中調用"delete this"把自己刪除?
            考慮到有可能會在RunL處理中彈出選擇對話框,有問題嗎?(考慮到RunL內部不能停留太長時間)
            還有有什么需要注意的地方,也請賜教。
            -------------------------------------------

            答:
            活動對象的請求操作接口負責發出異步請并調用SetActive(),然后在RunL()中調用通知的函數.這個通知函數的接口可以添加一個活動對象本身的指針做為參數,這樣就可以在接收通知的函數中區分出是哪個活動對象發出的請求了,就像Listbox的觀察器一樣:
               Code:
            class MEikListBoxObserver
            {
            public:
            enum TListBoxEvent
            {
            EEventEnterKeyPressed,
            EEventItemClicked,
            EEventItemDoubleClicked,
            EEventItemActioned, // reported by dir tree and dir contents listboxes
            EEventEditingStarted,
            EEventEditingStopped,
                    EEventPenDownOnItem,
                    EEventItemDraggingActioned
            };
            public:
            virtual void HandleListBoxEventL(CEikListBox* aListBox, TListBoxEvent aEventType)=0;
            };


            Symbian中的線程、進程及同步

            全局內存塊:跨越多個進程直接訪問的內存塊。
            創建自己的全局內存塊可以通過Rchunk API類
            Rchunk chk;
            _LIT(KChunkName,"My Globla Chunk");
            TInt rc=chk.CreateGlobal(KChunkName,0x1000,0x5000);
            其中CreateGlobal()方法第一個參數指定全局內存塊的名稱。后面兩個參數為塊指定分配給它的物理RAM和為塊保留的虛擬內存的數量。
            再其他進程中要訪問全局內存塊可以這樣操作。
            Rchunk chk;
            _LIT(KChunkName,"My Globla Chunk");
            TInt rc=chk.OpenGlobal(KChunkName,0);
            TInt *ptr=(TInt *)chk.Base();
            可以通過*ptr直接讀寫這塊內存。
            RChunk::OpenGlobal()第一個參數指定了全局內存塊的名稱,第二個參數用于說明塊是為只讀(1)還是可寫的(0)
            可以通過RChunk::Ajust(Tint newsize)方法來擴大塊的提交內存尺寸。
            TInt rc=chk.CreateGlobal(KChunkName,0x1000,0x5000);
            例如上面創建了一個塊,RAM提交給它0x1000字節,塊的最大尺寸是0x5000
            在這種情況下,只有用于讀寫的0x1000字節的物理RAM分配給了塊。但是隨后可以擴充該塊,例如:
            chk.Ajust(0x3000)
            現在塊被分配了0x3000字節的內存。

            信號量:
            全局信號量:
            創建全局信號量
            RSemphore::CreateGlobal(const TDesc &aname,TInt aCount,TOwnerType aType=EOwnerProcess);
            第一個參數是信號量的名稱,第二個參數是信號量的標記計數。
            第三個參數指定句柄的所有權。
            EOwnerProcess標記該信號量句柄可以在進程的任何位置進行訪問。
            EOwnerThread表示它只能被創建的線程訪問。
            打開全局信號量
            RSemphore::OpenGlobal(const TDesc &aname,TOwnerType aType=EOwnerProcess);或者
            RSemphore::Open(const TFindSemphore& aFind,TOwnerType aType=EOwnerProcess);
            第一個函數通過信號量全名打開它。
            第二個函數使用TFindSemphore類,通過包含通配符字符的部分名稱打開它。
            例如_LIT(KMatchName,"MySemphore*");
            TFindSemphore semName(KMatchName);
            RSemphore::Open(semName)
            打開一個全局的信號量。
            RSemphore::Signal()給信號量的標記數加1
            RSemphore::Wait()給信號量的標記數減1
            如果Wait()發現減后的標記數為負值,則Wait()阻塞,直到調用Signal()增加標記計數才返回。

            (1)使用信號量可以做為一個直接信號,控制不同進程之間執行的流程。
            (2)信號量也可以用來保護共享資源。
            本地信號量:
            創建本地信號量
            TInt CreateLocal(Tint aTokenCount,TOwnerType aType=EOwnerProcess);
            本地信號量沒有名字,不需要打開,通過創建它的RSemphore,就可以簡單地訪問它。
            注意,如果把aType指定為EOwnerThread,但又想在另外一個線程中使用信號量,那么,就必須使用Duplicate()方法,為該線程創建句柄的副本。可以查看SDK文檔中的RHandleBase::Duplicate()

            symbian中的幾個API

            1. 定位當前程序,并將當前程序的優先級調高

            TInt prio = 1001; //設置一個較高的值

            //將當前程序的窗口組設計一個高的優先級,并置為同級最前
            CEikonEnv::Static()->RootWin().SetOrdinalPosition(0, prio);

            2.將當前程序的窗口組設置為最前, 可與HandleForegroundEventL 配合使用,使當前程序使終處于最前(條件是優先級要有足夠高,否則的話還是會被優先級更高的程序搶占)

            RWsSession ws = CEikonEnv::Static()->WsSession();
            TApaTaskList tlist(ws);
            TApaTask task = tlist.FindApp(KUidMyApp);  //KUidMyApp 是指要調高優先級的程序的ID
            task.BringToForeground();

            或者是:

            TApaTaskList taskList(CCoeEnv::Static()->WsSession());
            TApaTask currentTask = taskList.FindByPos(0);
            TApaTask ourAppTask = taskList.FindApp(KUidNightClockApp);
            if(currentTask.ThreadId() != ourAppTask.ThreadId())
              ourAppTask.BringToForeground();

            3.模擬一個按鍵消息,并發向指定窗口組

            RWsSession sess=CCoeEnv::Static()->WsSession();
            TWsEvent event;
            TInt id=sess.FindWindowGroupIdentifier( 0, _L("*Phone?") ); //取得電話程序的窗口組

            event.SetType(EEventKey);
            event.SetTimeNow();
            event.Key()->iCode = EKeyDownArrow; //模擬一個向下的箭頭按鍵
            event.Key()->iModifiers = 0;
            event.Key()->iRepeats = 0;
            event.Key()->iScanCode = EStdKeyNull;
            sess.SendEventToWindowGroup( id, event ); 將模擬的按鍵消息發給窗口組

            給pys60以不同的屏幕顯示方式

            如下就可以做到,我喜歡用全屏,嘿嘿
            首先記得要裝入appuifw模塊

            >>>import appuifw

            然后可以選擇你想要的顯示方式

            >>>appuifw.app.screen='normal' #(a normal screen with title pane and softkeys)
            >>>appuifw.app.screen='large' #(only softkeys visible)
            >>>appuifw.app.screen='full' #(a full screen)


            Symbian平臺編碼問題

            自己在開發時遇到的一些問題:<!--[if !vml]-->

            1.  <!--[endif]-->將Symbian應用程序改為中文名稱
                • 修改資源文件xxx_caption.rss:
                    Code:
            #include “xxx.loc”
            RESOURCE CAPTION_DATA
            ...{
                caption = qtn_app_caption_string;
                shortcaption = qtn_app_short_caption_string;
            }
                • 打開xxx.loc 用記事本或UltraEdit轉成UTF-8編碼
                    Code:
            CHARACTER_SET UTF8    //必須加這行

            #define qtn_app_caption_string “程序中文名”
            #define qtn_app_short_caption_string “顯示名”

            2.在Nokia 7610中文機上顯示Shift-JIS編碼的日文串
                • Symbian平臺以Unicode編碼,可以表示、顯示所有字符,只需將本地編碼轉為Unicode即可。
               • 因為中文機上沒有日文字庫,所以不能用類CcnvCharacterSetConverter提供的方法
                 (但可實現GBK -> Unicode)
                Code:
            CCnvCharacterSetConverter* converter = CCnvCharacterSetConverter::NewLC();
            converter->PrepareToConvertToOrFromL(KCharacterSetIdentifierShiftJis, CEikonEnv::Static()->FsSession());
            /***************************************************
            *上一行會異常退出,因目標平臺沒有SJIS字庫
            *但可以實現GBK -> Unicode,只需將KcharacterSetIdentifierShiftJis
            *改為KCharacterSetIdentifierGbk
            ***************************************************/
            TInt state = CCnvCharacterSetConverter::KStateDefault;
            ……
            TInt ctu = converter->ConvertToUnicode(bufPtr, srcPtr, state);
            • 在網上找了一個實現SJIS到Unicode轉換的函數
                Code: #include "cp.h"

            #define is_zen(c)
                    ((0x81 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0x9f)
                    || (0xe0 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xfc))
            #define is_han(c)
                    ((0xa0 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xdf))

            s32_t skip_bytes(char c)
            {
              if(is_zen(c)) {
                return 2;
              } else if (is_han(c)) {
                return 1;
              }
              return 0;
            }

            s32_t jis2utf8(char **srcbuf, s32_t *srclen, char **outbuf, s32_t *outlen) {
              unsigned char *dst;
              unsigned char *src;
              unsigned short utf8code;
              int sjiscode;
              s32_t  len;
              unsigned char *to2;

              if (! (srcbuf && srclen && outbuf && outlen))
                return 0;

              src = (unsigned char *)*srcbuf;
              dst = to2 = (u8_t*)malloc(*outlen);
              while (*src && ((dst - to2) < (*outlen - 4))) {
                len = skip_bytes(*src);
                if ( len == 2 ) {
                  sjiscode = (int)(*src++ & 0xff);
                  sjiscode = (int)((sjiscode << 8)|(*src++ & 0xff));
                } else {
                  sjiscode = (int)(*src++ & 0xff);
                }

            utf8code = cp[sjiscode];// convert sjis code to utf8 (cp[] is conversion table array)

                if ( utf8code <= 0x7f ) {
                  *dst++ = (char)(utf8code & 0xff);
                }
               else if ( utf8code <= 0x7ff ){
                  *dst++ = (char)( 0xc0 | ((utf8code >> 6) & 0xff));
                  *dst++ = (char)( 0x80 | ( utf8code & 0x3f ));
                } else {
                  *dst++ = (char)( 0xe0 | ((utf8code >> 12) & 0x0f));
                  *dst++ = (char)( 0x80 | ((utf8code >> 6)  & 0x3f));
                  *dst++ = (char)( 0x80 | (utf8code & 0x3f));
                }

              }
              *dst++='';
              memcpy(*outbuf,to2,*outlen);
              free(to2);
              return strlen(*outbuf);
            }
            <!--[endif]-->


            symbian代碼編寫方面的提醒

            給自己在symbian代碼編寫方面的提醒:

            (1)在控件環境下進行文件操作,建議不要連接文件服務器,而應該直接利用 CEikonEnv::FsSession()這樣省去了連接文件服務器的開銷;

            (2)常量的判斷語句,常量應該放在==的左邊,這樣的好處是萬一程序員誤將==寫成=也可以檢查出來,因為常量不能被賦值的;

            (3)在程序中要善于運用條件編譯,使得同樣的一份代碼可以適應模擬器調試和真機的運行;


            (4)提醒自己寫函數時要注意考慮函數退出的問題,含可能退出代碼的函數,函數名應以L結尾。


            如何顯示s60 3rd中文菜單

            sword 222  問:

            1。如何在真機中顯示中文菜單啊?
            我在.rss文件中,寫上CHARACTER_SET UTF8
            將rls文件存儲成u8-dos格式。編譯GCCE過不了,說我文件有非法字符。
            2.在模擬器上如何顯示中文?
            我的sdk是s60 3.0 maint的。用的是vs.net2003+carbide 2.1

            beover1984 答:
            最好使用EditPlus把文件轉換成UTF-8編碼

            sword 222  問:
            我用UE轉成的就是UTF8啊。編譯不過。郁悶中。
            提示非法字符如下:
            ..\\data\\HotelAd.rls(1) : *** Unknown character '? (value 0xffffffef)
            ..\\data\\HotelAd.rls(1) : *** Unknown character '? (value 0xffffffbb)
            ..\\data\\HotelAd.rls(1) : *** Unknown character '? (value 0xffffffbf)
            還有其他可能不對嗎?
            60 2.0版本的我用UE轉換是對的。極度郁悶中。謝謝beover1984的回復。

            beover1984 答:

            用UE轉換的在3.0上可能也存在問題,我換成EditPlus就好了.

            sword 222  問:
            用edit plus轉換成utf8編譯通過。
            謝謝beover1984的回復。
            我用EPlus轉換,可以編譯了,但是,真機上運行不了,顯示系統錯誤。我自簽名,只用了ReadUserData能力。在亂碼的時候,我還是可以在真機上使用的。在模擬器上可以運行,但是依舊是亂碼。有沒有模擬器顯示中文的辦法?

            beover1984 答:

            執行abld reallyclean清理一下工程,然后重新生成一下.

            cdutly 答:
            如果你用UE的話,那么在"高級--配置"中將"當保存時寫入UTF-8 BOM頭到所有UTF-8文件"去掉,再去掉"自動檢測UTF-8". 設置好以后重新打開loc文件,你會發現在文件頭部會有一些亂碼字符,刪掉. 但如果你的loc是中文的話,中文將會變成很奇怪的字,不要擔心,再將"自 動檢測UTF-8"選種然后重新打開loc及可,試試看.


            如何選擇編輯器中的一段文本

            問題:
            比如一個用來顯示聊天記錄的編輯器,通過上下鍵需要能每次選中一段文本進行復制或其它操作,請問要如何實現呢?

            解決:

            CEikEdwin::SetSelectionL()
            void SetSelectionL(TInt aCursorPos, TInt aAnchorPos);
            Description
            Sets the text selection. Highlights the selected area and makes the new cursor position visible. Any previous selection is cancelled. This function also updates the scroll bar thumbs if present.


            用什么函數來獲得當前剩余堆內存

            問題:
            現在的問題就是要檢測系統的內存容量,當內存的剩余量達到一個臨界值的時候做出相應處理。可是找不到什么辦法。搜了站內的帖子,找到一個說是Available()可以實現的,但是我不能通過SDK help解決問題,最好有高人給個代碼片斷。

            解決:

            下面的代碼可以得到可用內存的數量。
            Code:
            TMemoryInfoV1Buf info;

            UserHal::MemoryInfo(info);

            TInt freeMemory = info().iFreeRamInBytes;


            如何對描述符讀寫數值型數據

            saintrui :
            TInt num = 123456;
            TBuf8<64> buf;
            buf.AppendNum(num);

            上面的代碼把TInt型的數字num存在描述符里后,buf中就有6個字節的數據了,每個字節分別是num中的一個數字,本來TInt是占4個字節的,我想讓描述符用4個字節存儲這個num,即按num的4個字節不變地儲存在buf的四個字節中,而不是6個字節,該如何做?

            上面的問題是我在寫socket通信中碰到的,還有個問題,假如pc服務器上發送過來的數據中有個數字123456,那么手機接收后存在TBuf8的描述符中,我想把這個數字從該描述符中讀到一個TInt變量里,該怎樣讀呢?

            saintrui :

            解決了,symbian中不提供這樣的函數,只好自己轉換。太依賴symbian了,c++的類型轉換都差點忘了。
            把a的二進制值寫到描述符中:
            TInt a =123456;
            TUint8* ch = (TUint8*)(&a);
            TBuf8<20> buf;
            buf.Append(ch,4);

            從描述符中讀取二進制TInt值:
            TUint8* p = &(buf[0]);
            TInt* b = (TInt*)(p);

            ch2000pro:

            從描述符讀出來可以這樣做:
            TLex iLex(iBuf);
            TInt iNum2;
            iLex.Val(iNum2);

            SymbianLn:
            樓住的最初的做法
            AppendNum就是把數字轉換成字符串存儲,所以就變成了6個字符。
            讓描述符直接指向存儲地址,就可以了。
            因為指針描述符既能指向字符,也能指向內存地址。
            可以使用TPtr8等等。


            在程序啟動后先彈出確定使用文字頁面,然后確定后繼續運行

            windnoway  問:
            ---------------------------------------------------------------
            我們的程序做好了
            但是我想在程序剛啟動的時候
            彈出一個文字頁面“歡迎使用授權軟件”,點擊同意后再繼續啟動程序
            怎么來做到程序還沒有啟動的時候顯示呢?
            用甚么函數寫在甚么地方 給個例子看看?
            -----------------------------------------------------------------

            beover1984   回答:
            ---------------------------------------------------------------------
            Code:

            HBufC *header = StringLoader::LoadLC( R_ABOUT_HEADER, iCoeEnv );
            HBufC *body = StringLoader::LoadLC( R_ABOUT_TEXT, iCoeEnv );

            CAknMessageQueryDialog *dlg = CAknMessageQueryDialog::NewL( *body );

            dlg->PrepareLC( R_AVKON_MESSAGE_QUERY_DIALOG );
            dlg->SetHeaderTextL( *header );

            dlg->RunLD();

            CleanupStack::PopAndDestroy( 2, header );

            上面的代碼可以彈出一個帶"確定"和"取消"的對話框,在UI的ConstructL()中判斷dlg->RunLD();的返回值就可以了.
            --------------------------------------------------------------------------------------------

            windnoway  問:
            ------------------------------------------------------------------------------------------
            兄弟您看一下是否正確
            我在**appui.cpp中加入


            #include<avkon.rsg> //for R_AVKON_MESSAGE_QUERY_DIALOG

            _LIT(R_ABOUT_HEADER,"message");
            _LIT(R_ABOUT_TEXT,"Copyright 2006 SE");

            HBufC *header = StringLoader::LoadLC( R_ABOUT_HEADER, iCoeEnv );
            HBufC *body = StringLoader::LoadLC( R_ABOUT_TEXT, iCoeEnv );

            CAknMessageQueryDialog *dlg = CAknMessageQueryDialog::NewL( *body );

            dlg->PrepareLC( R_AVKON_MESSAGE_QUERY_DIALOG );
            dlg->SetHeaderTextL( *header );

            dlg->RunLD();

            CleanupStack::PopAndDestroy( 2, header );

            void CFreeJoyAppUi::ConstructL()
            {
            BaseConstructL();
            if (dlg->RunLD())
            {
            //這里就寫明啟動的時候啟動的程序了
            }
            }


            //兄弟這樣寫沒有錯誤吧
            -------------------------------------------------------------------------------------------------------

            beover1984   回答:
            ----------------------------------------------------------------------------------------------------
            Code:

            .......

            if( !dlg->RunLD() )  //選擇"取消"退出程序
            {
                Exit();
            }


            VC中的CString類,在symbian中用什么解決

            問題:
            在VC中的CString類,可以用什么代替呢?請大家指教

            解決:

            Symbian平臺上提供了描述符用于處理字符串,其中HBufC是在heap上創建的,并且提供了ReAlloc()用于重新分配空間,但是不會像CString那樣在空間不夠時自動分配,需要自己做處理.


            關于在手機上安裝的問題

            問題:
            我用自己的SIS文件在手機上安裝之后,只出現圖標,但是老是打不開程序,請問是什么原因。
            我的SIS文件是在C:\Symbian\8.0a\S60_2nd_FP2_SC\epoc32\tools下創建的,為什么在其他目錄下用不了makesis的? 老是提示出錯。

            解決:

            把C:\Symbian\8.0a\S60_2nd_FP2_SC\epoc32\tools這個路徑加到環境變量path中,然后重啟一下就能在任意目錄使用makesis了,程序不能在真機上啟動很可能是打包時少打了一些資源文件引起的,建議檢查一下PKG文件,或者編譯打包一個例子試一下.


            不通過rss文件,程序手動構建CEikEdwin的問題

            問題:
            我不想用rss來構建CEikEdwin,因為CEikEdwin會經常改變輸入限制。所以考程序來設置比較好。
            但我一個只讓輸入英文的輸入框構建如下:
            iPSEdwin = new (ELeave) CEikEdwin;
            iPSEdwin->SetContainerWindowL(*this);
            iPSEdwin->ConstructL(EAknEditorFlagDefault,16,16,1);
            iPSEdwin->SetInputCapabilitiesL(TCoeInputCapabilities::EWesternAlphabetic );

            可運行后發現,只能輸入數字,右上腳的輸入法提示也沒有了。
            但另外一個只讓輸入數字的輸入框構建如下:
            iIDEdwin = new (ELeave) CEikEdwin;
            iIDEdwin->SetContainerWindowL(*this);
            iIDEdwin->ConstructL(EAknEditorFlagDefault,16,16,1);
            iIDEdwin->SetInputCapabilitiesL(TCoeInputCapabilities::EWesternNumericIntegerPositive);
            這就是正常的,只有數字輸入,右上角的輸入法提示也是正確的。

            我該如何設置CEikEdwin的輸入限制,不通過rss來構建

            解決:
            可以使用下面的代碼實現只能輸入字母的限制:

            Code:

            iPSEdwin = new (ELeave) CEikEdwin;
            iPSEdwin->SetContainerWindowL(*this);
            iPSEdwin->ConstructL(EAknEditorFlagDefault,16,16,1);
            iPSEdwin->SetAknEditorFlags(EAknEditorFlagLatinInputModesOnly);

            壓縮SVG文件

            我們可以在S60第二版,FP3的SDK中通過SVGTBINENCODE.EXE程序來對Scalable Vector Graphics(SVG)圖形文件進行二進制編碼和壓縮。
            SVGTBINENCODE的使用方法為:
            1、備份你的.svg文件——你將使用一個不可編輯的壓縮版本覆蓋原來的那個。
            2、對.svg文件在模擬器目錄中進行拷貝。如%EPOCROOT%\epoc32\winscw\c\system\temp\
            3、在命令行方式下,運行svgtbinencode壓縮程序。

            cd %EPOCROOT%\epoc32\release\winscw\udeb svgtbinencode -Dnogui -- c:\system\temp\sourceimage.svg

            這將在同樣目錄下生成一個二進制編碼版本的SVG圖形文件,其擴展名為.svgb

            將這個.svgb文件拷貝到原來.svg文件所在位置。
            刪除老的.SVG文件,因為你將要用壓縮版本去替代它。
            將這個.svgb文件重命名為.svg擴展名。
            運行MifConv.exe程序來生成一個multi-icon文件(.MIF),如果使用擴展過的makefile(icons.mk),那這些在編譯過程中會自動完成。
            注意,MifConv無法識別.svgb擴展名,如果你沒將其改名為.svg擴展名那它將無法識別接收這個二進制編碼的SVG文件。


            調用掛機鍵時行為發生變化

            標題: 調用掛機鍵時行為發生變化

            設備, 軟件 版本: S60 2nd Edition, Feature Pack 3  S60 3rd Edition

            說明:
            從S60第二版,FP3開始向后,按下掛機鍵將導致程序關閉。

            詳細描述:
            在早期S60設備上,當我們按下掛機鍵時(就是紅色的end鍵),當前程序將會切換到后臺而不會被關閉。但從S60第二版,FP3開始,當我們按下掛機鍵時將導致當前運行的程序關閉。

            情景重現:
            運行一個第三方程序,按下掛機鍵,程序將會被關閉,設備會切換到主畫面。

            解決方案:
            這是一個改進的特性,因此無法饒過掛機鍵操作所引發的行為。如果需要切換程序時,終端用戶應該使用菜單鍵去處理以避免掛機鍵導致程序的關閉。通過按1到2次菜單鍵來切換程序或返回待機畫面。


            在異常發生后音頻流處于不穩定狀態

            標題: 在異常發生后音頻流處于不穩定狀態

            設備, 軟件 版本: S60 2nd Edition, FP1, FP2, and FP3

            說明:
            如果程序因為異常而終止了音頻播放,那重新打開它的話將會引發錯誤。

            詳細描述:
            如果程序因為一個音頻流操作(使用CMdaAudioOutputStream或CMMFDevSound對象打開或運行)引起中斷并終止運行,那如果再次試圖使用如CMdaAudioOutputStream::OpenL去打開它時就會引發錯誤。
            如果想要成功再次打開那個音頻流,我們需要將設備重新啟動。

            情景重現:
            在音頻流播放出現異常中斷后,嘗試去調用CMdaAudioOutputStream::OpenL(),將無法得到返回,程序因此會掛起。


            當鍵盤鎖定被取消時可能會導致無意中將程序切換到后臺

            標題: 當鍵盤鎖定被取消時可能會導致無意中將程序切換到后臺

            設備, 軟件 版本: S60 2nd Edition, FP2, Nokia 6630, Nokia 6680
            S60 2nd Edition, FP3, N70, N90

            說明:
            當使用CAknAppUi::SetKeyBlockMode()取消鍵盤鎖定功能時,一些按鍵的組合可能會導致程序被切換到后臺。

            詳細描述:
            大部分S60游戲都會將鍵鎖定模式取消,以便多鍵同時按下時不影響游戲。這可以通過調用應用程序UI(CAknAppUi)來完成:
            SetKeyBlockMode(ENoKeyBlock)

            在S60第二版FP2或更新的版本中,如果為鍵盤鎖定模式取消狀態,那下列按鍵組合被同時按下會導致程序被切換到后臺:
            ‘7’ + ‘8’ + ‘0’ + ‘*’
            ‘9’ + ‘0’ + ‘#’

            解決方案:
            目前沒有好的解決方案,游戲設計中應該避免用戶偶然按下上述按鍵組合。


            導入工程到IDE時圖象資源無法生成

            標題: 導入工程到IDE時圖象資源可能無法生成

            設備, 軟件 版本: S60 2nd Edition, FP3,S60 3rd Edition

            說明:
            當我們將一個Symbian應用程序工程導入到IDE時(如MetroWorks CodeWarrior或Microsoft Visual Studio),試圖編譯它,但bitmap資源會無法生成。

            詳細描述:
            在S60第三版上,multi-image文件(.mbm或.mif)以及相關的image header files(.mbg)是通過mifconv圖象轉換工具生成的。是bld.inf中通過一個擴展的makefile來完成的,這個方法在S60 2nd Edition, Feature Pack 3.就開始被支持。
            這些擴展的makefile不會在IDE中隨著工程的編譯而被執行,因此圖片文件和頭文件將不會在編譯時自動產生,通常這就導致工程因為缺少.mbg文件而失敗。

            解決方案:
            我們可以通過abld命令去生成這些圖片資源,到\group目錄下,輸入
            bldmake bldefiles
            abld resource
            在此后,工程就能被順利導入IDE并被編譯,注意如果圖片資源(source bitmaps或SVG images)被修改了,那這個過程還要重復做一次。

            posted on 2007-06-18 17:33 Khan 閱讀(8499) 評論(2)  編輯 收藏 引用 所屬分類: GCC/G++跨平臺開發

            評論

            # re: Symbian 開發知識,瑣碎篇  回復  更多評論   

            哇,很細致啊,挖到寶了~~
            2007-08-07 12:04 | tiro

            # re: Symbian 開發知識,瑣碎篇  回復  更多評論   

            因為上一個項目要開發一個symbian的客戶端..所以我也是當時四處收集的
            2007-08-09 09:37 | Khan's Notebook
            久久人妻少妇嫩草AV蜜桃| 国产精品久久久久久久人人看| 久久青青草原亚洲av无码| 97久久精品无码一区二区| 精品无码久久久久国产动漫3d| 蜜臀久久99精品久久久久久| 99久久伊人精品综合观看| 色综合久久88色综合天天| 久久99国产精品久久久| 久久国产精品成人片免费| 波多野结衣中文字幕久久| 国内精品久久久久影院优| 精品久久久噜噜噜久久久 | 久久亚洲精品中文字幕三区| 91视频国产91久久久| 久久国产精品99久久久久久老狼 | 国产精品99久久不卡| 久久国产综合精品五月天| 日日狠狠久久偷偷色综合96蜜桃| 色99久久久久高潮综合影院 | 久久国产香蕉视频| 久久天天婷婷五月俺也去| 精品久久久久久久久免费影院 | 久久婷婷五月综合色高清| 一本大道加勒比久久综合| 久久久久亚洲爆乳少妇无| 国产精品久久婷婷六月丁香| 久久精品无码专区免费东京热| 91久久精品91久久性色| 日韩电影久久久被窝网| 久久亚洲AV成人无码国产| 狠狠人妻久久久久久综合蜜桃| 欧美激情精品久久久久久久| 久久青青草原亚洲av无码app| 伊人久久综合热线大杳蕉下载| 伊人久久大香线蕉AV一区二区| 久久精品a亚洲国产v高清不卡| 精品熟女少妇aⅴ免费久久| 亚洲国产一成人久久精品| 久久国产精品免费| 久久99国内精品自在现线|