franksunny的个人技术空间
获得人生中的成功需要的专注与坚持不懈多过天才与机会。 ——C.W. Wendte

关于CompleteWithAppPath函数

一直以来以为这个函数在S60平台是万能的,特别是之前用这个函数也是百试不爽,今天无意间写了个小Demo发现在我的N81手机上,假如传递“Data\\rpm.xml”返回值则为-28BadName;假如只传递文件名,则其返回值虽然为0,但是路径却变成了“c:sys\bin\ rpm.xml”。

一头的雾水啊,干Symbian也快有3个多年头了,从2版本到现在5版本,居然在这个函数上没搞灵清,实在是汗颜一下,结果又搜了些资料,先一个还是支持以前观点的,见后面的补全文件路径操作;后又在诺基亚论坛找到一个wiki——检测应用程序路径,发现这个函数在3rd上有变故,问题算是找到了,为此想着好久没有更新博客了,来更新下吧,呵呵。另外跟这里的朋友道个喜,我上个月喜得千金,终于升级做爸爸了。

关于这个函数,劝大家以后还是少用、不用为妙,当然明白就理能满足应用那就用吧,我写这个只是提醒大家其并没有真正搜索补全路径的功效。

补全文件路径操作

 

s60下能用CompleteWithAppPath(aFileName)这个函数来补全文件路径(aFileName可以不包含任何路径信息,但是文件名必须完成,而且如果包含路径信息,则必须正确),如果在UIQ下就不能用该工具函数,只能编写通用与Symbian平台的代码。以下是一个实际的案例问答。

提问:

第一,我想知道CompleteWithAppPath(aFileName);这个在s60下的具体含义是什么?

第二,在UIQ下能用什么代替它。

  下面是我写的一小段程序,请改下

  CBitmap* CBitmap::NewL(TFileName& aFileName, TInt aIndex )

  {

  CBitmap* bmp = new( ELeave )CBitmap;

  //CFbsBitmap tmpb;

  //load the correct bitmap..

  TInt ret;

  //CompleteWithAppPath(aFileName);

 

  // load the bitmap from the mbm file

  CFbsBitmap* tmpb = new (ELeave) CFbsBitmap();

  CleanupStack::PushL(tmpb);

  User::LeaveIfError(tmpb->Load(aFileName, aIndex));

  CleanupStack::Pop(); // bitmap

  bmp->iSize = tmpb->SizeInPixels ();

  bmp->iDrawRect = bmp->iSize;

  bmp->iData = new( ELeave ) TUint16[ bmp->iSize.iWidth * bmp->iSize.iHeight ];

  bmp->iMode = tmpb->DisplayMode();

 

  TUint16* dst = ( TUint16* )bmp->iData;

  for ( TInt y=0; y<bmp->iSize.iHeight; y++ )

  {

  TPtr8 buf( ( TUint8* )dst, bmp->iSize.iWidth*2 );

  tmpb->GetScanLine( buf, TPoint( 0, y ), bmp->iSize.iWidth, EColor4K );

  dst += bmp->iSize.iWidth;

  }

  tmpb->Reset();

  delete tmpb;

  tmpb = NULL;

  return bmp;

  }

解决:

1.CompleteWithAppPath(aFileName)根据当前程序的安装位置补上aFileName中缺少的路径组成部分:

Code:

Example1:

TFilename fname = _L("\testdir\pics.mbm"); // Use _LIT instead

CompletePathWithAppPath( fname );

Result: fname == "c:\testdir\pics.mbm" if application was installed to c:

 

Example2:

TFilename fname = _L("pics.mbm"); // Use _LIT instead

CompletePathWithAppPath( fname );

Result: fname == "c:\system\apps\myapp\pics.mbm" if application was installed to c:

 

2.UIQ上可以使用Symbian本身提供的TParse来做路径的解析:

Code:

TFileName filename;

TParse parse;

parse.Set( CEikonEnv::Static()->EikAppUi()->Application()->AppFullName(), NULL, NULL);

filename.Copy( parse.DriveAndPath() );

//filename.Copy( parse.Drive() );

 

检测应用程序路径

 

详细描述终端用户将会选择将程序安装到C盘(手机存储)或E盘(存储卡或内置硬盘)中。程序有时需要知道自己的安装位置,对程序安装位置的判断取决于S60平台的版本。

解决方案S60第二版

 

S60第二版中,使用aknutils.h中的CompleteWithAppPath(TDes& aFileName)方法。这个方法将返回给定一个描述符,内有所有需要的组成部分(盘符,路径,包括后缀的文件名)。任何缺少的部分(路径和盘符)将从应用程序路径(<drive>:\system\apps\<application_name> )中获取

#include <aknutils.h>    // Insert the full application path into the file name (fileName)  

TFileName fullPath(fileName);  

CompleteWithAppPath(fullPath);    // from aknutils.h

解决方案S60第三版

S60第三版向后,所有二进制程序(ExeDll文件)都存储在\Sys\bin中。要访问这个目录,程序需要AllFiles能力。资源和数据是无法存入这个目录的。程序有自己的目录\private\<SID>\各自安放,这里SID是一个安全标识,每个程序都是独一无二的。程序通过这个私有目录存放.ini.mbm.rsc和数据文件。其他没有AllFiles能力的程序将无法访问它们。

S60第三版上CompleteWithAppPath()方法将总是返回\sys\bin作为程序安装目录。如果要访问程序的私有目录中的数据,将要用另一个方法来获得路径:

TFileName appPath;

TBuf<2> appDrive;   // Returns private path of this application 

// in following format: \Private\<SID of the application>\ 

// (does not contain drive specification). 

iEikonEnv->FsSession().PrivatePath( appPath );

// Extract drive letter into appDrive 

appDrive.Copy(iEikonEnv->EikAppUi()->Application()->AppFullName().Left(2));

// Insert drive letter into path

appPath.Insert(0, appDrive);

