- •Contents
- •1.1 Fundamental Types
- •1.2 T Classes
- •1.3 C Classes
- •1.4 R Classes
- •1.5 M Classes
- •1.6 Static Classes
- •1.7 Buyer Beware
- •1.8 Summary
- •2 Leaves: Symbian OS Exceptions
- •2.1 Leaving Functions
- •2.3 Constructors and Destructors
- •2.4 Working with Leaving Functions
- •2.5 Trapping a Leave Using TRAP and TRAPD
- •2.6 LeaveScan
- •2.7 Summary
- •3 The Cleanup Stack
- •3.1 Using the Cleanup Stack
- •3.2 How Does the Cleanup Stack Work?
- •3.4 Using TCleanupItem for Customized Cleanup
- •3.5 Portability
- •3.6 An Incidental Note on the Use of Casts
- •3.7 Summary
- •5 Descriptors: Symbian OS Strings
- •5.3 Pointer Descriptors
- •5.4 Stack-Based Buffer Descriptors
- •5.5 Heap-Based Buffer Descriptors
- •5.6 Literal Descriptors
- •5.7 Summary
- •6 Good Descriptor Style
- •6.1 Descriptors as Parameters and Return Types
- •6.2 Common Descriptor Methods
- •6.3 The Use of HBufC Heap Descriptors
- •6.4 Externalizing and Internalizing Descriptors
- •6.5 The Overuse of TFileName
- •6.6 Useful Classes for Descriptor Manipulation
- •6.7 Summary
- •7 Dynamic Arrays and Buffers
- •7.1 CArrayX Classes
- •7.3 Why Use RArray Instead of CArrayX?
- •7.4 Dynamic Descriptor Arrays
- •7.5 Fixed-Length Arrays
- •7.6 Dynamic Buffers
- •7.7 Summary
- •8.1 Multitasking Basics
- •8.2 Event-Driven Multitasking
- •8.3 Working with Active Objects
- •8.4 Example Code
- •8.5 Threads Without an Active Scheduler
- •8.6 Application Code and Active Objects
- •8.7 Summary
- •9 Active Objects under the Hood
- •9.1 Active Object Basics
- •9.2 Responsibilities of an Active Object
- •9.3 Responsibilities of an Asynchronous Service Provider
- •9.4 Responsibilities of the Active Scheduler
- •9.5 Starting the Active Scheduler
- •9.6 Nesting the Active Scheduler
- •9.7 Extending the Active Scheduler
- •9.8 Cancellation
- •9.9 Request Completion
- •9.10 State Machines
- •9.11 Long-Running Tasks
- •9.14 Common Mistakes
- •9.15 Summary
- •10 Symbian OS Threads and Processes
- •10.2 Thread Priorities
- •10.3 Stopping a Running Thread
- •10.5 Exception Handling
- •10.6 Processes
- •10.7 Summary
- •11.2 How Do the Client and Server Fit Together?
- •11.3 How Do the Client and Server Communicate?
- •11.5 How Do Synchronous and Asynchronous Requests Differ?
- •11.6 How Is a Server Started?
- •11.7 How Many Connections Can a Client Have?
- •11.8 What Happens When a Client Disconnects?
- •11.9 What Happens If a Client Dies?
- •11.10 What Happens If a Server Dies?
- •11.15 How Many Outstanding Requests Can a Client Make to a Server?
- •11.16 Can Server Functionality Be Extended?
- •11.17 Example Code
- •11.18 Summary
- •12.2 Client Boilerplate Code
- •12.3 Starting the Server and Connecting to It from the Client
- •12.4 Server Startup Code
- •12.5 Server Classes
- •12.6 Server Shutdown
- •12.7 Accessing the Server
- •12.8 Summary
- •13 Binary Types
- •13.1 Symbian OS EXEs
- •13.2 Symbian OS DLLs
- •13.3 Writable Static Data
- •13.4 Thread-Local Storage
- •13.5 The DLL Loader
- •13.6 UIDs
- •13.8 Summary
- •14 ECOM
- •14.1 ECOM Architecture
- •14.2 Features of an ECOM Interface
- •14.3 Factory Methods
- •14.4 Implementing an ECOM Interface
- •14.5 Resource Files
- •14.6 Example Client Code
- •14.7 Summary
- •15 Panics
- •15.2 Good Panic Style
- •15.3 Symbian OS Panic Categories
- •15.4 Panicking Another Thread
- •15.5 Faults, Leaves and Panics
- •15.6 Summary
- •16 Bug Detection Using Assertions
- •16.3 Summary
- •17 Debug Macros and Test Classes
- •17.1 Heap-Checking Macros
- •17.2 Object Invariance Macros
- •17.3 Console Tests Using RTest
- •17.4 Summary
- •18 Compatibility
- •18.1 Forward and Backward Compatibility
- •18.2 Source Compatibility
- •18.3 Binary Compatibility
- •18.4 Preventing Compatibility Breaks
- •18.5 What Can I Change Without Breaking Binary Compatibility?
- •18.6 Best Practice: Planning for Future Changes
- •18.7 Compatibility and the Symbian OS Class Types
- •18.8 Summary
- •19 Thin Templates
- •20.1 Class Layout
- •20.3 Parameters and Return Values
- •20.4 Member Data and Functional Abstraction
- •20.5 Choosing Class, Method and Parameter Names
- •20.7 Summary
- •21 Good Code Style
- •21.1 Reduce the Size of Program Code
- •21.2 Use Heap Memory Carefully
- •21.3 Use Stack Memory Carefully
- •21.5 Optimize Late
- •21.6 Summary
- •Glossary
- •Bibliography and Online Resources
- •Index
FACTORY METHODS |
237 |
virtual void DecryptL(TDesC8& aCiphertext, TDesC8& aKey, TDes8& aPlaintext, CryptoInterface::TAlgorithm) = 0;
private:
TUid iDtor_ID_Key; // Identification on cleanup };
This example interface derives directly from CBase, but it is not mandatory for an ECOM interface definition to do so. Interfaces may, for example, equally well derive from another C class such as CActive, which, of course, itself derives from CBase. Although the interface is defined as a C class, you’ll notice that a couple of the usual characteristics of a C class are missing. Firstly, the class has no explicitly declared constructor because it is an abstract base class and will not itself be constructed.2 In addition, although the class defines a static NewL() factory function, it does not define a ConstructL() method for second-phase construction. This is because the NewL() factory method of CCryptoInterface does not use standard twophase construction, but instead calls ECOM to instantiate a suitable implementation object.
An ECOM interface has the following characteristics:
•It is an abstract class with a set of one or more pure virtual functions.
•It must provide one or more factory functions to allow clients to instantiate an interface implementation object.
•It must provide a means for its clients to release it, such as a destructor or a method such as Release() or Close().
•It has a TUid data member, used internally to identify an implementation instance for cleanup purposes.
14.3Factory Methods
Let’s look at the implementation of the two static factory methods in more detail. The overload of NewL() that takes no parameters creates an object of the default implementation of CCryptoInterface. In the example
2 The compiler generates an implicit default constructor when instances of classes deriving from CCryptoInterface are created, in order to construct the virtual function table for the class.
238 |
ECOM |
code below, the method hard-codes a specific implementation (CSoftwareCrypto) by passing ECOM its UID.3 This method of specifying a particular class assumes that the UID of at least one implementation class is known when the factory function for the interface is compiled.
An alternative, more loosely-coupled, technique for instantiating a default implementation uses the ECOM resolver. The resolver can be passed a cue to enable it to instantiate, for example, the first concrete class it discovers which implements that interface. I’ll illustrate how to do this later, when I discuss how to customize the ECOM resolver.
EXPORT_C CCryptoInterface* CCryptoInterface::NewL()
{
// Hard-coded to use CSoftwareCrypto by default const TUid KSWCryptoUid = { 0x10008EE5 };
TAny* defaultCrypto =REComSession::CreateImplementationL(KSWCryptoUid, _FOFF(CCryptoInterface, iDtor_ID_Key));
return (reinterpret_cast<CCryptoInterface*>(defaultCrypto));
}
REComSession::CreateImplementationL() is passed the UID of the required implementation and the offset4 of the iDtor_ID_Key member of the interface class. Don’t worry too much about this ECOMinternal housekeeping, which initializes iDtor_ID_Key so ECOM can later identify the object for cleanup. CreateImplementationL() returns a TAny* which must be re-cast to the interface pointer before returning it to the caller. There are a number of other overloads of
REComSession::CreateImplementationL(), which you’ll find documented in detail in your preferred SDK (from Symbian OS v7.0 onwards).
The second NewL() factory method takes a cue parameter, which it passes to the default ECOM resolver. The resolver uses it to determine the concrete instance of the implementation to be instantiated, by matching the cue against the default_data attributes for registered implementations of that interface. I’ll discuss the role of the default resolver and ECOM resource files in more detail later.
// interface_uid for CCryptoInterface
const TUid KCCryptoInterfaceUid = {0x10008EE0};
3 Implementations are identified by their implementation_uid attribute, which is declared in the resource file associated with the plug-in DLL, as I’ll discuss later in the chapter.
4 The _FOFF macro is defined as follows in e32def.h:
#define _FOFF(c,f) ((TInt)(&((c *)0)->f))
It returns the offset in bytes of a member variable f of class c
FACTORY METHODS |
239 |
EXPORT_C CCryptoInterface* CCryptoInterface::NewL(const TDesC8& aCue) {// Resolution using the default ECOM resolver TEComResolverParams resolverParams; resolverParams.SetDataType(aCue);
// Allows wildcards in the string match resolverParams.SetWildcardMatch(ETrue);
TAny* cryptoInterface = REComSession::CreateImplementationL(KCCryptoInterfaceUid, _FOFF(CCryptoInterface, iDtor_ID_Key), NULL, resolverParams));
return (reinterpret_cast<CCryptoInterface*>(cryptoInterface));
}
Besides the TEComResolverParams object, which encapsulates the resolver cue, other parameters passed to REComSession::CreateImplementationL() include the UID of the requested interface and the offset of iDtor_ID_Key as discussed previously. The third parameter, NULL in the example above, can be used to pass data to the initialization method of the concrete class. If CreateImplementationL() cannot find an appropriate interface implementation for the cue given, it leaves with KErrNotFound.
By calling NewL(), an interface client instantiates an object which implements that interface. The implementation type is either determined by the cue passed into NewL() or is the default type. Because the caller uses the interface to perform instantiation, it does not need information about the class that actually implements that interface. However, REComSession does provide a function, ListImplementationsL(), which returns information about all the implementations of a given interface in an object of type RImplInfoPtrArray. This can be used to provide interface clients with a list of every implementation class available. I’ll illustrate how a client might use this function at the end of this chapter.
EXPORT_C void CCryptoInterface::ListImplementationsL(RImplInfoPtrArray&
aImplInfoArray)
{
REComSession::ListImplementationsL(KCCryptoInterfaceUid,
aImplInfoArray);
}
The CCryptoInterface class also defines a virtual destructor, which calls the ECOM framework, passing iDtor_ID_Key to identify the object. This enables ECOM to perform reference counting and cleanup as necessary. Subclasses will inherit this destructor code, which C++ calls after the destructor of the derived class.
240 |
ECOM |
EXPORT_C CCryptoInterface:: CCryptoInterface()
{// Notify ECOM that this object is being deleted
REComSession::DestroyedImplementation(iDtor_ID_Key);
}
14.4Implementing an ECOM Interface
Having discussed CCryptoInterface, let’s move on to consider how it is implemented. Concrete classes must be defined in an ECOM plugin DLL, which is built with targettype ECOMIIC (which stands for ”ECOM Interface Implementation Collection”). Here’s the .mmp file for the example plug-in DLL:
// ECryptoExample.mmp
TARGET ECryptoExample.dll
TARGETTYPE ECOMIIC
// UID2 = ECOM plug-in DLL recognition, UID3 = unique UID for this DLL UID 0x10009D8D 0x10009EE1
SOURCEPATH .
SOURCE // ... Omitted for clarity
USERINCLUDE // ... Omitted for clarity
SYSTEMINCLUDE \epoc32\include \epoc32\include\ecom
RESOURCE 10009EE1.RSS // The resource file for this implementation
LIBRARY ECOM.lib |
// |
ECOM plug-in DLLs must link against ECOM.lib |
LIBRARY ... |
// |
Other libraries as required |
The plug-in is a polymorphic Symbian OS DLL and must define a standard DLL entry point as follows:
TBool E32Dll()
{
return (ETrue);
}
Within a plug-in DLL, an ECOM interface collection may contain one or more implementations of one or more ECOM interfaces and/or multiple implementations of the same interface. The sample code implements two subclasses of CCryptoInterface.
// implementation_uid = 0x10008EE5 (discussed later)
class CSoftwareCrypto : public CCryptoInterface
{
IMPLEMENTING AN ECOM INTERFACE |
241 |
public:
static CSoftwareCrypto* NewL();
CSoftwareCrypto();
// Implementation using a software cryptography library virtual void EncryptL(TDesC8& aPlaintext, TDesC8& aKey,
TDes8& aCiphertext);
virtual void DecryptL(TDesC8& aCiphertext, TDesC8& aKey, TDes8& aPlaintext);
private:
CSoftwareCrypto(); void ConstructL();
private:
... // Omitted for clarity };
// implementation_uid = 0x10008EE4 (discussed later)
class CHardwareCrypto : public CCryptoInterface
{
public:
static CHardwareCrypto* NewL();
CHardwareCrypto();
// Implementation using hardware & device drivers virtual void EncryptL(TDesC8& aPlaintext, TDesC8& aKey,
TDes8& aCiphertext);
virtual void DecryptL(TDesC8& aCiphertext, TDesC8& aKey, TDes8& aPlaintext);
private:
CHardwareCrypto(); void ConstructL();
private:
... // Omitted for clarity };
Besides the interface functions, every implementation must have an instantiation function which it ”registers” with ECOM. The classes above each define a static NewL() factory function, which uses two-phase construction as described in Chapter 4.
ECOM calls the NewL() factory function of a class when a call to
REComSession::CreateImplementationL() specifies that class directly or a given cue resolves to that implementation.
A plug-in DLL ”registers” these instantiation functions with ECOM by exporting a standard function (ImplementationGroupProxy()) which returns a pointer to an array of TImplementationProxy objects. This is the only function that a polymorphic ECOM plug-in DLL exports. Each TImplementationProxy object represents a single implementation class and contains the TUid which identifies the implementation (this should match the implementation_uid value for the class in its registration resource file) and a pointer to its instantiation method.
#include <ImplementationProxy.h> |
// ECOM header file |
#include "CHardwareCrypto.h" |
// Class definition for CHardwareCrypto |
#include "CSoftwareCrypto.h" |
// Class definition for CSoftwareCrypto |
|
|