×
Namespaces

Variants
Actions

Shared Library DLLs on Qt for Symbian

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Code ExampleCompatibility
Platform(s):
Symbian
Article
Created: hamishwillee (17 Dec 2010)
Last edited: hamishwillee (11 Oct 2012)

This article demonstrates how to create and use shared library DLLs with Qt on Symbian . It is accompanied by a simple code example that populates a label widget with a random number from a DLL when a button is pressed.

Contents

Introduction

Shared library DLLs are a common paradigm on most operating systems; re-usable functionality is stored in a shared library that executable code can link against at build time and load at runtime. As the functionality in the DLL is shared by several clients the overall code size, memory usage and maintenance are reduced.

Shared library DLLs are created and used in much the same way on the Symbian platform as on other platforms. However there are a number of platform specific issues that developers need to be aware of:

  • Symbian uses link by ordinal exclusively: Shared library DLLs provide a fixed API with a number of entry points. On some platforms entry points can be referenced either by name or by ordinal number - on Symbian you can only use the ordinal number (the names are stripped out of the DLL in order to reduce code size). In practice this means that we will need to create a .def file to ensure that the ordinals are kept in the same order between releases and thereby maintain binary compatibility. See Fundamentals of Symbian C++/Compatibility for more information.
  • DLLs may need Platform security capabilities.
  • DLLs need a unique identifier (UID)
  • DLLs that have writeable static data are not fully supported by the free GCCE 3.4.3 compiler. For more information see Symbian Platform Support for Writeable Static Data in DLLs and WSD and the Singleton Pattern

This article walks you through the steps for creating, using and packaging a shared DLL. At the end of the article there is an overview of the code example.

Tip.pngTip: While shared library DLLs are recommended when library code is going to be shared by many clients, there are other options that may be more suitable for application developers - for example including the code as a static library or building it as part of the application executable.

Creating and building a shared library

Creating a shared library is relatively easy:

Creating the library

The easiest way to create a skeleton shared library is to use the Qt Creator Wizard: File | New File or Project | Projects | C++ Library. This creates a number of files, which are discussed below.

Project file (.pro)

The .pro file created by the wizard is as shown below. The interesting lines are:

  • TEMPLATE=lib which tells qmake that we're building a shared library DLL
  • DEFINES += QTENGINEDLL_LIBRARY, which creates a project specific macro which we use (and explain) in the header files section below.

The remaining lines define the sources and headers, and which Qt libraries the DLL will link against. Note that in this case that will only be the "core" libraries because we've removed the "gui" (we might equally well have just put QT = core).

QT       -= gui
 
TEMPLATE = lib
 
DEFINES += QTENGINEDLL_LIBRARY
 
SOURCES += qtenginedll.cpp
 
HEADERS += qtenginedll.h\
qtenginedll_global.h

In addition to the general settings, we define some Symbian-specific sections as shown in the following code fragment.

#Symbian specific definitions
symbian: {
TARGET.UID3 = 0xEF76E062
TARGET.EPOCALLOWDLLDATA = 1
# TARGET.CAPABILITY = ReadDeviceData
 
BLD_INF_RULES.prj_exports += "qtenginedll.h"
BLD_INF_RULES.prj_exports += "qtenginedll_global.h"
}

Set DLL unique identifier
You must define a UID3 for the application using TARGET.UID3. The UID used below is a random number from the 0xE test range, however if you're deploying an app commercially you'd get a specific UID allocated by Symbian Signed.

Export header files
As this is a DLL we export its public header files into the SDK's /epoc32/include/ directory using the BLD_INF_RULES.prj_exports keyword. This adds the headers into the include path of any application built against the SDK.

Tip.pngTip: The BLD_INF_RULES.prj_exports keyword copies the specified text into the (temporary) bld.inf file used by the Symbian toolchain to build the project. This mechanism can be used to copy other file types into other locations in the Emulator tree using the syntax described here: bld.inf file syntax reference documentation. In this case we haven't specified a destination file, so the file is copied (with the same name) to /epoc32/include.

Enable static data
DLLs that use writable static data (WSD) must define TARGET.EPOCALLOWDLLDATA = 1 as shown. Writable static data has some associated costs and should be avoided if your DLL is to be shared by many clients. See Symbian Platform Support for Writeable Static Data in DLLs and WSD and the Singleton Pattern for more information.

