The .NET Compact Framework

In this section, we'll explore the architecture, core functionality, and UI support in the Compact Framework and compare it with the desktop Framework. In this way, developers and technical managers can quickly get a feel for the technology involved in developing mobile applications using the Compact Framework.

Architecture

You may recall that one of the design goals of the Compact Framework was to create a "portable (and small) subset of the desktop Framework, targeting multiple platforms." To support this goal Microsoft created the architecture shown in Figure 2-3. In this section we'll walk through the components of that architecture from the bottom up to describe how each contributes to this goal.

Figure 2-3. The Compact Framework Architecture.

graphics/02fig03.gif

The size of the Compact Framework installed on the device varies from device to device. Generally, it ranges from 1.7MB to 2.6MB and can be installed in RAM, ROM, or FlashROM. As you might expect, the initial release is installed in RAM so that it is immediately available to all devices. However, in the future, expect OEMs to offer FlashROM upgrades that include the Compact Framework for devices like the Pocket PC 2002 (where FlashROM is already present). Future devices (i.e., Pocket PC 2003 devices) will likely ship with the Compact Framework in ROM.

Host Operating System

Obviously, at the lowest level, the code written for the Compact Framework must be executed on a host operating system such as Windows CE. At this time, the Compact Framework will run on Windows CE 3.0 and Windows CE .NET 4.1, although as we'll see, the architecture in Figure 2-3, particularly through the inclusion of the PAL and the NSLs, lends itself to portability to other host operating systems as well.

PAL

The PAL is the primary component that makes platform portability possible. Essentially, the PAL contains a variety of subsystems that expose the functionality of the underlying operating system and hardware in a consistent set of APIs to the NSL and EE, as shown in Figure 2-3. For example, the PAL includes interfaces for device drivers, a system memory manager, interrupts and timers, multimedia, and I/O ports, among others. All of these subsystems must be fully implemented on the target device.

As a result, in order to port the Compact Framework between devices, OEMs must rewrite the PAL to make native calls on the target operating system and to the hardware. Of course, depending on the features of the device and what its native operating system supports, the functions of the PAL may or may not map to the operating system in a straightforward manner. For example, the PAL was designed with Windows CE and the Pocket PC in mind, and so, many of its APIs simply map directly to APIs exposed by Windows CE.

In summary, you can think of the PAL as the equivalent of a device driver used by the Compact Framework that abstracts and drives the underlying operating system and its hardware.

Native Support Libraries

Because not all devices support the same set of services, the Compact Framework also includes a set of NSLs that implement features that the Compact Framework requires, including file system operations, heap management, globalization, cryptography, and graphical user interface (GUI) manipulation.

These services then make calls into the PAL to perform their operations and are in turn called by the EE, as shown in Figure 2-3. A typical example is the GUI support implemented as an NSL that is then exposed by the classes in the System.Windows.Forms namespace in the Compact Framework class library.[10] As you might expect, the NSLs can also be called by other native code running on the device, thereby substantially increasing the feature set available to other unmanaged applications as well.

[10] This NSL is implemented by the file Netcfagl1_0.dll installed on the device. On Windows CE, this Advanced Graphics Library interfaces with the Graphics, Windowing, and Event Subsystem (GWES), as shown in Figure 1-2.

The addition of NSLs levels the playing field for devices that do not support these core features, making the Compact Framework capable of running on a wide variety of devices.

Because the NSLs make use of the PAL, OEMs do not have to port the code to implement them as they do with the PAL. The NSLs can simply be compiled for the target platform.

EE

The EE in Figure 2-3 provides essentially the same set of services that the common language runtime does for desktop and server applications shown in Figure 2-2 by managing the execution of a .NET application. However, because it performs these functions in an environment where resources are scarce (on devices with less memory and a slower CPU), the EE was designed from the ground up with these constraints in mind and, as a result, performs some of them differently. Even so, the core technology, like the desktop Framework, still conforms to the ECMA-335 specification. The EE was written in C and is implemented in two DLLs, Mscoree.dll (the stub) and Mscoree1_0.dll (the bulk of the EE), which, like the NSLs, are compiled for the target platform per CPU and operating system.[11]