posted @ 2010-07-09 19:42 frank.sunny 阅读(1922) | 评论 (2)编辑 收藏
     摘要: 本贴以后做为一些小问题的解决方案帖,遇到问题能够通过网络简单解决的问题会不定期的来这里更新下,给自己和有同样问题的人留个Mark  阅读全文
posted @ 2010-05-14 19:38 frank.sunny 阅读(3711) | 评论 (6)编辑 收藏

 

关于Symbian识别移动、联通和其它运营商网络的方法

 

自从Symbian OSEKA2提供了强大的CTelephony,这之后很多跟网络相关的参数都通过这个获取,像识别目前手机是注册的是移动还是联通等信息网络上传的比较多的也是通过该方法,特别是啸天兄在nokia论坛分享了代码之后,网络上大多采用其代码,当然也包括我这样的懒人在内,而且往往很多人都没有注意最关键的原理即“国际移动用户识别码(IMSIInternational Mobile Subscriber Identification Number)是区别移动用户的标志,储存在SIM卡中,可用于区别移动用户的有效信息。IMSIMCCMNCMSIN组成,其中MCC为移动国家号码,由3位数字组成,唯一地识别移动客户所属的国家,我国为460MNC为网络id,由2位数字组成,用于识别移动客户所归属的移动网络,中国移动为00,中国联通为01MSIN为移动客户识别码,采用等长11位数字构成”。具体详见http://wiki.forum.nokia.com/index.php/%E5%8C%BA%E5%88%86%E5%BD%93%E5%89%8D%E7%94%A8%E6%88%B7SIM%E5%8D%A1%E6%98%AF%E7%A7%BB%E5%8A%A8%E8%BF%98%E6%98%AF%E8%81%94%E9%80%9A

最近在使用该代码时,发现假如当手机处于离线状态下,则不论有无插SIM卡,使用啸天兄的方法,就识别不出来了,这个应该跟CTelephony的实现有关,本人试着通过其源码去了解了,但是貌似跟踪到底层没有完全公开,或者说个人看源码能力还太弱了些。由于采用啸天兄方法实现不了了,所以只能从上面的红头文件(即红体字)寻找解决方法,虽然CTelephony::GetCurrentNetworkInfo在离线模式下失效,但是CTelephony::GetSubscriberId仍然可用,为此我们就可以通过直接分析IMSI来实现对运营商网络的识别,至于MNC的信息,大家可以查询http://en.wikipedia.org/wiki/Mobile_network_code,在国内的情况如下截图:

既然知道了如上信息,我们就可以简单的对IMSI号进行分析了,小可对啸天兄的代码进行简单修改,当然该代码也是使用CTelephony,只能使用在EKA2平台上,EKA1可以采用RMobilePhone::GetSubscriberId的方法来获取IMSI,在这里也就不做展开了,具体代码如下:

头文件

/*

 * TelephonyAO.h

 *

 *  Created on: 2010-4-22

 *      Author: frank

 */

 

#ifndef TELEPHONYAO_H_

#define TELEPHONYAO_H_

 

#include <e32base.h>

#include <Etel3rdParty.h>

 

typedef enum

{

       ENetWorkUnKnow,

       ENetWorkCM,

       ENetWorkUN,

       ENetWorkTC,

       ENetWorkTT,

}TNetWorkType;

 

class CTelephonyAO : public CActive

{

public:

       static CTelephonyAO* NewL();

       TNetWorkType GetNetWorkId();

 

public:

       ~CTelephonyAO();

 

protected:

       void DoCancel();

       void RunL();

 

private:

       CTelephonyAO();

       void ConstructL();

 

       void GetNetWorkInfo();

 

private:

       CActiveSchedulerWait*                iActiveSchedulerWait;

       CTelephony*                        pTelephony_;

       CTelephony::TSubscriberIdV1             iSubscribId;

       CTelephony::TSubscriberIdV1Pckg     iSubscriberIdPckg;

};

 

#endif /* TELEPHONYAO_H_ */

 

实现文件

/*

 * TelephonyAO.cpp

 *

 *  Created on: 2010-4-22

 *      Author: frank

 */

 

#include "TelephonyAO.h"

 

CTelephonyAO::CTelephonyAO() : CActive(EPriorityStandard), iSubscriberIdPckg(iSubscribId)

{

      

}

 

CTelephonyAO::~CTelephonyAO()

{

       delete pTelephony_;

       pTelephony_ = NULL;

       delete iActiveSchedulerWait;

       iActiveSchedulerWait = NULL;

}

 

void CTelephonyAO::ConstructL()

{

       pTelephony_ = CTelephony::NewL();

       iActiveSchedulerWait = new (ELeave)CActiveSchedulerWait;

       CActiveScheduler::Add(this);

}

 

CTelephonyAO* CTelephonyAO::NewL()

{

       CTelephonyAO* pSelf = new(ELeave) CTelephonyAO;

       CleanupStack::PushL(pSelf);

       pSelf->ConstructL();

       CleanupStack::Pop();

       return pSelf;

}

 

void CTelephonyAO::RunL()

{

 

       if (iActiveSchedulerWait->IsStarted())

       {

              iActiveSchedulerWait->AsyncStop();

       }

}

 

void CTelephonyAO::DoCancel()

{

       pTelephony_->CancelAsync(CTelephony::EGetSubscriberIdCancel);

       if (iActiveSchedulerWait->IsStarted())

       {

              iActiveSchedulerWait->AsyncStop();

       }

}

 

void CTelephonyAO::GetNetWorkInfo()

{

       Cancel();

       pTelephony_->GetSubscriberId(iStatus, iSubscriberIdPckg);

       SetActive();

       iActiveSchedulerWait->Start();

}

 

TNetWorkType CTelephonyAO::GetNetWorkId()

