- •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
242 |
ECOM |
// An array of TImplementationProxy objects which connect each
//implementation with its instantiation function const TImplementationProxy ImplementationTable[] =
{
{{0x10008EE4}, CHardwareCrypto::NewL}, {{0x10008EE5}, CSoftwareCrypto::NewL} };
//Exported proxy function to resolve instantiation methods for an ECOM
//plug-in DLL
EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount)
{
aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy);
return ImplementationTable;
}
Slightly confusingly, ECOM uses a number of different UIDs for identification:
•interface_uid is used to identify a specific interface
•implementation_uid is used to identify each concrete class that implements an interface
•an ECOM plug-in DLL, which can contain one or more ECOM interface implementations, is identified using UID3 for the DLL (as described in Chapter 13).
14.5Resource Files
Earlier, I briefly mentioned that ECOM uses resource files to build up a ”registry” of all the ECOM plug-ins installed. Let’s now examine the detail. Each plug-in DLL must supply a compiled .rss resource, of a particular format, in order to register with the ECOM framework. Both the plug-in DLL and the resource file should be built into the
\system\libs\plugins\ directory.
The resource file lists all the plug-in properties, including information about each concrete class implementing an ECOM interface. ECOM associates the resource file with the plug-in DLL by name – it should be named using the hexadecimal value of the third UID of the plug-in DLL (for the example below, this is 10008EE1.rss).
RESOURCE FILES |
243 |
//10008EE1.RSS
//Registry file for the CCryptoInterface Implementation Collection
#include "RegistryInfo.rh" // Defines the resource structures
RESOURCE REGISTRY_INFO theInfo
{
// UID3 for the plug-in; must match the name of this file dll_uid = 0x10008EE1;
interfaces = // interfaces info
{
INTERFACE_INFO
{
interface_uid = 0x10008EE0; // UID of CCryptoInterface
implementations =
{
IMPLEMENTATION_INFO // Info for CHardwareCrypto
{
//Identifies the specific implementation implementation_uid = 0x10008EE4; version_no = 1;
display_name = "Hardware Cryptography";
//Used for cue lookup by the default ECOM resolver default_data = "HW";
opaque_data = ""; },
IMPLEMENTATION_INFO // Info for CSoftwareCrypto
{
//Identifies the specific implementation implementation_uid = 0x10008EE5; version_no = 1;
display_name = "Software Cryptography";
//Used for cue lookup by the default ECOM resolver default_data = "SW";
opaque_data = "";
}
};
}
};
}
A single REGISTRY_INFO structure is used to declare all the implementations of ECOM interfaces (identified by interface_uid) available in the plug-in DLL (identified by dll_uid). The details of each implementation are declared inside separate IMPLEMENTATION_INFO structures. The default ECOM resolver uses the default_data attribute to resolve a cue passed to a factory instantiation function. In addition, it is possible to customize the resolver to use the opaque_data attribute for lookup. Customized resolution can be useful to extend the selection criteria from those of the default ECOM resolver, for example by implementing case-insensitive lookup.
244 |
ECOM |
A customized resolver is actually an implementation of an ECOM interface, CResolver, and it should thus be implemented in an ECOM plug-in DLL and registered with ECOM by supplying a resource file as described above. The interface_uid for CResolver is 0x10009D0 – you should always use this value in the resource file created for your custom resolver.
Such a resolver must implement the pure virtual functions
IdentifyImplementationL() and ListAllL() declared in
CResolver. It should also specify a factory creation function which takes an MPublicRegistry reference parameter; this object gives the resolver access to a list of implementations of a specified interface.
IdentifyImplementationL() should be implemented to identify the most appropriate implementation of a specified interface, according to a cue. It will use the MPublicRegistry object to obtain information about each implementation. ListAllL() must return a list of all implementations which match the cue.
class CCryptoResolver : public CResolver
{
public:
// Factory function
static CCryptoResolver* NewL(MPublicRegistry& aRegistry);
CCryptoResolver();
public: // From CResolver (see resolver.h)
/** Request that the resolver identify the most appropriate interface implementation, returning the UID or KNullUid if no match is found */
TUid IdentifyImplementationL(TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters) const;
/** Return a pointer to an array of all the implementations which satisfy the specified interface */
RImplInfoArray* ListAllL(TUid aInterfaceUid,
const TEComResolverParams& aAdditionalParameters) const;
... // Omitted };
The custom resolver may be used by the interface instantiation functions; for example, the CCryptoInterface::NewL() factory method may be extended to use a custom resolver as follows:
CCryptoInterface* CCryptoInterface::NewL()
{// The implementation instantiated is the first found by resolver
_LIT8(KAny,"*");
TEComResolverParams resolverParams;
resolverParams.SetDataType(KAny());
resolverParams.SetWildcardMatch(ETrue);
EXAMPLE CLIENT CODE |
245 |
// UID of the crypto resolver
const TUid KCustomResolverUid = {0x10008EE6};
TAny* cryptoInterface = REComSession::CreateImplementationL(KCCryptoInterfaceUid, _FOFF(CCryptoInterface,iDtor_ID_Key), NULL, resolverParams, KCustomResolverUid));
return (reinterpret_cast<CCryptoInterface*>(cryptoInterface));
}
The third UID of an ECOM plug-in DLL is used to name its associated compiled resource, which contains its ECOM ”registration” information.
14.6 Example Client Code
So how does a client use an ECOM plug-in? As I have already described, the caller doesn’t need to be aware of the details and simply uses the factory function supplied by the interface, supplying a cue if it is required. ECOM takes care of the details of locating and instantiating the appropriate implementation. An interface client must simply include the header files which define the interface, link against ECOM.lib and use the factory instantiation functions as appropriate:
#include "CryptoInterface.h"
void GetDefaultCryptoL()
{// Get the default implementation of CCryptoInterface CCryptoInterface* crypto = CCryptoInterface::NewL(); CleanupStack::PushL(crypto);
...
CleanupStack::PopAndDestroy(crypto);
}
void GetSpecifiedCryptoL()
{// Use a cue - gets CCryptoInterface implementation which uses // hardware support
_LIT8(KHardware,"HW");
CCryptoInterface* crypto = CCryptoInterface::NewL(KHardware);
CleanupStack::PushL(crypto);
...
CleanupStack::PopAndDestroy(crypto);
}
void GetAllCryptoL()
{
//Get all implementations using CCryptoInterface::ListImplementationsL() RImplInfoPtrArray infoArray;