[11] Typically, the EE ranges in size from 400K to 500K depending on the operating system and CPU architecture.

To get a better understanding of how the EE does its work, the following list explicates some of the core functionality in the order it is encountered during the execution of a managed application:

  • Class Loader: As with the desktop Framework, code executed by the Compact Framework must have been previously compiled into MSIL instructions and placed in an assembly (a PE file) on the device. The compilation occurs on the developer's PC using SDP, as explained later in this chapter. As the name implies, the job of the Class Loader is to locate and load the assemblies required to execute an application. However, before the Class Loader can do its work, the application must be activated at the operating system level, which occurs when the Compact Framework application is executed by the host operating system.[12] At that time, a process is created, and Mscoree.dll (and subsequently Mscoree1_0.dll) is loaded by the operating system[13] into the process. At this point an Application Domain is created, and the EE takes over execution of the application within the domain running in the operating system process.[14] As with the desktop Framework, Application Domains serve as a means to isolate Compact Framework applications running within the same process and can therefore be thought of as "lightweight processes." Once the EE has been invoked, the Class Loader can then do its job by loading the set of assemblies, with the required versions, necessary to execute the application. It does this by inspecting the metadata in the assembly that includes the information about dependent assemblies. The list of required assemblies can (and often does) include both custom assemblies that developers create and assemblies that ship with the Compact Framework, such as System.Windows.Forms.[15] The Compact Framework Class Loader uses a simpler scheme for binding than does the desktop Framework. In short, the Class Loader looks at the major and minor version number (of the four-part naming scheme) of the referenced assembly and will load it as long as they are the same as the version the calling assembly was compiled with. The Class Loader also supports side-by-side execution, which means that a Compact Framework application always runs on the version of the EE with which it was compiled.

    [12] For example, the Windows CE PE Loader.

    [13] For example, using the Windows CE LoadLibrary API.

    [14] The APIs required to create and manage Application Domains within a custom host process are not documented in the initial release of the Compact Framework. It is also not possible to load assemblies into a domain-neutral code area for use by multiple Application Domains.

    [15] The Compact Framework, however, does not support multifile assemblies as the desktop Framework does.

  • Type Checker: After the Class Loader has loaded the required assemblies, the Type Checker is invoked to determine if the MSIL code is safe to execute. In this way, the Compact Framework provides the same verifiably type-safe execution as the desktop Framework, for example, by making sure that there are no uninitialized variables, that parameters match their types, that there are no unsafe casts, that the array indexes are within bounds, and that pointers are not out of bounds.

  • JIT compiler: Once the Type Checker has verified the code and completed successfully, the MSIL code can be JIT-compiled to native instructions on the CPU. And, as with the desktop Framework, the compilation occurs on a method-by-method basis as each method is invoked. However, the Compact Framework JIT compiler must be especially sensitive to the resource constraints of the device and so uses a code-pitching technique to free blocks of memory when resources are low. This works by marking sections of JIT-compiled code that were recently executed and then allowing the least recently executed blocks to be reclaimed in a process similar to that used by a GC. As with most things, this too is a trade-off because MSIL code must be recompiled if it is subsequently executed. However, using this technique typically ensures that the core working set of the application stays natively compiled in memory. It should be noted that the Compact Framework does not support compiling an entire application to native code at install time using the native code generation (Ngen.exe) command-line utility as the desktop Framework does.

  • Thread support: As a Compact Framework application runs, it can gain access to underlying operating system threads through the Thread class in the System.Threading namespace. This allows Compact Framework developers to create applications that appear more responsive by offloading work (for example, a call to an XML Web Service) to background threads, while waiting for stylus input from the user.[16] It should be noted that the Compact Framework was designed to coexist peacefully with the host operating system and so relies on native operating system threads and synchronization primitives. As a result, operating system scheduling priorities also apply to Compact Framework applications, and threads produced by the EE can coexist with native threads in the same process. In addition, the Compact Framework includes a thread pool (System.Threading.ThreadPool) that allows a developer to queue a method for execution on one of a number of background worker threads controlled by the EE. When a thread in the pool is free, the method will execute and, when finished, can notify the main thread through a callback.

    [16] The Application Domain, in which a multithreaded Compact Framework application runs, will exist until all of the created threads have exited.

  • Exception handling: During the execution of an application, unforeseen events sometimes transpire. To handle these gracefully, the Compact Framework supports structured exception handling (SEH), as does the desktop Framework. This allows developers to use Try-Catch semantics in their code and to test for specific types of exceptions thrown by the application. And, as with its desktop cousin, the EE of the Compact Framework is optimized for the nonexceptional case, and so throwing exceptions should be reserved for true exceptions and not simply to signal a normal occurrence. The key difference in exception handling in the Compact Framework is that the error strings are actually stored in a separate assembly, System.SR.dll. This is due to the resource constraints of devices and allows the developer optionally to install this assembly on the device. If present, the EE will load it and display the appropriate message, and, if not, a default message will be displayed.

  • GC: One of the most discussed features of the common language runtime is the GC, which is responsible for managing memory by collecting and deallocating objects that are no longer used. As you might expect, the design of the GC is especially important in the constrained environment of a mobile device, and, for that reason, it differs from the GC implemented in the desktop Framework. The GC in the Compact Framework consists of an allocator and a collector. The allocator is responsible for managing the object pools that provide storage for the instance data associated with an object, while the collector implements the GC algorithm. At a high level, the collector runs on a background thread when resources are low and, while working, freezes all other active threads at a safe point. It then finds all reachable objects by traversing the various thread call stacks and global variables and marks them. The collector then frees all the unmarked objects and executes their finalizers.[17] Finally, the object pools are compacted, which returns free memory to the global heap. This approach is referred to as a "mark-and-sweep approach" and does not use the concepts of generations or implement a finalization queue, as does the more complex GC of the desktop Framework.

    [17] Finalizers are the destructors associated with an instance of a class. Destructors are often used explicitly to free resources but are not required.