{

       GetNetWorkInfo();

       TNetWorkType vNetWorkType = ENetWorkUnKnow;

       if(iSubscribId.iSubscriberId.Length() < 15)

       {

              vNetWorkType = ENetWorkUnKnow;

       }

       else

       {

              TBuf<6> vHeader;

              vHeader.Copy(iSubscribId.iSubscriberId.Left(5));

              TBuf<3> vPtrTemp;

              vPtrTemp.Copy(vHeader.Right(2));

              TInt vNetWorkId = 0;

              TLex vLex(vPtrTemp);

              vLex.Val(vNetWorkId);

              if((vNetWorkId == 1) ||(vNetWorkId == 6))

              {

                     vNetWorkType = ENetWorkUN;

              }

              else if((vNetWorkId == 0) ||(vNetWorkId == 2))

              {

                     vNetWorkType = ENetWorkCM;

              }

              else if((vNetWorkId == 3) ||(vNetWorkId == 5))

              {

                     vNetWorkType = ENetWorkTC;

              }

              else if(vNetWorkId == 20)

              {

                     vNetWorkType = ENetWorkTT;

              }

              else

              {

                     vNetWorkType = ENetWorkUnKnow;

              }

       }

       return vNetWorkType;

}

至于如何调用,就可以通过如下简单获取了,不用再自己去比较了。

       CTelephonyAO* pTelephony = CTelephonyAO::NewL();

       CleanupStack::PushL(pTelephony);

       TNetWorkType vNetWorkType = pTelephony->GetNetWorkId();

       CleanupStack::PopAndDestroy(pTelephony);

好了,暂时小结如下吧,感谢啸天兄前人植树。

posted @ 2010-04-22 19:13 frank.sunny 阅读(2154) | 评论 (3)编辑 收藏
     摘要:   关于RMsgQueue类的使用   RMsgQueue类是Symbian OS EKA2才提供的一个类,最近因为项目中要使用,为此对使用进行如下小结。 因为RMsgQueue类只是一个封装好的内核资源类,类似于RSocket和RTimer类,要想使用它进行异步操作就必须对其用AO类来封装,从而来实现监听消息,在有消息过来时得到通知并根据消息内容进行相对应的处理。 ...  阅读全文
posted @ 2010-04-09 21:40 frank.sunny 阅读(2001) | 评论 (1)编辑 收藏

 

关于3rd程序启动是参数的小结

 

Symbian 3rd手机上对于GUI程序的启动方式可以采用安装时自启动、开机自启动、编码启动和点击功能表图标的方式启动,有很多软件往往对采用何种方式启动需要做些前后台以及其它相关的处理方式,那么如何识别目前的GUI程序是何种启动呢?

方法就是通过重载GUI框架的C*AppUi:: ProcessCommandParametersL (CApaCommandLine &aCommandLine)函数(该函数还有其它重名函数,这里只对最简单的形式进行解析,抛砖引玉),对其入参进行识别来判断是何种形式的自启动。下面对我试验中的几种情况给出示例,方便以后查询

1、安装时启动,即pkg中标记为FR,RI标记的启动方式

C*AppUi::ProcessCommandParametersL中入参的

aCommandLine.OpaqueData().Length()0

 

2、通过Startup List开机自启动的方式

结果参数与1

 

3、通过点击程序图片启动

C*AppUi::ProcessCommandParametersL中入参的

aCommandLine.OpaqueData().Length()1,具体符号,搞不出来,反正是ASCII码值为1的那个符号

 

4、代码编程启动

试过三种方式:

第一种

       const TUid KAppUid={0x2CC2D30E};

       TThreadId app_threadid;

       RApaLsSession ls;

       User::LeaveIfError(ls.Connect());

       TInt err=ls.StartDocument(KNullDesC, KAppUid, app_threadid);

       ls.Close();

结果同3

 

第二种

   RApaLsSession apaLsSession;

   User::LeaveIfError(apaLsSession.Connect());

   CleanupClosePushL(apaLsSession);

   TApaAppInfo appInfo;

   TInt retVal = apaLsSession.GetAppInfo(appInfo, aAppUid);

   if (retVal == KErrNone)

   {

       CApaCommandLine* cmdLine = CApaCommandLine::NewLC();   

       cmdLine->SetCommandL(EApaCommandRun);

        cmdLine->SetExecutableNameL(appInfo.iFullName);

       _LIT8(KExitDesC,"start");

       cmdLine->SetOpaqueDataL(KExitDesC);

       User::LeaveIfError(apaLsSession.StartApp(*cmdLine));

       CleanupStack::PopAndDestroy(cmdLine);

   }

   else

   {

       // The application not found!     

   }

   CleanupStack::PopAndDestroy(&apaLsSession);

结果还是同3

 

第三种

   _LIT8(KExitDesC,"start");

   CApaCommandLine *cmd=CApaCommandLine::NewLC();

   cmd->SetCommandL(EApaCommandRun);

   cmd->SetExecutableNameL(_L("start.exe"));

   cmd->SetOpaqueDataL(KExitDesC);

   RApaLsSession als;

   User::LeaveIfError(als.Connect());

   CleanupClosePushL(als);

   User::LeaveIfError(als.StartApp(*cmd));

   CleanupStack::PopAndDestroy(2);

结果与上述就不一样了,aCommandLine.OpaqueData()内容就是"start",也即五位描述符长度的内容。对于第二种和第三种情况,我是百思不得其解,为什么传执行文件名就可以,而传完整路径就是不行。

好了,暂时小结到这里。好久没更新空间,今天就上来码几个字。

 

 

 

posted @ 2010-03-26 17:10 frank.sunny 阅读(1626) | 评论 (0)编辑 收藏

 

声音提示和震动提示的开发

 

声音提示可以采用两种方法:一种是利用系统自带的CoeSoundPlayer类来实现单音铃声的播放;另一种则是利用S60提供的多媒体框架CMda*类来实现音频播放。

关于声音提示的使用

