This topic describes how to port a range of traditional C applications to Symbian OS. It uses three examples: Hello
, ConsoleApp
and GUIApp
. The first two are simple console-based examples. The third is a command-line driven program which has been converted into a standard Symbian application. It demonstrates how to link STDLIB into a C++ project. It also demonstrates some typical problems which may be encountered when porting C code to Symbian OS, including the restriction on writeable data in DLLs. These example code projects can all be found in directory epoc32ex\stdlib\
.
Note that these examples may not be present on all Symbian OS v6.1 SDKs.
Some of the code for the second two examples was taken from the FreeBSD source code. For information on FreeBSD, see http://www.freebsd.org.
If it is not already present on the target machine, STDLIB should be installed using the stdlib.sis
installation file, supplied by the SDK in directory \epoc32\release\wins\udeb\
and \epoc32\release\wins\urel\
. Installation using a .sis
file has several advantages over copying files manually, including:
the DLL is installed in the correct location
the correct version of the DLL is installed
if you supply a .sis
file to install your product, STDLIB can easily be included as a sub-component of it
For more information on how to include a .sis
file as a sub-component of another, see How to create an installation with embedded sis files.
The code in this section has been taken from the Hello
example.
This example demonstrates the most basic use of STDLIB. It consists of the single source code file, slhello.c
whose sole function, main()
calls printf()
.
The Hello
example's project definition file (Hello.mmp
) contains the following project specification:
TTARGET hello.exe
TARGETTYPE exe
UID 0
SOURCEPATH .
SOURCE slhello.c
SYSTEMINCLUDE \epoc32\include\libc \epoc32\include
LIBRARY estlib.lib euser.lib
STATICLIBRARY ecrt0.lib
The project includes the import libraries estlib.lib
(the C standard library), and euser.lib
(the E32 user library), whose services are used by STDLIB.
The project also links to ecrt0.lib
using the STATICLIBRARY
keyword. This file provides the E32Main()
entrypoint for a .exe
. It also provides other services including command-line parsing, and it calls main()
.
The SYSTEMINCLUDE
path specifies \epoc32\include\libc\
. This is the directory in which STDLIB's header files are installed.
For more information on the project specification for a .exe
see How to build for an EXE target.
To perform a debug build of Hello
for the Emulator, run bldmake
from the directory where the bld.inf
file is located:
bldmake bldfiles
This creates the abld.bat
batch file.
Use abld
to build the project:
abld build wins udeb
To start the emulator locate the directory containing the debug emulator, \epoc32\release\wins\udeb\
or type epoc
at the command line. For more information on launching the emulator see How to start the emulator.
For information on building see How to use the Symbian build process.
To build Hello
for the target machine, invoke bldmake
and abld
, specifying either arm4 udeb
or arm4 urel
.
To install Hello.exe
on the target device you will need to create a .sis
file using the Symbian Installation system; see the SIS file creator guide, Application installation guide and Installation reference.
This program converts a quantity from one unit of measurement into another, prompting the user for input. Conversion information is provided by slunits.dat
.
This program is built and run in much the same way as hello.exe
. The user is prompted for input, but it is important to note that STDLIB does not provide all of the functionality of a traditional console driver. STDLIB is intended to be a "behind-the-scenes" enabling technology, rather than an alternative user interface.
The code for the ComsoleApp program was taken from the FreeBSD source code (See http://www.freebsd.org ).
The .mmp
file project specification for ConsoleApp is the same as Hello's, except that it uses the additional source files units.c
and getopt.c
.
Run and build the program using abld
and bldmake
, as for the Hello example. For the Emulator slunits.dat
is copied automatically by abld into the right directory, this location is \epoc32\wins\c\
. For a target device ensure that slunits.dat
is located in the directory specified in pathname.h
, this is the root of the drive on which the program is installed.
To install ConsoleApp.exe
on the target device you will need to create a .sis
file using the Symbian Installation system; see the SIS file creator guide, Application installation guide and Installation reference.
To run it on the target device, tap on the ConsoleApp.exe
icon.
Note that in porting this project to the Symbian platform a reduction in the size of the two arrays in the struct unittype
, defined in units.c
was made.
struct unittype {
char *numerator[225]; // was [500]
char *denominator[225]; // was [500]
double factor;
};
This change was necessary because each thread has only 8k stack as standard. Failing to observe the limit on stack usage may cause a build to fail with the following error:
unresolved external symbol __chkstk
The application uses the FreeBSD cksum
utility as its engine.
The checksum example is an application with a user interface and an engine written in C. It is anticipated that many C programs which are ported to the Symbian platform will use this structure.
A checksum is a number generated by adding together the value of all the data in a file. Recalculating a file's checksum and comparing the new value with a previously calculated value will reveal whether any changes have occurred to the file's contents.
The checksum generating code was taken from FreeBSD source code. The original FreeBSD program is a traditional command-line utility. It uses its command-line arguments to specify the files to be processed and writes the files' size, name and checksum to the standard output without any user intervention. A choice of algorithm is provided.
The first stage in porting this program to the Symbian platform was to split the project into two parts — the engine (GUIAppEng) and the application (GUIApp). The engine must be built first because its DLL is used by the UI. The engine and the user interface could be built as a single project, but producing a separate DLL for the engine means that different applications could use the same engine code.
The engine is written in C. At its core is the crc()
function. This function takes a file descriptor and returns the checksum and the file size. The return value from this function is zero on success and greater than zero on failure.
The engine's project specification file (GUIAppEng.mmp
) is as follows:
TARGET GUIAppEng.dll
TARGETTYPE dll
UID 0x1000008d 0x01000a02
SOURCEPATH .
SOURCE crc.c GUIAppeng.cpp
SYSTEMINCLUDE \epoc32\include\libc \epoc32\include
LIBRARY estlib.lib euser.lib
#if defined(WINS)
deffile ..\GUIApp\GuiAppEngWINS.def
#else if defined(ARM)
deffile ..\GUIApp\GuiAppEngARM.def
#endif
nostrictdef
The project includes header files from both \epoc32\include\
and from libc\
. The output file is GUIAppEng.dll
, whose import library will be included by the UI.
The first UID specified (0x1000008d) is the UID for an interface DLL. This must be specified for any project which produces a DLL and an import library, and where the DLL is intended for automatic loading at run-time. The second UID ( 0x01100a02) is unique to the GUIApp
project.
Splitting the project into engine and UI means that the definition of crc()
in crc.c
must be marked EXPORT_C
because this functions will be exported from the engine's DLL.
Because all DLLs need an entry point called E32Dll()
, the GUIApp
project includes a fourth source code file, GUIAppEng.cpp
which consists of the single exported function:
EXPORT_C TInt E32Dll(TDllReason)
For more information on DLLs, see DLLs.
The implementation of checksum (GUIApp.app
) limits the user to the selection of a single file at a time and uses the default algorithm crc()
, defined in crc.c
, to produce a 32-bit value.
The user interface demonstrates a typical Symbian application whose engine is written in C and which uses STDLIB.
The application provides two main menu commands; Calculate checksum
and View checksums
. Calculate checksum
invokes a CExampleChecksumDialog
. This is similar to the standard file open dialog (CEikFileOpenDialog
). When a file has been selected, and OK
pressed, TDes::PtrZ()
is used to get a pointer to the zero terminated string containing the selected filename. The selected file is opened, using open()
, declared in epoc32\include\libc\sys\fcntl.h
, passing in the pointer:
const TUint16* fn=iFileName->PtrZ();
int fd = wopen((const wchar_t*)fn, O_RDONLY, 0);
This code fragment is taken from epoc32ex\stdlib\GUIApp.cpp.
open()
returns a file descriptor which the engine's crc()
function uses to identify the file. The checksum is calculated (unless an error occurred in attempting to open or read the file), and is printed in an infomessage. The file is closed using close()
declared in epoc32\include\libc\sys\unistd.h
.
The filename and checksum are appended to a descriptor array, the contents of which may be viewed by selecting View checksums
.
For information on Symbian applications, their project specification, and resource files, see How to build for a GUI App target
The application program includes several STDLIB header files. These files are located in the standard include directory \epoc32\include\libc\
which, as in previous examples, is specified in the SYSTEMINCLUDE
line in the .mmp
file. At link time, the program includes estlib.lib
and the engine DLL's .lib
file (GUIAppEng.lib
). Unlike the previous examples in this topic, this application does not link to ecrt0.lib
. In this application there is no main()
and the Symbian platform provides its own E32Main()
.
The project specification (GUIApp.mmp
) follows the typical pattern of a Symbian application's .mmp
file, with some exceptions.
This section describes some issues which may arise during the porting of code written in C to the Symbian platform.
The PETRAN stage of building may report a message similar to the following:
WARNING: Dll 'SLSUMENG[0x01000a02].DLL' has initialised data.
This warning, which is not reported when building for the Emulator, indicates that the DLL contains non-const static data. This is not allowed in ARM builds. If it is not obvious where the problem occurs, the associated .map
file (epoc32\release\<target>\urel\<dllname>.map
) contains information which can help to track down the source file involved. A search for from *(.bss)
(to find uninitialised data) or from *(.data)
(to find initialised data) in GUIAPPEng.map
will reveal the file in which the problem occurs, and the names of the offending variables, although static variables will not be named.
For more information about the restriction on writeable static data, see Writeable static data in DLLs.
In C++ source files which use STDLIB routines, the Symbian platform C++ include files should be included before any of the STDLIB files. Failure to do this will result in the following warning:
'NULL' : macro redefinition"
C and C++ have different views about the names of functions. If you refer to a C function from C++, ensure that its prototype is declared as extern "C"
. If there are several such function declarations, it may be more convenient to enclose them within the following:
#ifdef __cplusplus
extern "C" {
#endif
…
…
#ifdef __cplusplus
}
#endif
See for example epoc32ex\stdlib\GUIApp\extern.h
. For more information about issues which arise when mixing C and C++, refer to any good book on C++.
Some projects will produce the following error:
unresolved external symbol __chkstk
unless the amount of stack they use is reduced. Symbian threads have only 8k stack as standard.
The Symbian platform has a requirement that all resources which were allocated by an application must be cleaned up by the time the program terminates. On the Emulator, in debug builds, failure to do this will cause a panic from the __UHEAP_MARKEND
macro.
Because the data allocated in the thread-local storage for STDLIB's DLL (the _reent
structure) is not automatically cleaned up when the environment is destroyed, it must be cleaned up by the user of STDLIB.
The function to achieve this is CloseSTDLIB()
. To use this function, file epoc32\include\libc\sys\reent.h
should be included in the project. Call CloseSTDLIB()
after the point at which it is known that code in STDLIB's DLL will no longer be called and its thread-local storage no longer needed.
For example, see the destructor for CExampleDocument
in epoc32ex\stdlib\GUIApp\GUIApp.cpp
.