In addition to the features discussed here, the EE also provides other services that will be discussed in more detail in the following section, including exception handling, native code interoperation, and debugging.

Class Libraries

In order to create a robust programming environment for devices, the Compact Framework ships with a set of class libraries in assemblies organized into hierarchical namespaces similar to those found in the desktop Framework described previously and shown in Figure 2-3. However, there are four major differences between the class libraries shipped with the desktop Framework and those included in the Compact Framework.

graphics/key point_icon.gif

First, the Compact Framework libraries can rightly be thought of as a subset of the desktop libraries and, in fact, include just over 1,700 types, or roughly 25% of the desktop Framework. The diagram shown in Figure 2-4 highlights the namespaces supported in the Compact Framework as a subset of those in the desktop Framework, where there is overlap. As a result, developers familiar with the desktop Framework should not expect all the functionality they are accustomed to. The most important additional omissions are listed here:

  • ASP.NET: Because the Compact Framework is designed to support applications that execute on the device, it does not include any support for building Web pages hosted on a Web server running on the device. This means that the classes of the System.Web namespace familiar to ASP.NET developers are not found in the Compact Framework. To write Web applications that can be accessed by a mobile device, use the ASP.NET Mobile Controls as discussed in Chapter 1.

  • COM Interop: Because the Windows CE operating system and the eVC++ tool support creating COM components and ActiveX controls, it would be nice if the Compact Framework supported the same COM Interop functionality (complete with COM callable wrappers and interop assemblies) as does the desktop Framework. Unfortunately, COM Interop did not make it into the initial release of the Compact Framework. However, it is possible to create a DLL wrapper for a COM component using eVC++ and then to call the wrapper using the Platform Invoke (PInvoke) feature of the Compact Framework, which allows native APIs to be called. Examples of using PInvoke can be found throughout this book, but especially in Chapter 11.

  • OleDb access: The Compact Framework omits the System.Data.OleDb namespace and so does not support the ability to make calls directly to a database using the OleDb .NET Data Provider. However, the remote data access (RDA) features of SQL Server CE do support pulling data down from a SQL Server that can act as a repository for data from other data sources, as discussed in Chapter 7.

  • Generic serialization: The desktop Framework supports binary and SOAP serialization of any object through the use of the Serializable attribute, the ISerializable interface, and the XmlSerializer class in the System.Xml.Serialization namespace. This functionality is not supported in the Compact Framework. However, the Compact Framework does support serializing objects to XML for use in XML Web Services and serializing DataSet objects to XML as discussed in Chapter 3.

  • Asynchronous delegates: Delegates in both the desktop Framework and Compact Framework can be thought of as object-oriented function pointers. They are used to encapsulate the signature and address of a method to invoke at runtime. While delegates can be called synchronously, they cannot be invoked asynchronously and passed a call back method in the Compact Framework. However, it should be noted that asynchronous operations are supported for some of the networking functionality found in the System.Net namespace and when calling XML Web Services described in Chapter 4. In other cases, direct manipulation of threads or the use of a thread pool is required as described in Chapter 3.

  • Application configuration files: The desktop Framework includes a ConfigurationSettings class in the System.Configuration namespace. This class is used to read application settings from an XML file associated with the application and called appname.exe.config. The Compact Framework does not support this class, but developers can write their own using the classes in the System.Xml namespace discussed in Chapter 3. An example class of this type can be found in the book by Wigley and Wheelright referenced in the "Related Reading" section at the end of the chapter.

  • .NET remoting: In the desktop Framework, it is possible to create applications that communicate with each other across application domains using classes in the System.Runtime.Remoting namespace. This technique allows for data and objects serialized to SOAP or a binary format to be transmitted using TCP or HTTP.[18] This functionality is not supported (in part because generic serialization is not supported) in the Compact Framework, where, instead, XML Web Services and the Infrared Data Association (IrDA) protocol can be used, as discussed in Chapter 4.

    [18] See Chapter 8 of Building Distributed Applications with Visual Basic .NET for an overview of .NET Remoting.

  • Reflection emit: Although the Compact Framework does support runtime type inspection using the System.Reflection namespace,[19] it does not support the ability to emit dynamically created MSIL into an assembly for execution.

    [19] For example, to create objects dynamically at runtime using the Activator.CreateInstance method.

  • Printing: Although the Compact Framework does support graphics and drawing through a subset of the GDI+ functionality of the desktop Framework, it does not support printing through the System.Drawing.Printing namespace.[20]

    [20] The most popular third-party printing software is the PrinterCE SDK from Field Software Products (www.fieldsoftware.com). Look for a Compact Framework version of their SDK in the near future.

  • XPath/XSLT: Support for XML is included in the Compact Framework and allows developers to read and write XML documents using the XmlDocument, XmlReader, and XmlWriter classes, as discussed in Chapter 3. However, it does not support executing XPath queries or performing XML Stylesheet Language (XSL) transformations.

  • Server-side programming models: As you would expect, in addition to those shown in Figure 2-4, the Compact Framework also does not support the server-side programming models, including System.EnterpriseServices (COM+),[21] System.Management (Windows Management Instrumentation, or WMI),[22] and System.Messaging (Microsoft Message Queue Server, or MSMQ).[23]

    [21] Or Component Services that enable .NET components to access services such as distributed transactions, object pooling, and loosely coupled events.

    [22] Used to monitor services on a Windows machine.

    [23] Used to create asynchronous message-based applications.

  • Multimodule assemblies: The desktop Framework supports the ability to deploy an assembly as a collection of files. This is useful for creating assemblies authored with multiple languages. This feature is not supported in the Compact Framework where a single file (.exe or .dll) represents the entire assembly.