CoeSoundPlayer类使用

该类声明于coesndpy.h头文件,库是cone.lib,最简单的使用莫过于如下格式的代码应用

TBaSystemSoundType a(KSystemSoundMessageUID);

CoeSoundPlayer::PlaySoundNow(a);

在以上代码的使用时,第一行声明一个系统tone的类型,该类型声明在bassnd.h文件中,同时在mmp中加上bafl.lib库文件。通常这种简单应用,在模拟器上能够听到声音(3rd MR版本的模拟器上都听不到声音),但是在真机上,基本听不到声音,一个原因据说是默认的缺省音量被置成了KSystemSoundDefaultVolume,其值最大可以到100(我亲测的结果是最小为0,没有声音,最大只能到10,超过10之后和传负值一样都会报MMFAudioClient 4的错误,程序也会Crash。所以关于这点最好还是有高人指点下)。另外bassnd.h中定义的类型还有KSystemSoundRingUID, KSystemSoundAlarmUID, KUidSystemSoundError, KUidSystemSoundEvent等,具体的效果,可以自己亲测下。

 

稍微复杂一点的应用代码如下:

TBaSystemSoundType soundType(KSystemSoundMessageUID);

//TBaSystemSoundInfo::TTone soundTone(aFrequency, aDuration);

TBaSystemSoundInfo::TTone soundTone(1500, 3*1000*1000);

TBaSystemSoundInfo soundInfo(soundType, soundTone);

BaSystemSound::SetSoundL(CCoeEnv::Static()->FsSession(), soundInfo);

CoeSoundPlayer::PlaySoundNow(soundType);

在这里,我对音调不是很懂,但是aFrequency的值,经人测试1003400是工作正常,效果很好(可能10003000最好), 36003800就变弱了,再往上到4000基本上已经听不到了。这种方法一般在真机上还是可以感受出来的,并非像第一种情况,只有模拟器上有效果。

 

只是长时间播放简单的音调估计很刺耳,那么我们就可以通过事先设计好的rng文件来进行播放单音铃声,具体代码如下:

_LIT(KRingToneFileName1, "\\Data\\Sounds\\simple\\alarm.rng");

const TInt KRingingTypeSilent = 4; // Silent

TInt tRingingType (0);

CRepository* tRepository = CRepository::NewLC(KCRUidProfileEngine);

User::LeaveIfError( tRepository ->Get(KProEngActiveRingingType, tRingingType ) );

if ( tRingingType != KRingingTypeSilent )

{

       TBaSystemSoundType soundType(KSystemSoundRingUID);

       TBaSystemSoundName soundName(KRingToneFileName1);

       CompleteWithAppPath(soundName);

       TBaSystemSoundInfo soundInfo(soundType, soundName, 10,
        KSystemSoundDefaultPriority);

       BaSystemSound::SetSoundL(CCoeEnv::Static()->FsSession(), soundInfo);

       CoeSoundPlayer::PlaySoundNow(soundType);

       //CoeSoundPlayer::PlaySound(soundType);

}

CleanupStack::PopAndDestroy(); // tRepository

使用以上代码需要注意的是alarm.rng文件必须要有,否则会没有声音传出,该文件在FP2版本的模拟器路径下没有,可以在S60_3rd_MR\Epoc32\release\winscw\udeb\z\system\sounds\simple下找到,并将其拷贝到相应的epoc32\release\winscw\udeb\z\system\sounds\simple下面即可。

注:另外,在这里虽然对情景模式是否静音进行了判断,其实不判断也是可以的,因为情景模式设为静音,仍然是可以播放出声音来的。这点很不同于震动。

 

多媒体框架的使用

S60MMF(多媒体框架)提供了对音频进行播放、录制和格式转换等功能函数,具体的功能类如下:

CMdaAudioPlayerUtility:音频播放;

CMdaAudioRecorderUtility:音频录制;

CMdaAudioConvertUtility:音频格式转换;

CMdaAudioToneUtility:音调播放

CMdaAudioInputUtility/ CMdaAudioOutputUtility:音频流操作

对于这一块内容的介绍在灵活使用EMCCsoft提供的AudioPlayer例子程序就会比较清楚,在这里就不多做展开了。唯一需要提醒的是,相应的回调接口虚函数一定要实现,否则不好控制。另设置音量的函数SetVolume也是只能在0~10之间,否则也会报MMFAudioClient 4错误。

 在用CMdaAudioPlayerUtility进行音乐文件比如*.wav格式播放时,假如一个文件还没有播放完,又开始播放一个新文件,也会引发MMFAudioClient 4的错误。

关于震动提示的使用

震动这个接口的发展历史很奇特,Symbian OS v8.x之前没有提供震动接口,之后开始使用CVibraControl类提供震动接口,而在Symbian OS v9.x之后在保留原有接口基础上又提供了新的CHWRMVibra类来提供震动接口。

网上的代码很多,常见形式如下:

// for S60 2nd FP2 and FP3

#include <vibractrl.h>  // CVibraControl, VibraCtrl.lib

 

void DoVibrateL( TUint16 aDuration )

{

  CVibraControl* control = VibraFactory::NewL();

  // get vibration setting in the user profile

  if ( CVibraControl::EVibraModeON == control->VibraSettings() ) 

  {

     control->StartVibraL( aDuration );

  }

 

  delete control;

  control = NULL;

}

 

// for S60 3rd

#include <hwrmvibra.h>  // CHWRMVibra, HWRMVibraClient.lib

 

void DoVibrateL( TInt aDuration )

{

  CHWRMVibra* vibra = CHWRMVibra::NewLC();

  // get vibration setting in the user profile

  if ( CHWRMVibra::EVibraModeON == vibra->VibraSettings() ) 

  {

     vibra->StartVibraL( aDuration );

  }

 

  CleanupStack::PopAndDestroy( vibra );

}