Set platform security capabilities
Access to sensitive APIs are protected by platform security capabilities - these are discussed in the article Fundamentals of Symbian C++/Platform Security. Capabilities are treated slightly differently for EXES and DLLs. In essence, while an EXE must be granted the capabilities required to call the APIs that it uses (or which are used by its loaded DLLs), a DLL must be given all the capabilities of all the EXEs that might need to load it. For a DLL used by a single application exe this would be the same as the application's capabilities. If however the DLL is to be used by arbitrary clients, you will need to give it as many capabilities as possible. Capabilities are added using TARGET.CAPABILITY; in this example we don't need any, so the option is commented out.

Header files (.h)

The public header files are shown below. The first header file defines a single class QtEngineDll with a constructor and a single method that returns a random number between two values (by default, between zero and one hundred).

#ifndef QTENGINEDLL_H
#define QTENGINEDLL_H
 
#include "qtenginedll_global.h"
 
class QTENGINEDLLSHARED_EXPORT QtEngineDll {
public:
QtEngineDll();
int randInt(int aLow=0, int aHigh=100);
 
};
 
#endif // QTENGINEDLL_H

The interesting part of the class declaration is the use of the QTENGINEDLLSHARED_EXPORT macro, which marks the class for export/import from the DLL. The definition of this macro in the #includeed header file qtenginedll_global.h show below.

#ifndef QTENGINEDLL_GLOBAL_H
#define QTENGINEDLL_GLOBAL_H
 
#include <QtCore/qglobal.h>
 
#if defined(QTENGINEDLL_LIBRARY)
# define QTENGINEDLLSHARED_EXPORT Q_DECL_EXPORT
#else
# define QTENGINEDLLSHARED_EXPORT Q_DECL_IMPORT
#endif
 
#endif // QTENGINEDLL_GLOBAL_H