Figure 2-4. The Compact Framework Class Libraries.

graphics/02fig04.gif

The second major difference between the desktop class libraries and those included in the Compact Framework is how they are factored into assemblies. Simply put, in the Compact Framework the 14 assemblies that comprise the class libraries are more granular than those found in the desktop Framework. For example, in the desktop Framework, the classes of the System.Data.SqlClient namespace used to access SQL Server are included in the System.Data.dll assembly, whereas in the Compact Framework they are factored into their own assembly. In this way, the Compact Framework can support a smaller footprint if applications installed on the device do not require some of the Compact Framework class libraries.

The third major difference is that the Compact Framework supports two additional namespaces (each shipped in its own assembly) that expose functionality particular to smart devices, as shown in Table 2-1.

In addition, support for the IrDA protocol has been included in the Compact Framework and exposed in the six classes found in the System.Net.Sockets and the System.Net namespaces, as covered in Chapter 4.

Finally, the Compact Framework supports a subset of the core types supported in the desktop Framework. These are often referred to as the Common Type System (CTS) types because they are the foundational types. Table 2-2 presents the CTS types found in the desktop Framework and their support in the Compact Framework. Note that in each case, some of the overloaded methods and properties found in the desktop Framework are not supported in the Compact Framework.

Table 2-1. Additional Compact Framework Namespaces