事实上如果原封不动拷贝如上代码是实现不了震动功能的,因为不管是CHWRMVibra还是CVibraControl对象在被新建并调用完StartVibraL函数之后,立即就被析构了,因为StartVibraL有类似异步函数的功能,并非阻塞在持续时间之内才会返回,所以对象还没起振就删除了。

震动功能的实现代码虽然简单,但是要想震起来还是有点麻烦的,为此我在使用时除了以上问题,还遇到其它几个问题:

当前情景模式里震动提示设置为关时,显然会因为

if ( CVibraControl::EVibraModeON == control->VibraSettings() )

if ( CHWRMVibra::EVibraModeON == vibra->VibraSettings() )

两个条件判断没通过而没有真实调用StartVibraL函数,那么我如果将判断去掉,始终让其调用StartVibraL函数应该也会震动的吧?

结果是震动函数返回-21KErrAccessDenied(拒绝接受),这和播放声音提示时的效果完全两样,所以说读情景配置模式里的参数在这里完全是必要的。

好,那就加判断,总算执行到了StartVibraL (TUint16 aDuration, TInt aIntensity)函数,假如在这里aDuration超过KHWRMVibraMaxDuration,或者aIntensity不在-100100之间(这里的强度值是马达的运转强度值,负值是马达反转,有些文章说该值在+-30范围内会报-2KErrGeneral错误,但是自己用E65亲测过,在+-30以内,没有报错,震动感不强烈而已,可能跟手机和具体硬件有关吧),那么震动效果又没有起来,此时的震动函数返回为-6,即KErrAgument(错误要求)。

我们解决了以上两个问题后,还有两种特殊情况,一种是当你的手机在充电时,如果调用正确的StartVibraL会返回-22错误,即KErrLocked(锁闭)。以上几种情况还好,虽然不震,但是你可以用TRAP机制捕获错误码,但是如果当你是通过数据线的手机PC模式装上软件后没有拔出数据线,就算使用TRAP返回时KErrNone,但是手机还是没有震动起来,你就会头大了,难道这个函数在当前手机上不管用吗?

事实是,当你拔掉数据线,居然震动来了。

唉,问题总算解决了,代码虽简单,但是实现却并不简单啊。

 

posted @ 2009-08-12 17:38 frank.sunny 阅读(1865) | 评论 (1)编辑 收藏

RConnection 的基类是RSubSessionBase。目前我所知道的就是两个功能:一、为套接字在SymbianOS上使用建立会话通道,二、使用已建立的会话通道

第一种应用是为了屏蔽接入点(IAP)选择对话框,具体代码如下

TInt currentProfileId;

 

// Check whether we are offline or online

iRepository->Get(KProEngActiveProfile, currentProfileId);

if (currentProfileId == 5)

{

    // Close and uninitialize

    iConnection.Close();

    iSocketServ.Close();

}

//这里的iSelectedIap是已经选择好的接入点iap

if(iSelectedIap == 0)

       return;

 

User::LeaveIfError(iSocketServ.Connect());

User::LeaveIfError(iConnection.Open(iSocketServ));

TCommDbConnPref pref;

// IAP ID for connection to be used

pref.SetIapId(iSelectedIap);

//CEikonEnv::Static()->InfoWinL(_L("note:"), _L("Iap OK"));

pref.SetDialogPreference( ECommDbDialogPrefDoNotPrompt);

pref.SetDirection(ECommDbConnectionDirectionOutgoing );

//CEikonEnv::Static()->InfoWinL(_L("note:"), _L("Pref Ok"));

iConnection.Start(pref,iStatus);

SetActive();

由以上代码可知,建立会话通道是一个异步函数,所以最好在活动对象中使用,因为目前该代码只是在G网上亲测过,在3GWLAN是否也通用暂时还不敢肯定。

 

第二种应用往往用在复用通道上,比方说同一个程序有多个线程(或者使用C/S架构的程序,不同的进程乃至两个完全无关的)需要使用网络,没必要每个线程都去建立一个会话通道,所以就采用复用的方式;还有一种复用的要求就是用RConnection来获得当前链接的流量等数据,以下就给出这么一种应用的代码:

TUint      vUl = 0;

TUint      vDl = 0;

TPckg< TUint >  UplinkVolume(vUl);

TPckg< TUint >  DownlinkVolume(vDl);

TRequestStatus  aStatus;

RConnection          vConnection;

if(vConnection.Open(iSocketServ) == KErrNone)

{

       TPckgBuf<TConnectionInfo> connInfo;

       TUint count;

       if (vConnection.EnumerateConnections(count) == KErrNone)

       {

              for (TUint i=1; i<=count; i++)

              {

                     // Note: GetConnectionInfo expects 1-based index.

                     if (vConnection.GetConnectionInfo(i, connInfo) == KErrNone)

                     {    

                            TInt vErr = vConnection.Attach(connInfo, RConnection::EAttachTypeNormal);

 

                     }

              }

       }

}

 

vConnection.DataTransferredRequest(UplinkVolume, DownlinkVolume, aStatus);

User::WaitForRequest(aStatus);

 

TFileName vBuffergg,vBuffergg2;

vBuffergg.AppendNum(vDl);

iObserver.RComValuesL(EGetConnectId,_L("KDownlinkData"),vBuffergg,0); 

vBuffergg2.AppendNum(vUl);

iObserver.RComValuesL(EGetConnectId,_L("KUplinkData"),vBuffergg2,0);    

 

vConnection.Close();

 

代码往往都是很简单的,但是往往会有点问题,在这个获取流量的方法上,起初我们使用单线程可以用,但是后来居然不能用了,百思不得其解,后来经过分析存在后面的一些问题,先得出一个结论,RConnection可以跨线程乃至进程使用

 

注意点

TConnectionInfoV2获取的coninfo不能进行attach,否则会爆-34错误,如果同一个RConnection对象已经连接上了,再继续使用attach,则会报-14错误,但是RConnection对象仍然是可以正常使用的,可以通过它创建Socket和监控流量等。