What is happening here is basically a trick to ensure that we can use a single header file to apply the correct dllexport / dllimport declaration to the class when exporting / importing the DLL (some compilers don't like you to mix them).

In the DLL .pro file we define the QTENGINEDLL LIBRARY macro; when building the DLL the class is marked with Q_DECL_EXPORT. When we build the application (or other client) we don't define this macro, so the header is marked as Q_DECL_IMPORT (an import).

Note.pngNote: Symbian C++ developers may find exporting the whole class a bit odd - in Symbian C++ code we export only those members which are part of the public API. Qt uses a different but equally valid approach - the API is separated into a public API (exported) and a private implementation (not exported). This approach is fine in a Symbian context provided that a class doesn't export and then change any private methods.

Source file (.cpp)

There is nothing "special" about the source file, however I've included it below for completeness. As you can see, all this does is create a seed for the random number generator based on the time the QtEngineDll class is created, and return the next "random" value in the sequence whenever randInt() is called.

#include <QTime>
#include "qtenginedll.h"
 
 
QtEngineDll::QtEngineDll()
{
//Create random number seed
QTime time = QTime::currentTime();
qsrand((uint)time.msec());
}
 
int QtEngineDll::randInt(int aLow, int aHigh)
{
// Random number between low and high
return qrand() % ((aHigh + 1) - aLow) + aLow;
}


Building and freezing the DLL API

On the Symbian platform the function name is stripped from the DLL in order to reduce its size; instead of linking to the function name we link to its ordinal position in the exports. The only way we can guarantee that a new DLL is binary compatible with the old version is to make sure that the position of the exported functions is the same every time the DLL is built.

The process of ensuring that the ordinals are the same every time is known as freezing the API. This is done through the use of a .def file which records the position of each function. The format of the .def file is different for Emulator (winscw) and target (GCCE/ARMv5). To generate interface definition file you need to add the following instruction to your project profile:

MMP_RULES += EXPORTUNFROZEN

in the build log you may notice warning about your dll interface is unfrozen -- that means your interface is still in development stage and may be changed freely. To freeze your dll interface use Qt SDK command prompt

sbs -c gcce_udeb freeze

Note.pngNote: Qt SDK command prompt means OS command interpreter with environment variables needed to run Qt command lines. You can launch command prompt from Qt SDK program group --> target (e.g. Symbian Anna) --> Command Prompt. In the Command Prompt go to your project profile location before issue command to freeze interface

Using the DLL

Shared libraries are used in much the same way, irrespective of whether they come as part of Qt, you've created them yourself, or they come from another third party:

  1. #include the header file(s) for the shared library
  2. Add the library to the application project (.pro) file
  3. Create and use the objects.

The library is included and used as shown (in this case iTheEngine is a QtEngineDll that is owned by the user interface)

#include <qtenginedll.h>
 
...
int currentValue = iTheEngine->randInt();

To add the file to the library to the application, use the LIBS keyword in its .pro file, where qtenginedll is the name of our DLL:

LIBS += -lqtenginedll

Note that the application .pro file should also specify all the capabilities that the EXE needs to use any protected API that it calls or the DLL calls - the DLL should have all the capabilities specified in the EXE (and it may have more).

The rest of the example is trivial, so has not been reproduced here.

The last step in the process is to deploy the DLL. This is discussed in the following section.

Deploying the DLL

Deploying as part of your application

Including the DLL in your application SIS file is a reasonable approach if the application is the only client, and the main reason you've created the library as a DLL is to allow it to be patched when necessary.

Include the DLL in the application's SIS file by adding the following lines to the application project file:

symbian: {
addFiles.sources = \epoc32\release\$(PLATFORM)\$(CFG)\qtenginedll.dll
addFiles.path = \sys\bin
DEPLOYMENT += addFiles
}

This will add the following lines to your pkg file:

; DEPLOYMENT
"c:/S60/devices/S60_5th_Edition_SDK_v1.0/epoc32/release/$(PLATFORM)/$(TARGET)/qtenginedll.dll" - "!:\sys\bin\qtenginedll.dll"

Deploying as and embedded SIS

Genuinely shared DLLs (ie DLLs with many clients) shouldn't be added directly into the application SIS; Instead, shared DLLs should be packaged in their own SIS files which should then be embedded in the application's SIS file (or possibly just listed as a dependency, depending on its size). This allows the DLLs to be delivered with every application that needs them and patched separately.

Warning.pngWarning: Embedding SIS files can complicate Symbian Signed. The embedded SIS file must be signed before being embedded, and a test UI must be provided.

To create a SIS file for the DLL a DEPLOYMENT line must be added to the DLL project file, as shown:

symbian: {
addFiles.sources = qtenginedll.dll
addFiles.path = /sys/bin
DEPLOYMENT += addFiles
}

Note that the SIS file would probably require further customisation, e.g. the change the vendor ID or other human readable strings. This is discussed further in Deploying a Qt Application on Symbian.

The DLL SIS file can then be embedded in your main application SIS file by making the following addition to it's project file:

myembeddedsis.pkg_postrules = "@/"C:/the_path_to_the_DLL_SISX_file/qtengine.sisx/",(0xEF76E062)"\
DEPLOYMENT += myembeddedsis

, where the UID specified is that of the DLLs SIS.

Code example overview

Description

The code example that accompanies this article is here:File:QtSymbianDllExample.zip.

The code example consists of a simple DLL (qtenginedll.dll) that exports a constructor, destructor, and a method that returns a random number. The DLL is loaded by a test UI (testuisimpledll.exe), which updates a label using the random number whenever the button is pressed.

Environment

The application (and instructions) have been tested using the following environment (note that it is expected to work in any Qt 4.6 environment):

  • Device : N8 software version 111.030.0609
  • Qt SDK : 1.2
  • build target  : Qt 4.7.4 for Symbian Anna

Building and deploying the example

The example can be built through the project file \QtSymbianDllExample\qtssymbiandllexample.pro; this references the individual project files for the DLL and the test UI, ensuring that the DLL is build first.

Assuming you are using Qt Creator you can import the example using File | Open File or Project then select the file: \QtSymbianDllExample\qtssymbiandllexample.pro.


By default Qt Creator will set the correct build configuration for a target device (GCCE). To be able to run the application on device you need to install CODA debug agent on device. You can find CODA installation package in Qt SDK by path: <QtSDK>\Symbian\sis\common\CODA

Folder structure

This is the folder structure for the example:

\QtSymbianDllExample  - the parent folder, contains the "master" .pro file
      \qtenginedll - engine DLL 
           \bwins  - .def file for winscw
           \eabi  - .def file for eabi (armv5/GCCE)
      \testui_simpledllengine - UI test code exe


Defects affecting this article


Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 11 October 2012, at 04:18.
86 page views in the last 30 days.
×