Mobil alkalmazások fejlesztése
ELTE PSZT – 2010. március
Előkészületek 1. Perl 5.6.x (Set the pathvariable!) – 5.8 /5.10 nem jó! http://downloads.activestate.com/ActivePerl/Windows/5.6/ActivePerl5.6.1.638-MSWin32-x86.msi 2. SDK(s)(S60 3rd Ed. MR + újabb) http://www.forum.nokia.com/Resources_and_Information/Tools/Platforms/S 60_Platform_SDKs/ 3. Carbide.c++ 2.0 Developer Edition (vagy újabb) http://www.forum.nokia.com/main/resources/tools_and_sdks/carbide/index. html Eszközök telepítése a felsorolás sorrendjében történjen Az eszközöket ugyanarra a meghajtóra kell telepíteni, ahol az SDK van. (javasolt: C:\. Ne használjon hálózati meghajtót! Fájl nevekben ne használjon helyközt.)
Segédanyagok www.symbian.com/books www.symbian.com/developer www.symbian.developer/public/index.html
http://forum.nokia.com/main.html SDK online dokumentáció: C:\S60\devices\S60_3rd_FP2_SDK_v1.1\docs\eclipse.exe
Online dokumentáció
c:\S60\devices\S60_3rd_FP2_SDK_v1.1\docs\eclipse.exe
Symbian operációs rendszer SIBO – Psion Computers (1980) EPOC - irodai kisszámítógép (1980-1998) Symbian OS – (alapítva 1998 : Nokia, Motorola, Psion,
Ericsson, 2002: Sony-Ericssson, Siemens) Mérföldkő: 2000-ben megjelent 6.0-ás verzió
Symbian – Symbian OS – S60 – C++
Symbian OS felépítése
Felhasználói interfész szinkronizáció Alkalmazás szintű szolgáltatások Operációs rendszer szolgáltatásai
UI alkalmazás keretrendszer
UI eszközkészlet
MIDP CLDC
PIM
Általános szolgáltatások
Üzenetkezelés
Böngészés
Kommunikációs szolgáltatások
Adatszinkron
Grafikai szolgáltatások
Alapszolgáltatások
Alacsonyszintű szolgáltatások
Fájlszerver
Kernel és hardware integráció
Kernel szolgáltatások
Eszközmeghajtók
JVM Java
PC-s kapcsolat szolgáltatásai
Kliens-szerver keretrendszer
fájlszerver ablakkezelő szerver kommunikácó kezelés
A CServer osztályból származnak.
adatbázis-kezelés határidőnapló
2009.09.19.
Mobil alkalmazások fejlesztése, Symbian OS/C++
7
Fájl kezelés Hívó kliens
File szerver kliens oldali része
mytest.exe szolgáltatás kérés (API hívások) pl.: RFs::Connect()
efsrsv.dll
Process boundary
Kliens-szerver kommunikáció (Kernel által felügyelt) File server
efile.exe
//File I/O in Symbian OS RFs fSession; User::LeaveIfError(fSession.Connect()); RFile file; ret = file.Open(fSession, _L(“file.txt”), EFileWrite); if (ret) { ret = file.Create(fSession, _L(“file.txt”), EFileWrite); } if (!ret) { file.Write(_L8(“Write some data”)); } file.Close(); fSession.Close();
Futtaható program „modularizálása” Forráskód
.exe
Forráskód
„.exe”
Kép
Futtató környezet
Kép
.exe
Erőforrás
„szöveg”
Erőforrás Fájl szerver
Fájl szerver
„szöveg” Ablak szerver Kód ...
indexek Ablak szerver Kód ...
A fordítás és szerkesztés folyamata .bmp
<.h>
„.h”
.rsg
.hrh
.rh
Bmp konverter
.rls
.cpp
.rss
.bmp
C++ fordító
Erőforrás fordító
Bmp konverter
Aif fordító
.mbm
.aif
.obj
.lib
.rsc
.pkg
.mbm
.rss
linker
.exe; .dll
Sis készítő
.sis
Biztonságos alkalmazás fejlesztésének támogatása Saját elnevezési konvenciók:
osztályok (T,C,M,R), függvények (L,LC) Kódolási minták a memóriaszivárgás kiszűrésére: kétfázisú konstrukció, CleanUpStack Saját kivételkezelés: TRAP és User::Leave String helyett deszkriptorok Többszálúság kezelése: ActiveObject ...
Osztálytípusok T: egyszerű típus; nincs destruktora; csak T-ből, vagy M-ből
öröklődhet; értékként átadható; stack-en tárolható C: csak a heapen foglalható; van destruktora; pontosan egy C és tetszőleges M osztályból származhat; nem adható át értékként M: interfész; csak virtuális metódusok; nincsenek tagváltozók; nem példányosítható R: erőforrás, melyet meg kell nyitni és be kell zárni
Az osztályelnevezéseknek fontos „üzenete” van, ezért mindig jól át kell gondolni, hol és hogyan használjuk az osztály példányait, és ennek megfelelően kel elnevezni őket. A névkonvenció segíti a fejlesztőt abban, hogyan használhatja majd ezeket az osztályokat anélkül, hogy mélyebben beleásná magát az adott osztály implementációjába. C osztályként definiálhatunk pl. olyan osztályt is , amely túl nagy, és nem ajánlatos stack-ben tárolni.
T osztályok - példa
TInt n; enum TColors { KRed, KBlue }; class TMyNumber { public: void SetNumber (TInt aNumber); TInt Number() const; private: TInt iNumber; };
R osztályok - példa TInt doExampleL() { RFs fSession; User::LeaveIfError(fSession.Connect()); RFile file; TInt ret = file.Open(fSession,_L("C:\\resource\\apps\\second.txt"),EFileWrite); if(ret) ret = file.Create(fSession,_L("C:\\resource\\apps\\second.txt"),EFileWrite); if(!ret) file.Write(_L8("Write some new data")); file.Close(); fSession.Close(); return ret; }
Kivételkezelés try { utasítások; // A kivétel figyelése alatt álló "veszélyes" utasítások
C++-os kivételkezelés
} catch (kivétel deklaráció) { //A kivétel kezelője utasítások; }; int foo1(); //Minden kivétel továbbítódik int foo2() throw (char*) //csak a char* típusú kivételek továbbítódnak int foo3() throw() // egyetlen kivétel sem továbbítódik
Symbianos kivételkezelés void CMyClass::Foo() { TRAPD (error, HandleKeyL())) if(error != KErrNone) { //Ha a HandleKeyL() leavel, akkor ideadódik a vezérlés //és kezelhetjük a hibát } }
Kivételkezelés - leavelés A Symbianban a C++-os kivételkezelés helyett (mellett) a „kivételes
szituációk” kezelésére a „leavelést” használják. Leavelésnél a „kivételes szituáció” helyéről átkerül a vezérlés oda, ahol a kivételes helyzetet kezelni tudjuk. (TRAP) Mikor leave-elhet egy függvény? Ha közvetlenül meghívja a User::Leave() függvényt Heapen foglal helyet new (ELeave) metódussal Más leave-elő függvényt hív
Majd látni fogjuk, hogy azokban a kódrészekben, ahol leavelő függvényhívások (is) vannak fokozottabb gondossággal kell eljárni, ezért a programozónak feltétlenül tudnia kell egy függvényről, hogy esetleg leavelhet. Symbianban azt, hogy egy függvény leavelhet a függvény nevében, a neve végén megadott L betűvel jelezzük.
Kivételkezelés C++-os kivételkezelés A kivétel keletkezésekor egy objektumot dobunk Egy try blokkban vagy az abból hívott függvényekben keletkező kivételt a catch blokkban elkaphatjuk és lekezelhetjük. A kivétel keletkezésekor a vezérlés visszatér a hívó blokkba, majd az azt hívóba fel és fel, amíg kivételt le nem kezeljük. Közben a verem memória (stack) visszafejtésre kerül, az abból kikerülő objektumok destruktorai meghívódnak.
2009.09.19.
Symbianos kivételkezelés A kivétel keletkezésekor egy egész szám (hiba kód) generálódik. Egy TRAP vagy TRAPD makróval meghívott függvényben vagy abból
hívott függvényekben keletkező kivételt a makróban lekezelhetjük. A kivétel keletkezésekor a vezérlés visszatér a hívó blokkba, majd az azt hívóba fel és fel, amíg kivételt le nem kezeljük. Közben a verem memória kiürül, de az abból kikerülő objektumok destruktorai nem hívódnak meg.
Mobil alkalmazások fejlesztése, Symbian OS/C++
18
Memóriakezelés void CMyClass::Foo() { TInt myInteger; TBufC<16> buffer; . . . //függvény törzs
Helyfoglalás a stack-en
} //A lefoglalt hely kilépéskor „törlődik”
void CBasicAppUi::Foo() { CMyClass* myPtr = new CMyClass; if(myPtr) { myPtr->Foo(); //Adatok és függvények biztonságos elérése } delete myPtr; }
Helyfoglalás és biztonságos elérés a heapen
A kódolást nagyon nehézkessé tenné, ha minden helyfoglalást becsomagolnánk egy ilyen ellenőrzésbe.
Objektum létrehozása a heapen Symbianban LOCAL_C void MainL() { CMyClass* myPtr = new CMyClass; if(myPtr) { myPtr->Foo(); //Can safely access data & functions } delete myPtr; }
LOCAL_C void MainL() { CMyClass* myPtr = new (ELeave) CMyClass; myPtr->Foo(); . . . delete myPtr; }
Erre a pontra csak akkor kerül a vezérlés, ha biztosan megtörtént a helyfoglalás.
Ha nincs hely, akkor leavel.
CleanupStack LOCAL_C void MainL() { CMyClass* myPtr = new (ELeave) CMyClass; myPtr->Foo();
Nem leavelő függvényt hívunk.
. . . delete myPtr; } case ECleanupCmdUse3: { CX* x=new(ELeave) CX; x->UseL(); delete x; }
Az x automatikus változó a heap-re mutat. Ha UseL() leave-el, akkor a delete nem hajtódik végre, a CX által lefoglalt terület „árván” marad.
Leavelő függvényt hívunk. Megoldás: CleanupStack
case ECleanupCmdUse3: { CX* x=new(ELeave) CX; CleanupStack::PushL(x); x->UseL(); CleanupStack::PopAndDestroy(x); }
A CX helyfoglalása után a rá mutató pointert elhelyezzük a Cleannup Stack-en. Ha UseL() nem leave-el, akkor MI szedjük le a címet a stackről. Ha UseL() leave-el, akkor a Laeve kezelő eljárás.
CleanupStack alkalmazása
case ECleanupCmdUse3: { CX* x1=new(ELeave) CX; CleanupStack::PushL(x1); CX* x2=new(ELeave) CX; CleanupStack::PushL(x2); x1-<UseL(); CleanupStack::PopAndDestroy(2); }
Tilos!! CleanupStack::PushL(iMember);
A PushL() nem leave-el (előrefoglalás miatt)
Egyszerre több elemet is leemelhetünk a stackről.
Pointert tartalmazó adattagot ne tegyünk a CleanupStack-re. Az általa mutatott terület felszabadítása az adattagot tartalmazó osztály feladata, nem a Leave-elő mechanizmusé.
Leavelés és CleanupStack
case ECleanupCmdUse3: { CX* x=new(ELeave) CX; TRAPD (error, x->UseL(); if(error) { delete x; User::Leave(error); } delete x; }
case ECleanupCmdUse3: { CX* x=new(ELeave) CX; CleanupStack::PushL(x); x->UseL(); x->UseL(); x->UseL(); CleanupStack::PopAndDestroy(x); }
Leave kezelése TRAP segítségével. Több függvényhívás esetén mindegyiket bele kellene tenni egy TRAP konstrukcióba.
A CleanupStack használatával egyserűbben kódolhatjuk, ha egy eljárson belül több leavelő függvényt is meg kell hívnunk.
Mostmár minden rendben van?
class CY : public CBase { public: CY(); ~CY(); public: CX* iX; };
class CX : public CBase { public: void UseL(); public: TInt iInt; };
CY::CY() // bad bad { iX=new(ELeave) CX; }
void CX::UseL() { TInt* pi=new(ELeave) TInt; delete pi; }
CY::~CY() { delete iX; }
Probléma: Az y példányosításakor az Y konstruktora leaveelhet és a „félkész” y által lefoglalt tárhely árván marad.
{ CY* y=new(ELeave) CY; CleanupStack::PushL(y); y->iX->UseL(); CleanupStack::PopAndDestroy(); // y }
„Dinamikus” osztály példányosítása - probléma class CY : public CBase { public: CY(); ~CY(); public: CX* iX; };
class CX : public CBase { public: void UseL(); public: TInt iInt; };
CY::CY() // bad bad { iX=new(ELeave) CX; }
void CX::UseL() { TInt* pi=new(ELeave) TInt; delete pi; }
CY::~CY() { delete iX; }
CY* y; CY* temp = User::AllocL(sizeof(CY)); //Allocate memory temp->CY::CY(); //C++ constructor y=temp;
{ CY* y=new(ELeave) CY; CleanupStack::PushL(y); y->iX->UseL(); CleanupStack::PopAndDestroy(); // y }
Megoldás: Kétfázisú konstrukció A konstruktort két részre bontjuk: 1. rész: Biztonságos, nem leave-elő a példányra mutató pointer biztosan felkerül a CleanupStack-re 2. rész: A „veszélyesebb” leave-elő rész. De ekkor már jó helyen van a pointer.
Kétfázisú konstrukció
class CZ : public CBase { public: static CZ* NewL(); static CZ* NewLC(); void ConstructL(); ~CZ(); public: CX* iX; };
A „két fázist” a NewL, NewLC függvényekbe „becsomagolják”.
void CZ::ConstructL() { iX=new(ELeave) CX; } CZ::~CZ() { delete iX; } CZ* CZ::NewL() { CZ* self=new(ELeave) CZ; CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); return self; } CZ* CZ::NewLC() { CZ* self=new(ELeave) CZ; CleanupStack::PushL(self); self->ConstructL(); return self; }
Kétfázisú konstrukció - összefoglalás Az osztály standard konstruktorában nem hívunk leave-elő kódot A leave-elő hívásokat egy külön „második fázisú konstruktorba”
tesszük. (ConstructL) Az osztály példányosításakor: Meghívjuk a standard konstruktort (new) A „félig létrejött” objektumot feltesszük a CleanupStack-re Meghívjuk a második fázisú konstruktort (ConstructL)
(Levesszük a CleanupStack-ről)
A „két fázist” a NewL, NewLC függvényekbe „becsomagolják”.
Cleanup-safe HelloWorld
int HelloWorldL() { CConsoleBase* console = Console::NewL(_L("Hello World"),TSize(KConsFullScreen,KConsFullScreen)); console->Printf(KTxtMessage); console->Printf(KTxtPressAnyKey); console->Getch(); // get and ignore character delete console; return 0; }
Konzolos alkalmazásnál a CleanupStack-et nekünk kell létrehozni.
TInt E32Main() { __UHEAP_MARK; CTrapCleanup* cleanup = CTrapCleanup::New(); TInt retVal = KErrNone; if(cleanup) { TRAP(retVal,HelloWorldL()); __ASSERT_ALWAYS(!retVal, User::Panic(_L("Hello World Panic"),retVal)); delete cleanup; } __UHEAP_MARKEND; return retVal; }
int main() { printf("Hello world!\n"; return 0; }
Aszinkron események kezelése - ActiveObject CDeleyHello.cpp CDelayedHello* CDelayedHello::NewL() { CDelayedHello* self = new (ELeave) CDelayedHello(); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; } CDelayedHello::CDelayedHello() : CActive(0) { CActiveScheduler::Add(this); } void CDelayedHello::ConstructL() { iEnv = CEikonEnv::Static(); User::LeaveIfError(iTimer.CreateLocal()); } CDelayedHello::~CDelayedHello() { Cancel(); iTimer.Close(); }. . .
ActiveHello projekt
Aszinkron események kezelése - ActiveObject CDeleyHello.cpp . . . // from CActive void CDelayedHello::RunL() { iEnv->InfoMsg(R_ACTIVEHELLO_TEXT_HELLO); } void CDelayedHello::DoCancel() { iTimer.Cancel(); }
TInt CDelayedHello::RunError(TInt /*aError*/) { return KErrNone; //Must be given if RunL() calls a leaving function }. . .
ActiveHello projekt
Deszkriptorok
Symbian OS C++ 3rd Edition
GUI alkalmazások felépítése Application creates
AppView (V)
creates
Document
Model (M)
creates, manages
AppUi (C) manages
creates, manages
renders
manages
bld.inf
bld.inf PRJ_PLATFORMS DEFAULT PRJ_MMPFILES Legkisebb.mmp
Legkisebb.mmp TARGET TARGETTYPE
Legkisebb_0x0CBFF1BD.exe exe
Legkisebb.mmp
UID 0x100039CE 0x0CBFF1BD SOURCEPATH ..\src SOURCE Legkisebb.cpp LegkisebbApplication.cpp LegkisebbAppView.cpp LegkisebbDocument.cpp LegkisebbAppUi.cpp USERINCLUDE ..\inc CAPABILITY ReadUserData SOURCEPATH ..\data START RESOURCE Legkisebb.rss TARGET Legkisebb_0x0CBFF1BD TARGETPATH resource\apps HEADER END START RESOURCE Legkisebb_reg.rss TARGET Legkisebb_0x0CBFF1BD_reg TARGETPATH \private\10003a3f\apps END LANG SC SECUREID 0x0CBFF1BD VENDORID 0 SYSTEMINCLUDE
\epoc32\include
LIBRARY euser.lib apparc.lib cone.lib eikcore.lib avkon.lib commonengine.lib efsrv.lib estor.lib aknnotify.lib gdi.lib
Legkisebb.mmp Legkisebb.mmp
A .mmp fájl tartalma garfikus felületen is beállítható.
Legkisebb.rss
Legkisebb.pan, .rls, .hrh Legkisebb.rls
Legkisebb.pan #ifndef LEGKISEBB_PAN_ #define LEGKISEBB_PAN_
// Caption string for app. #define qtn_caption_string "Legkisebb 0x0CBFF1BD"
enum TLegkisebbPanics { ELegkisebbUi = 1 }; inline void Panic(TLegkisebbPanics aReason) { _LIT(applicationName, "Legkisebb-0x0CBFF1BD"); User::Panic(applicationName, aReason); }
Legkisebb.hrh #ifndef __LEGKISEBB_HRH__ #define __LEGKISEBB_HRH__ #define _UID3 0x0CBFF1BD
#endif /*LEGKISEBB_PAN_*/ #define qtn_caption_string "Legkisebb 0x0CBFF1BD"
#endif // __LEGKISEBB_HRH__
Legkisebb.cpp Legkisebb.cpp #include <eikstart.h> #include "LegkisebbApplication.h" LOCAL_C CApaApplication* NewApplication() { return new CLegkisebbApplication; } GLDEF_C TInt E32Main() { return EikStart::RunApplication( NewApplication ); }
LegkisebbApplication osztály #include
#include "Legkisebb.hrh"
LegkisebbApplication.h
const TUid KUidLegkisebbApp = { _UID3 }; class CLegkisebbApplication : public CAknApplication { public: TUid AppDllUid() const; protected: CApaDocument* CreateDocumentL(); };
#include "Legkisebb.hrh" #include "LegkisebbDocument.h" #include "LegkisebbApplication.h"
LegkisebbApplication.cpp
CApaDocument* CLegkisebbApplication::CreateDocumentL() { return CLegkisebbDocument::NewL(*this); } TUid CLegkisebbApplication::AppDllUid() const { return KUidLegkisebbApp; }
LegkisebbAppDocument osztály - definíció LegkisebbAppDocument.h #include class CLegkisebbAppUi; class CEikApplication; class CLegkisebbDocument : public CAknDocument { public: static CLegkisebbDocument* NewL(CEikApplication& aApp); static CLegkisebbDocument* NewLC(CEikApplication& aApp); virtual ~CLegkisebbDocument(); public: CEikAppUi* CreateAppUiL(); private: void ConstructL(); CLegkisebbDocument(CEikApplication& aApp); };
LegkisebbAppDocument osztály - implementáció #include "LegkisebbAppUi.h" #include "LegkisebbDocument.h"
LegkisebbAppDocument.cpp
CLegkisebbDocument* CLegkisebbDocument::NewL(CEikApplication& aApp) { CLegkisebbDocument* self = NewLC(aApp); CleanupStack::Pop(self); return self; } CLegkisebbDocument* CLegkisebbDocument::NewLC(CEikApplication& aApp) { CLegkisebbDocument* self = new ( ELeave ) CLegkisebbDocument( aApp ); CleanupStack::PushL(self); self->ConstructL(); return self; } void CLegkisebbDocument::ConstructL() {} CLegkisebbDocument::CLegkisebbDocument(CEikApplication& aApp) : CAknDocument(aApp) {} CLegkisebbDocument::~CLegkisebbDocument() {} CEikAppUi* CLegkisebbDocument::CreateAppUiL() { return new ( ELeave )CLegkisebbAppUi; }
LegkisebbAppUi osztály - definíció LegkisebbAppUi.h #include class CLegkisebbAppView; class CLegkisebbAppUi : public CAknAppUi { public: void ConstructL(); CLegkisebbAppUi(); virtual ~CLegkisebbAppUi(); private: void HandleCommandL(TInt aCommand); void HandleStatusPaneSizeChange(); private: CLegkisebbAppView* iAppView; };
LegkisebbAppUi osztály - implementáció void CLegkisebbAppUi::ConstructL() { BaseConstructL(CAknAppUi::EAknEnableSkin); iAppView = CLegkisebbAppView::NewL(ClientRect() ); }
#include #include #include #include
CLegkisebbAppUi::CLegkisebbAppUi(){} #include
CLegkisebbAppUi::~CLegkisebbAppUi() { if (iAppView) { delete iAppView; iAppView = NULL; } }
#include #include #include #include #include
void CLegkisebbAppUi::HandleCommandL(TInt aCommand) { switch (aCommand) { case EEikCmdExit: case EAknSoftkeyExit: Exit(); break; default: Panic(ELegkisebbUi); break; } } void CLegkisebbAppUi::HandleStatusPaneSizeChange() { iAppView->SetRect(ClientRect()); LegkisebbAppUi.cpp }
"Legkisebb.hrh" "Legkisebb.pan" "LegkisebbApplication.h" "LegkisebbAppUi.h" "LegkisebbAppView.h"
LegkisebbAppView osztály - definíció #include class CLegkisebbAppView : public CCoeControl { public: static CLegkisebbAppView* NewL(const TRect& aRect); static CLegkisebbAppView* NewLC(const TRect& aRect); virtual ~CLegkisebbAppView(); public: void Draw(const TRect& aRect) const; virtual void SizeChanged(); private: void ConstructL(const TRect& aRect); CLegkisebbAppView(); private: HBufC *iHelloMessage; };
LegkisebbAppView.h
LegkisebbAppView osztály – implementáció 1. CLegkisebbAppView* CLegkisebbAppView::NewL(const TRect& aRect) { CLegkisebbAppView* self = CLegkisebbAppView::NewLC(aRect); CleanupStack::Pop(self); return self; }
#include #include #include #include
CLegkisebbAppView* CLegkisebbAppView::NewLC(const TRect& aRect) { CLegkisebbAppView* self = new ( ELeave ) CLegkisebbAppView; CleanupStack::PushL(self); self->ConstructL(aRect); return self; } _LIT(KHelloMessage,"Hello Legkisebb!"); void CLegkisebbAppView::ConstructL(const TRect& aRect) { CreateWindowL(); SetRect(aRect); ActivateL(); iHelloMessage=HBufC::NewL(30); *iHelloMessage=KHelloMessage; } CLegkisebbAppView::CLegkisebbAppView() {} CLegkisebbAppView::~CLegkisebbAppView() { delete iHelloMessage; }
LegkisebbAppView.cpp
<EIKENV.H> "LegkisebbAppView.h"
LegkisebbAppView osztály – implementáció 2. void CLegkisebbAppView::Draw(const TRect& /*aRect*/) const { CWindowGc& gc = SystemGc(); TRect drawRect(Rect()); gc.Clear(drawRect); drawRect.Shrink(10,10); gc.DrawRect(drawRect); const CFont* fontUsed = CEikonEnv::Static()->TitleFont(); gc.UseFont(fontUsed); TInt baselineOffset = (drawRect.Height() + fontUsed>HeightInPixels())/2; gc.DrawText(*iHelloMessage,drawRect,baselineOffset,CGraphicsContext:: ECenter,0); gc.DiscardFont(); } void CLegkisebbAppView::SizeChanged() { DrawNow(); }
LegkisebbAppView.cpp
Carbide++
Futtatás
Futtatáshoz válasszuk az emulátort, mert így megkapjuk a hibaüzenetet.
Hibaüzenetek értelmezése
Projektek
First Legkisebb ActiveHello ConsLauncher OandX Peekaboo
Tutorial: http://developer.symbian.org/wiki/index.php/Going_Beyond_Hello:_A _Tutorial_for_Symbian_C%2B%2B_Applications
AandX
Kész alkalmazások importálása Carbide.C++ alá Az importálni kívánt projek bld.inf fájlja