在使用TPckg的时候尤其要注意类型的配对问题,因为上面代码中用到的是局部变量,很显然如下代码不会有误

TUint      vUl = 0;

TUint      vDl = 0;

TPckg< TUint >  UplinkVolume(vUl);

TPckg< TUint >  DownlinkVolume(vDl);

但是一次当我将vUlvDl声明成类的成员变量时,弄成如下方式

TUint32   iUl = 0;

TUint32   iDl = 0;

再将其用TPckg进行封装之后,传入DataTransferredRequest函数,得到的流量始终是0,真的让我百思不得其解,这种失误教训真的太深刻。


    另外,近来发现RConnection类的实例对象是不能够进行跨线程调用,同样的CRepository的实例对象跨线程调用也是不允许的。

posted @ 2009-08-12 17:37 frank.sunny 阅读(2463) | 评论 (0)编辑 收藏

 先声明一下,下面的这段代码是调用系统的浏览器实现网页浏览的功能,很显然这是一种比较简单的方法,但是它的可控制性就不行了,例如左软键的内容你是肯定该不了的。如何写一个自己的浏览器,而不调用系统的,等以后做出来再说吧。

调用系统的浏览器来实现网页浏览可以根据系统浏览器的状态而决定调用的方法,例如当系统浏览器正在使用的时候可以用TApaTask::SendMessage ()方法;当系统浏览器没有被使用的时候可以用RapaLsSession::StartDocument() 方法。

下面是实现代码:

TBool CinternetAppUi::ConnectL(const TDesC& addr)

{

 const TInt KBrowserUid = 0x10008D39;

 TUid id( TUid::Uid( KBrowserUid ) );

 TApaTaskList taskList( CEikonEnv::Static()->WsSession() );

 TApaTask task = taskList.FindApp( id );

 // the system browser is in use

 if ( task.Exists() )

    {

    HBufC8* param8 = HBufC8::NewLC( addr.Length() );

    param8->Des().Append( addr );

    task.SendMessage( TUid::Uid( 0 ), *param8 ); // Uid is not used

    CleanupStack::PopAndDestroy();

    }

 // the system browser is not in use

else

 {

  RApaLsSession   appArcSession;

  User::LeaveIfError(appArcSession.Connect());    // connect to AppArc server

  TThreadId id;

  appArcSession.StartDocument( addr, TUid::Uid( KBrowserUid ), id );

  appArcSession.Close();

 }

 return ETrue;

}

//其中入口参数addr的格式是4”+” <Space>“+”<Url>”,例如“4  www.google.com

 

其中采用以上方法不仅仅可以用于开启网页,还可以用于启动安装sis/sisx,具体示例代码如下:

RApaLsSession installSession;

TThreadId threadId;

TUid uid;

uid.iUid = 0x101F875A;

installSession.Connect();

installSession.StartDocument(aFileName, uid, threadId);

installSession.Close();

该代码自己没有亲测过,但是从理论上说应该可行,而且有大牛说uid都不用传进去。

另外播放音乐文件,网上也说可以通过该方法来实现,Uid分别如下:

0x102072c3 (from S60 3rd Edition onwards)

0x6c5b9d2 (S60 2nd Edition)

RapaLsSession::StartDocument()功能还是很强大的,在这里只做摘录,以后有机会再亲测,不过用其打开网页的确可行。

 

 

 

 

 

posted @ 2009-08-12 17:36 frank.sunny 阅读(1033) | 评论 (0)编辑 收藏

Nucleus PLUS介绍

 

今天在公司看到VIACBP系列CDMA手机方案用到了Nucleus +OS,说实话还是第一次听说,所以查了些资料,引用别人的总结做个百科小文,原文链接如下:

http://www.upsdn.net/html/2005-01/250.html

 

一、Nucleus PLUS嵌入式操作系统的简单介绍

Nucleus PLUS嵌入式操作系统是目前最受欢迎的操作系统之一,是美国源代码操作系统商ATI公司为实时嵌入式应用而设计的一个抢先式多任务操作系统内核,其95%的代码是用ANSIC写成的,因此非常便于移植并能够支持大多数类型的处理器

从实现角度来看,Nucleus PLUS 是一组C函数库,应用程序代码与核心函数库连接在一起,生成一个目标代码,下载到目标板的RAM中或直接烧录到目标板的ROM中执行。

Nucleus Plus内核在典型的CISC体系结构上占据大约20k空间,而在典型的RISC体系结构上占据空间为40k左右,其内核数据结构占据1.5k字节的空间。Nucleus Plus以其实时响应、抢先、多任务以及源代码开放特性获得在通讯、国防、工业控制、航空/航天、铁路、网络、POS、自动化控制、智能家电等领域的广泛应用。

Nucleus PLUS 采用了软件组件的方法。每个组件具有单一而明确的目的,通常由几个C及汇编语言模块构成,提供清晰的外部接口,对组件的引用就是通过这些接口完成的。除了少数一些特殊情况外,不允许从外部对组件内的全局进行访问。由于采用了软件组件的方法,Nucleus PLUS各个组件非常易于替换和复用Nucleus PLUS的组件包括任务控制、内存管理、任务间通信、任务的同步与互斥、中断管理、定时器及I/O驱动等。

 

二、Nucleus具有的优点:

1、提供源代码

Nucleus PLUS提供注释严格的C源级代码给每一个用户。这样,用户能够深入地了解底层内核的运作方式,并可根据自己的特殊要求删减或改动系统软件,这对软件的规范化管理及系统软件的测试都有极大的帮助。另外,由于提供了RTOS的源级代码,用户不但可以进行 RTOS 的学习和研究,而且产品在量产时也不必支付 License,可以省去大量的费用。对于军方来说,由于提供了源代码,用户完全可以控制内核而不必担心操作系统中可能会存在异常任务导致系统崩溃。