Namespace

Use

Microsoft.WindowsCE.Forms

Contains the InputPanel, MessageWindow, and Message classes. The InputPanel class is used to control the soft input panel (SIP) on a Pocket PC, the primary means of entering data. The MessageWindow and Message classes are used primarily to communicate with unmanaged (or managed) applications using standard Windows messaging.

System.Data.SqlServerCE

Contains approximately 30 classes used to communicate with SQL Server CE if installed on the device.

Table 2-2. Types Supported in Desktop and Compact Framework

Type

Description

System.Object

Root object of the CTS hierarchy from which all types derive

System.Delegate

Used to store the address of a method to invoke; basis of event handling; asynch is not supported

System.Array

Base type for all arrays

System.Boolean

True or false, 8-bit unsigned

System.Byte

Unsigned 8-bit integer

System.Char

A Unicode 16-bit character

System.DateTime

Stores date and time information from 1/1/0100 12:00:00AM in 100-ns tick intervals

System.Decimal

Represents positive and negative values with up to 28 significant digits

System.Double

IEEE 64-bit float

System.Int16

Signed 16-bit integer

System.Int32

Signed 32-bit integer

System.Int64

Signed 64-bit integer

System.IntPtr

Signed integer, native size

System.SByte

Signed 8-bit integer

System.Single

IEEE 32-bit float

System.TimeSpan

Represents a time interval

System.UInt16

Unsigned 16-bit integer

System.UInt32

Unsigned 32-bit integer

System.UInt64

Unsigned 64-bit integer

Developers familiar with eVB will no doubt note the vast difference between the strongly typed Compact Framework environment and eVB, where all variables are of the type Variant. This not only makes source code easier to read and avoids runtime errors, it also saves memory because in eVB each variable consumes a minimum of 16 bytes, whereas the types in the Compact Framework consume fewer; for example, System.Int32 is four bytes.

In all, the Compact Framework class libraries provide a wealth of functionality that allows developers to create robust applications for devices.

Portability

All development teams would like to be able to leverage the code they write by reusing it in as many scenarios as possible, and the developers of Compact Framework code are no exception. There are four key scenarios where portability must be addressed: device to device, desktop to device; device to desktop, and eMbedded Visual Tools to Compact Framework.

Device to Device

Although devices targeted for the Compact Framework span a variety of hardware manufacturers and processors, the architecture illustrated in Figure 2-3 allows Compact Framework applications to be moved between devices without recompilation. This is the case for two reasons. First, the MSIL code placed in the assembly by the compiler is machine-independent, allowing the JIT compiler of the EE to compile to native instructions for execution. In addition, the system assemblies contain the same set of classes, are factored identically, and are versioned identically on all platforms. This allows your development team to create a single binary to support multiple devices running on multiple CPUs (x86, SH3, ARM, MIPS), all of which are loaded with the Compact Framework.