2、性价比高

Nucleus PLUS由于采用了先进的微内核 ( Micro-kernel ) 技术,因而在优先级安排,任务调度,任务切换等各个方面都有相当大的优势。另外,对C++语言的全面支持又使得Nucleus PLUS Kernel 成为名副其实的面向对象的实时操作系统内核。然而,其价格却比较合理。所以,容易被广大的研发单位接受。

3、易学易用

Nucleus PLUS 能够结合 ParadigmSDS以及 ATI自己的多任务调试器组成功能强大的集成开发环境,配合相应的编译器和动态联结库以及各类底层驱动软件,用户可以轻松地进行 RTOS 的开发和调试。另外,由于这些集成开发环境 ( IDE ) 为所有的开发工程师所熟悉,因而,容易学习和使用。

4、功能模块丰富

Nucleus PLUS除提供功能强大的内核操作系统外,还提供种类丰富的功能模块。例如用于通讯系统的局域和广域网络模块,支持图形应用的实时化Windows模块,支持Internet网的WEB产品模块,工控机实时BIOS模块,图形化用户接口以及应用软件性能分析模块等。用户可以根据自己的应用来选择不同的应用模块。

 

Nucleus PLUSRTOS内核可支持如下类型的CPUx86,68xxx,68HCxx,NEC V25, ColdFire, 29K,i960, MIPS, SPARClite, TI DSP, ARM6/7, StrongARM, H8/300H, SH1/2/3, PowerPC, V8xx, Tricore, Mcore, Panasonic MN10200, Tricore, Mcore等。可以说NUCLEUS+支持CPU类型最丰富的实时多任务操作系统。针对各种嵌入式应用,Nucleus PLUS 提供相应的网络协议(如TCP/IPSNMP等),以满足用户对通讯系统的开发要求。另外,可重入的文件系统、可重入的C函数库以及图形化界面等也给开发者提供了方便。针对不同的CPU类型,Nucleus 还提供编译器、动态连接库、多任务调试器等相应的工具来配置用户的开发环境。

值得提出的是ATI公司最近还发表了基于Microsoft Developers Studio的嵌入式集成开发环境-NUCLEUS EDE。从而率先将嵌入式开发工具与Microsoft的强大开发环境结合起来,提供给工程师们强大的开发手段。

 

三、 源代码带来的优势众所周知,Nucleus实时多任务操作系统提供给用户源代码。

这除商务上给用户带来巨大益处(免交Royalty)外,还在技术方面给用户极大的方便,即无需编写和调试BSP,从而达到易学易用的目的,加速产品上市。对RTOS有一定知识的工程师一定清楚,使用RTOS最大的障碍在于编写和调试BSP。大家知道,在调试目标系统的软件之前,必须将目标与主机连接起来并建立通讯。为此,我们可以编写一段监控程序(Monitor)。然而,如果要调试基于RTOS内核的程序,主机上的调试器(Debugger)除要与目标建立通讯外,更重要的是必须识别RTOS的任务,这样才能进行任务级调试(Task-aware Debugger)。因此,只有Monitor是不够的。如果我们选用的RTOS不提供源代码,那么,主机上的调试器(Debugger)就只有通过用户编写的BSP来了解Kernel在现有硬件平台上对各个任务进行调度的情况。显然,编写BSP必须对CPU目标系统的硬件以及应用软件等有全面而深入的了解。一般说来,对于一个有一定硬件开发经验的工程师来说,编写一个新的BSP要花的平均时间为两个月左右。这对于一个新手来说可是比较困难的。对于编写BSP的工程师来说,另外一个更大的挑战就是如何调试BSP,即如何验证所编写的BSP是否正确。通常刚刚焊接安装好的PCB板中,硬件或软件的故障(Bugs)是比较多的。甚至更常见的是CPU部分都没有运作正常。有时时序错误和总线错误都还存在。在您把写好的BSP烧入EPROM(或FLASH)中试图将目标与主机建立联系时,您几乎100%地会发现根本无法通讯,眼前一团漆黑,不知是软件有错误还是硬件不运转。BSP在正常运行吗?不得而知。在焦急和摸索中您可能发现几个月已悄然而过。对于早期的实时操作系统来说,BSP是必由之路。然而,新一代的RTOS-Nucleus PLUS则避免了BSP带来的痛苦过程。因为Nucleus的调试是基于全新的动态连接库(DLL)。用户只须通过监控程序(Monitor)或者BDM调试口(或者JTAG调试口)建立目标系统(Target)与主机(Host)之间的通讯,并给主机上的Debugger初始化特性中加入Nucleus的动态连接库(DLLs),这时,调试器就能够自动地去识别运行在目标系统中的Nucleus内核和各个应用任务,从而完成任务级调试。上述的动态连接库(DLLs)是由RTOS厂商和Debugger厂商合作完成的,用户无须自行编写。因此,Nucleus的用户只需要将精力放在基于Nucleus的编程工作中。对于一个新手,往往经过一天到两天的学习和培训,就可以投入到应用程序编制工作中去,无需花大量的时间去研究CPU,特定的硬件等。

另外,由于有了源程序,用户在调试程序时可以清楚地通过STEP INTO命令,追踪到RTOS的内层中去,观看和学习Kernel对任务的管理和调度机制。对于有志研究RTOS深层技术的工程师来说极为方便。对于Motorola 68KPowerPC,用户可以利用GreenHill公司的Mutil调试器或TRACE-ICD来完成Nucleus PLUS的调试;对于ARMStrongARM,用户可以利用ARM公司的SDT251调试器或TRACE-ICD来完成Nucleus PLUS的调试;对于Intel x86实模式,用户可以利用Paradigm公司的调试器及其DLL来进行任务级调试;对于Intel x86保护模式则可以利用SSI公司Softprobe调试器和SSI DLL来调试;对于i960SH3/4ARM6/7MIPS等芯片,则可以使用ATI公司自己的UDB调试器来进行任务的调试。结论:动态连接库(DLL)是在RTOS工具中新出现的应用趋势,通过这种方式,用户可以免去BSP带来的麻烦,灵活方便地进行开发和调试,大大加速开发进度。Nucleus实时操作系统提供源代码,支持丰富的CPU种类,配合各类DLL动态连接库,为使用和研究RTOS技术的工程带来极大的利益。

 进行ARM系列的开发需要大量的设备投入,另外如果做比较大型的系统,还必须要操作系统,购买一个好的操作系统也是需要几十万元ARM应用层研发可以建立在CC++及其他的大多数开发语言上,这对于软件公司来说是很方便的,只要准备好硬件及操作系统,其他的工作就可以分模块给N个人来进行。而对于底层的东西,ARM公司也可以有比较大力度的支持,因此如果需要用ARM系列开发高端产品,可行性是比较高的,开发周期也不会很长。当然,对于arm芯片,还是有一定的限制,比如没有除法指令,这样在编程时就要尽量避免用除法,否则会带来程序代码的增加和执行速度的降低。一般说,除法还是可以通过移位和乘法来代替。


另外,好久没上来码字了,上周结束失业,估计金融危机对我的影响暂时结束一下了吧。

posted @ 2008-11-27 22:37 frank.sunny 阅读(1553) | 评论 (0)编辑 收藏

这次失业之后,突然发现现在工作好像真的不是很好找,没办法,主动权不在自己手里,静下心来想想就当通过笔试来给自己查漏补缺吧,昨天笔试遇到一个虚拟继承的概念,这不虽说2分的题,但是这个玩意有大内容,我学习了下,也就先整个入门出来吧:

 

为什么要引入虚拟继承?

虚拟继承在一般的应用中很少用到,所以也往往被忽视,这也主要是因为在C++中,多重继承是不推荐的,也并不常用,而一旦离开了多重继承,虚拟继承就完全失去了存在的必要(因为这样只会降低效率和占用更多的空间,关于这一点,我自己还没有太多深刻的理解,有兴趣的可以看网络上白杨的作品RTTI、虚函数和虚类的开销分析及使用指导,说实话我目前还没看得很明白,高人可以指点下我)。

以下面的一个例子为例:

#include <iostream.h>

#include <memory.h>

class CA

{

    int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性

public:

    void f() {cout << "CA::f" << endl;}

};

 

class CB : public CA

{

};

 

class CC : public CA

{

};

 

class CD : public CB, public CC

{

};

 

void main()

{

    CD d;

    d.f();

}

当编译上述代码时,我们会收到如下的错误提示:

error C2385: 'CD::f' is ambiguous

即编译器无法确定你在d.f()中要调用的函数f到底是哪一个。这里可能会让人觉得有些奇怪,命名只定义了一个CA::f,既然大家都派生自CA,那自然就是调用的CA::f,为什么还无法确定呢?

这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CBCC时还需要在编译器的语法树中生成CB::fCC::f等标识,那么,在编译CD的时候,由于CBCC都有一个函数f,此时,编译器将试图生成这两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译

 

要解决这个问题,有两个方法:

1、重载函数f():此时由于我们明确定义了CD::f,编译器检查到CD::f()调用时就无需再像上面一样去逐级生成CD::f标识了;

此时CD的元素结构如下:

|CB(CA)|

|CC(CA)|

故此时的sizeof(CD) = 8;CBCC各有一个元素k

2、使用虚拟继承:虚拟继承又称作共享继承,这种共享其实也是编译期间实现的,当使用虚拟继承时,上面的程序将变成下面的形式:

#include <iostream.h>

#include <memory.h>

class CA

{

    int k;

public:

    void f() {cout << "CA::f" << endl;}

};

 

class CB : virtual public CA  //也有一种写法是class CB : public virtual CA

{                       //实际上这两种方法都可以

};

 

class CC : virtual public CA

{

};

 

class CD : public CB, public CC

{

};

 

void main()

{

    CD d;

    d.f();

}

此时,当编译器确定d.f()调用的具体含义时,将生成如下的CD结构:

|CB|

|CC|

|CA|

同时,在CBCC中都分别包含了一个指向CA的虚基类指针列表vbptrvirtual base table pointer),其中记录的是从CBCC的元素到CA的元素之间的偏移量。此时,不会生成各子类的函数f标识,除非子类重载了该函数,从而达到“共享”的目的(这里的具体内存布局,可以参看钻石型继承内存布局,在白杨的那篇文章中也有)。

也正因此,此时的sizeof(CD) = 12(两个vbptr + sizoef(int);

 

另注:

如果CBCC中各定义一个int型变量,则sizeof(CD)就变成20(两个vbptr + 3sizoef(int)

如果CA中添加一个virtual void f1(){}sizeof(CD) = 16(两个vbptr + sizoef(int)+vptr;

再添加virtual void f2(){}sizeof(CD) = 16不变。原因如下所示:带有虚函数的类,其内存布局上包含一个指向虚函数列表的指针(vptr),这跟有几个虚函数无关。

以上内容涉及到类对象内存布局问题,本人还难以做过多展开,先贴这么多,本篇文章只是考虑对于虚拟继承进行入门,至于效率、应用等未作展开。本文在网上文章基础上修改了下而得此篇,原文载于http://blog.csdn.net/billdavid/archive/2004/06/23/24317.aspx

另外关于虚继承和虚基类的讨论,博客园有篇文章《虚继承与虚基类的本质》,总结得更为详细一点。

 

 

posted @ 2008-10-16 16:55 frank.sunny 阅读(13686) | 评论 (8)编辑 收藏
仅列出标题
共7页: 1 2 3 4 5 6 7 

常用链接

留言簿(13)

随笔分类

个人其它博客

基础知识链接

最新评论

阅读排行榜

评论排行榜