In this scenario the caveat is that Compact Framework applications also support several platforms (Pocket PC 2000, 2002, Windows CE .NET 4.1) and any particular application may rely on platform-specific features, for example, the InputPanel class to control the SIP on Pocket PC. In these cases, the application would need to be modified to remove the unsupported features and recompiled before executing it on another platform. Not doing so may result in exceptions being thrown or unpredictable behavior. For example, if a Compact Framework application targeted for Windows CE .NET 4.1 attempts to display the SIP using the InputPanel class, the EE throws a NotSupportedException. To work around these issues, it is possible to determine programmatically the platform using a native API call as discussed in Chapter 11.

Desktop to Device

graphics/key point_icon.gif

Porting an application from the desktop Framework to the Compact Framework, however, is not so simple. This is the case because of the differences between the class libraries in the Compact Framework as discussed in the previous section. Fortunately, because the design philosophy of the Compact Framework team at Microsoft was to provide both familiarity and full compatibility in the developer experience, the core programming model, language support, and file formats and protocols, porting an application from the desktop Framework to the Compact Framework is largely a matter of removing functionality not supported because of the subset nature of the Compact Framework and recompiling the application in an SDP in VS .NET. You or your team will of course also need to redesign the UI to work within the smaller screen size and other constraints of the device, as well as adding any platform-specific features.

Even in the simplest case, if an assembly is created in the desktop Framework and then referenced in an SDP in VS .NET, both a warning dialog and compiler errors will result, indicating that the mscorlib assembly referenced by the desktop Framework assembly differs from that referenced by the SDP. For this reason, it is recommended that all desktop Framework assemblies for use on the Compact Framework first be recompiled.

Interestingly, it is possible to load a desktop Framework assembly on the Compact Framework using the Assembly class of the System.Reflection namespace. However, because the Compact Framework does not support late binding, invoking the methods requires more runtime type inspection (using the Type and MethodInfo classes) and is therefore unwieldy at best.

Device to Desktop

This scenario has much in common with the previous one. Although the Compact Framework uses the same standard PE file format, header, and metadata as that used by the desktop Framework, applications created for the Compact Framework will be designed for the constraints of the device and will use platform-specific assemblies (Microsoft.WindowsCE.Forms) and functionality (IrDA). For this reason, most Compact Framework code will need to be modified and recompiled in a desktop Framework project for execution on the desktop.

graphics/key point_icon.gif

However, it is possible to create assemblies for the Compact Framework that the desktop Framework will load and execute without recompilation. This works because the desktop Framework supports assembly retargeting for the assemblies that have counterparts between the two systems.[24] In other words, even though a Compact Framework application references the Compact Framework?specific System.Data.dll assembly (that has its own strong name and is a subset of the desktop Framework version), at runtime the common language runtime can retarget this reference to the full desktop Framework version and bind accordingly. In future versions of the desktop Framework, look for the addition of a retargeting publisher policy that can be applied to allow administrators to control how and when retargeting is applied.

[24] This works through the inclusion of the retargetable modifier added to the MSIL code by the compiler.

Of course, because of the UI, memory, and other constraints of devices, this sort of binary compatibility will likely be useful only for developing custom code libraries (business logic, sorting, searching, data access, string, and file manipulation, for example).

eMbedded Visual Tools to Compact Framework

As mentioned in Chapter 1, before the availability of the Compact Framework, developers creating applications for smart devices used eVC++ and eVB that together are referred to as the eMbedded Visual Tools. However, because the Compact Framework uses an entirely new EE, class libraries, along with a new IDE and language syntax, porting an application written in eVB will require a substantial rewrite. And unlike in the desktop Framework, there is no tool available in VS .NET to assist in the upgrade process. For this reason, development teams will likely port only eVB applications to Compact Framework when adding significant additional functionality, for example, by adding support for XML Web Services.