/ Language: English / Genre:ref_guide,comp_osnet,comp_soft, / Series: MCTS

Microsoft Windows Embedded CE 6.0 Exam Preparation Kit

Nicolas Besson


Microsoft Windows Embedded CE 6.0 Exam Preparation Kit

Foreword

It seems like yesterday that we released Windows CE 1.0 to the market, although 12 successful years have passed and many things have changed. New technologies have emerged, while others have vanished; and we continue to push forward with our partners to take full advantage of new hardware and software innovations. Windows Embedded CE continues to evolve, yet remains a small-footprint, real-time, embedded operating system that runs on multiple processor architectures and an amazing array of devices, including robots, portable ultrasound imaging systems, industrial controllers, remote sensor and alarm systems, point-of-sale front-ends, media streamers, game consoles, thin clients, and even devices most of us would never associate with a Microsoft operating system. Perhaps one day Windows Embedded CE will run on devices on the moon. It would not come as a surprise. Windows Embedded CE can be everywhere that computer devices help to make life easier and fun.

Right from the start, we have focused on the needs of professional embedded developers by creating a comprehensive suite of development tools and by supporting Windows programming interfaces and frameworks. We have integrated the Windows Embedded CE development tools with Visual Studio 2005 to provide developers with the freedom to customize the operating system and build the applications for the operating system. Today, Windows Embedded CE 6.0 supports x86, ARM, MIPS and SH4 processors out of the box, and includes approximately 700 selectable operating system components. CE provides the tools needed to configure, build, download, debug, and test operating system images and applications, ships with source code for the kernel, device drivers, and other features, and gives application developers the flexibility to create Win32, MFC, or ATL native code applications or managed applications based on the .NET Compact Framework. As part of the Microsoft Shared Source Initiative, we ship more than 2.5 million lines of CE source code, which gives developers the ability to view, modify, rebuild, and release the modified source. And recently we launched a "Spark your Imagination" program to give hobbyist developers access to hardware and CE software development tools at low costs.

You can find plenty of information about the CE operating system, development tools, and concepts in this preparation kit for Microsoft Certified Technology Specialist (MCTS) Exam 70-571 "Microsoft Windows Embedded CE 6.0, Developing" released in May 2008. We are very excited about Exam 70-571. It signifies another important milestone in the Windows Embedded CE success story. Now, for the first time, embedded developers have the ability to assess and demonstrate their skills regarding the development of embedded solutions based on Windows Embedded technologies, and they can gain recognition for their knowledge and proficiency. Anybody with a passion for CE 6.0 should consider taking the exam. We hope that this book accelerates your preparation just as Windows Embedded CE 6.0 accelerates your development processes. Best wishes from all of us here at the Microsoft development team!

Mike Hall Windows Embedded Architect Microsoft Corporation

Introduction

Welcome to the Microsoft Windows Embedded CE 6.0 Exam Preparation Kit. The purpose of this preparation kit is to help Windows Embedded CE developers prepare for the Microsoft Certified Technology Specialist (MCTS) Windows Embedded CE 6.0 Application Development certification exam.

By using this preparation kit, you can maximize your performance on the following exam objectives:

■ Customize the operating system design.

■ Clone Windows Embedded CE components and manage catalog items.

■ Generate a Software Development Kit (SDK).

■ Build a run-time image and analyze build results.

■ Deploy, monitor, and optimize a run-time image.

■ Develop multi-threaded system applications.

■ Implement exception handling.

■ Support power management in applications, device drivers, and in the OEM adaptation layer (OAL).

■ Configure a Board Support Package (BSP), including customizations to boot loader and memory mappings.

■ Develop full-featured stream interface drivers.

■ Implement Interrupt Service Routines (ISRs) and Interrupt Service Threads (ISTs) and marshal data between kernel-mode and user-mode components.

■ Debug kernel-mode and user-mode components to eliminate software-related errors.

■ Use the Windows Embedded CE Test Kit (CETK) to perform standard and user-defined tests on a development workstation and on a target device.

■ Develop Tux extension components to include custom device drivers in CETK-based tests.

Intended Audience

This Exam Preparation Kit is for system developers with a basic level of knowledge about operating system design, programming system components, and debugging on the Windows Embedded CE platform.

Specifically, this Preparation Kit is designed for readers with the following skills:

■ Basic knowledge of Windows and Windows Embedded CE development and development

■ At least two years of experience with C/C++ programming and the Win32 Application Programming Interface (API).

■ Familiarity with Microsoft Visual Studio 2005 and Platform Builder for Windows Embedded CE 6.0.

■ Basic debugging skills using standard Windows debugging tools.

MORE INFO

Audience profile for Exam 70-571

For information about prerequisites to pass the certification exam, see the Audience Profile section in the Preparation Guide for Exam 70-571 at http://www.microsoft.com/learning/exams/70-571.mspx.

Features of This Book

Each chapter opens with a list of exam objectives covered in the chapter and a "Before You Begin" section, which prepares you for completing the chapter. The chapters are then divided into lessons. Each lesson begins with a list of objectives and states an estimated lesson time. The lesson content is subdivided further according to topics and lesson objectives.

Each chapter ends with hands-on procedures and a short summary of all chapter lessons. This is followed by a brief check of key terms and suggested practices which test your knowledge of the chapter material and help you successfully master the exam objectives presented in the chapter.

► The hands-on examples give you an opportunity to demonstrate a particular concept or skill and test what you have learned in the chapter lessons. All hands-on examples include step-by-step procedures that are identified with a bullet symbol like the one to the left of this paragraph. To help you successfully master the presented procedures, worksheets with detailed step-by-step instructions for each lab are also included in the companion material for this book.

To complete the hands-on procedures, you must have a development computer with Microsoft Windows XP or Microsoft Windows Vista, Visual Studio 2005 Service Pack 1, and Platform Builder for Windows Embedded CE 6.0 installed.

Hardware Requirements

The development computer must have the following minimum configuration, with all hardware on the Windows XP or Windows Vista Hardware Compatibility List:

■ 1 GHz 32-bit (x86) or 64-bit (x64) processor or faster.

■ 1 gigabyte (GB) of RAM.

■ 40 GB hard drive with at least 20 GB of available disk space for Visual Studio 2005 and Platform Builder.

■ DVD-ROM drive.

■ Microsoft Mouse or compatible pointing device.

■ Paging file set to twice the amount of RAM or larger.

■ VGA-compatible display.

Software Requirements

The following software is required to complete the procedures in this course:

■ Microsoft Windows XP SP2 or Windows Vista.

■ Microsoft Visual Studio 2005 Professional Edition.

■ Microsoft Windows Embedded CE 6.0.

■ Microsoft Visual Studio 2005 Professional Edition SP1.

■ Microsoft Windows Embedded CE 6.0 SP1.

■ Microsoft Windows Embedded CE 6.0 R2.

NOTE

Trial versions of Visual Studio 2005 and Windows Embedded CE 6.0

Installation guidelines and evaluation versions of Visual Studio 2005 and Windows Embedded CE 6.0 are available on the Microsoft Website, at http://www.microsoft.com/windows/embedded/products/windowsce/getting-started.mspx.

Notational Conventions

■ Characters or commands that you type appear in bold lowercase type.

■ <Angle brackets> in syntax statements indicate placeholders for variable information.

■ Italic is used for book titles and Web addresses.

■ Names of files and folders appear in Title Caps, except when you are to type them directly. Unless otherwise indicated, you can use all lowercase letters when you type a file name in a dialog box or at a command prompt.

■ File name extensions appear in all lowercase.

■ Acronyms appear in all uppercase.

■ Monospace type represents code samples, examples of screen text, or entries that you might type at a command prompt or in initialization files.

■ Square brackets [ ] are used in syntax statements to enclose optional items. For example, [filename] in command syntax indicates that you can choose to type a file name with the command. Type only the information within the brackets, not the brackets themselves.

■ Braces { } are used in syntax statements to enclose required items. Type only the information within the braces, not the braces themselves.

Keyboard Conventions

■ A plus sign (+) between two key names means that you must press those keys at the same time. For example, "Press ALT+TAB" means that you hold down ALT while you press TAB.

■ A comma (,) between two or more key names means that you must press each of the keys consecutively, not together. For example, "Press ALT, F, X" means that you press and release each key in sequence. "Press ALT+W, L" means that you first press ALT and W together, and then release them and press L.

■ You can choose menu commands with the keyboard. Press the ALT key to activate the menu bar, and then sequentially press the keys that correspond to the highlighted or underlined letter of the menu name and the command name. For some commands, you can also press a key combination listed in the menu.

■ You can select or clear check boxes or option buttons in dialog boxes with the keyboard. Press the ALT key, and then press the key that corresponds to the underlined letter of the option name. Or you can press TAB until the option is highlighted, and then press the spacebar to select or clear the check box or option button.

■ You can cancel the display of a dialog box by pressing the ESC key.

Notes

Several types of Notes appear throughout the lessons.

■ Notes marked Tip contain explanations of possible results or alternative methods.

■ Notes marked Important contain information that is essential to completing a task.

■ Notes marked Note contain supplemental information.

■ Notes marked Caution contain warnings about possible loss of data.

■ Notes marked Exam Tip contain helpful hints about exam specifics and objectives.

About the Companion CD-ROM

The Companion CD contains a variety of informational aids that may be used throughout this book. This includes worksheets with detailed step-by-step instructions and source code used in hands-on exercises, as well as complimentary technical information and articles from the Microsoft developers.

An electronic version (eBook) of this book is included with a variety of viewing options available. The Companion CD also contains a complete set of post-press files for this official self-paced study guide to produce a printed book. The post-press files are in Portable Document Format (PDF) and have the required crop marks for professional printing and binding.

Microsoft Certified Professional Program

The Microsoft Certified Professional (MCP) program provides the best method to prove your command of current Microsoft products and technologies. The exams and corresponding certifications are developed to validate your mastery of critical competencies as you design and develop, or implement and support, solutions with Microsoft products and technologies. Computer professionals who become Microsoft certified are recognized as experts and are sought after industry-wide. Certification brings a variety of benefits to the individual, employers, and organizations.

MORE INFO

All the Microsoft certifications

For a full list of Microsoft certifications, go to http://www.microsoft.com/learning/mcp/default.asp.

Technical Support

Every effort has been made to ensure the accuracy of this book and the contents of the companion CD. If you have comments, questions, or ideas regarding Windows Embedded CE development, contact a Windows Embedded CE specialist through Microsoft Product Support Services (PSS), Microsoft Developer Network (MSDN), or the following blog sites:

■ Nicolas BESSON's Weblog Contact the principal author of the Windows Embedded CE 6.0 Exam Preparation Kit with feedback and subject suggestions for new articles related to those subjects at http://nicolasbesson.blogspot.com.

■ Windows Embedded Blog Read about Mike Halls tricks, tips, and random thoughts on Windows Embedded at http://blogs.msdn.com/mikehall/default.aspx.

■ Windows CE Base Team Blog Get background information about Windows Embedded CE kernel and storage technologies and system tools directly from the Microsoft developers at http://blogs.msdn.com/ce_base/default.aspx.

MORE INFO

Windows Embedded CE product support

For detailed information about all available Windows Embedded CE product support options, go to http://www.microsoft.com/windows/embedded/support/products/default.mspx.

Chapter 1

Customizing the Operating System Design

Whenever you want to deploy Windows® Embedded CE 6.0 R2 on a target device, you must use a run-time image that includes the necessary operating system (OS) components, features, drivers, and configuration settings. The run-time image is the binary representation of the OS design. You can use Microsoft® Platform Builder for Windows Embedded CE 6.0 to create or customize an OS design and generate the corresponding run-time image. For each OS design, you typically create a new devel­opment project in Microsoft® Visual Studio® 2005 and include only the necessary components for your target device and applications. This helps to reduce the footprint of the operating system and to lower hardware requirements. However, in order to generate compact and functional run-time images, you must have an intimate under­standing of Platform Builder, including the user interface (UI), the catalog components, and the specifics of the build procedure. This chapter covers these aspects by explaining how to create an OS design and generate a new Windows Embedded CE run-time image.

Exam objectives in this chapter:

■ Creating and customizing OS designs

■ Configuring Windows Embedded CE subprojects

■ Cloning components

■ Managing catalog items

■ Generating a Software Development Kit (SDK)

Before You Begin

To complete the lessons in this chapter, you must have the following:

■ At least some basic knowledge about Windows Embedded CE software develop­ment.

■ A basic understanding of the directory structure and build process of Platform Builder for Windows Embedded CE 6.0 R2.

■ Familiarity creating binary Windows Embedded CE run-time images and downloading run-time images to target devices.

■ Experience using an SDK to develop applications for Windows Embedded CE.

■ A development computer with Microsoft Visual Studio 2005 Service Pack 1 and Platform Builder for Windows Embedded CE 6.0 installed.

Lesson 1: Creating and Customizing the Operating System Design

You can use Platform Builder in Visual Studio 2005 to create an OS design with as many or as few of the features available in Windows Embedded CE 6.0 R2 as you find necessary for your specific purpose. For example, you can create an OS design for a particular target device, such as a portable multimedia device, and another OS design for a remotely programmable wireless-enabled digital thermostat. These two target devices might rely on the same hardware, but the purposes of the devices are different and so are the corresponding OS design requirements.

After this lesson, you will be able to:

■ Understand the role and specifics of an OS design.

■ Create, customize, and use OS designs.

Estimated lesson time: 30 minutes.

Operating System Design Overview

The OS design defines the components and features contained in a run-time image. Essentially, it corresponds to a Visual Studio with Platform Builder for Windows Embedded CE 6.0 R2 project. The OS design can contain any or all of the following elements:

■ Catalog items, including software components and drivers

■ Additional software components in the form of subprojects

■ Custom registry settings

■ Build options, such as for localization or debugging based on Kernel Independent Transport Layer (KITL)

Additionally, every OS design contains a reference to at least one Board Support Package (BSP) with device drivers, hardware-specific utilities, and an OEM adaptation layer (OAL).

Creating an OS Design

Windows Embedded CE includes an OS Design Wizard, which, as the name suggests, provides a convenient way to create OS designs. To launch it, start Visual Studio 2005 with Platform Builder for Windows Embedded CE 6.0 R2, open the File menu, then point to New, and then click Project to display the New Project dialog box. In this dialog box, under Project Types, select Platform Builder for CE 6.0; and under Visual Studio Installed Templates, select OS Design, enter a name for the OS design in the Name field, and then click OK to start the Windows Embedded CE 6.0 OS Design Wizard.

The OS Design Wizard enables you to select a BSP and a design template with commonly used options and preselected catalog components. Any settings that you specify within the wizard you can also modify later, so don't worry about the individual settings too much for now. Depending on the template that you select on the Design Templates page, the OS Design Wizard might display an additional Design Template Variants page with more specific options related to the selected template. For example, Windows Thin Client, Enterprise Terminal, and Windows Network Projector are all devices that use the Remote Desktop Protocol (RDP) and are therefore variants of the same Thin Client design template. Depending on the selected template and variant, the OS Design Wizard might display additional pages to include specific components in the OS design, such as ActiveSync®, WMV/MPEG-4 video codec, or IPv6.

The OS Design Template

A CE 6.0 OS design template is a subset of the catalog components required to use Windows Embedded CE for a particular purpose. It is not necessary to start from a template when creating a new OS design, although it can save a significant amount of time to do so. It is straightforward to change catalog components later by selecting them in the Catalog Items View.

Choosing an appropriate template can save you development time and effort. For example, you might have to demonstrate the features of a new development board at a trade show. In this case, it is a good idea to start with the PDA Device or Consumer Media Device design template and add the required components and common Windows applications in the OS Design Wizard, such as the .NET Compact Framework 2.0, Internet Explorer®, and WordPad. On the other hand, if you are developing a driver for a Controller Area Network (CAN) controller, it might be better to start with the Small Footprint Device design template and only add what's absolutely necessary to minimize the size of the run-time image and to keep startup times at a minimum.

The OS Design Wizard is flexible and supports custom design templates. Template files are Extensible Markup Language (XML) documents, located in the %_WINCEROOT%\Public\CEBase\Catalog folder. You can start with a copy of an existing Platform Builder Catalog XML (PBCXML) file and modify the PBCXML structures according to your specific needs. Platform Builder automatically enumeates all .pbcxml files in the Catalog folder when you start Visual Studio or refresh the Catalog Items View in Visual Studio.

OS Design Customization with Catalog Components

After completing the OS Design Wizard, it is straightforward to customize the OS design. The catalog is a repository for all the components that can be added to an OS design. It is accessible directly from within the integrated development environment (IDE). Click Catalog Items View in the Solution Explorer window pane. Almost every CE feature is divided into separate user-selectable catalog components, from ActiveSync to TCP/IP. You can select these components directly in the UI. Each catalog item is a reference to all the components necessary to build and integrate a feature into the run-time image.

When you add a catalog item that depends on other catalog items, you implicitly add these items as dependencies to the OS design as well. The Catalog Items View shows these items with a green square in the check box to indicate that they are part of the OS design due to existing dependencies. In contrast, the Catalog Items View shows manually selected items and items included based on a design template with a green check mark.

In the Catalog Items View, you can show all catalog components or enable a filter to display only selected catalog items. Click the downward arrow on the Filter button in the top left corner of the Catalog Items View in Solution Explorer to apply a filter or select the option All Catalog Items in the catalog to display the complete list of catalog items.

Provided that you know the name of the catalog item or the SYSGEN variable a component sets, you might find it more convenient and faster to search for the desired catalog item that you want to add or remove than to look for it manually. To search by item name or SYSGEN variable, type the search term into the text box at the top of the Catalog Items View and click the green arrow next to it.

To analyze the dependencies of a catalog item, you can right-click the item and select Show Dependencies to display the Catalog Item Dependencies window, as illustrated in Figure 1-1. For example, you can use this feature to see the reason for the inclusion of a specific catalog item as a dependency. In CE 6.0 R2, Platform Builder dynamically traverses the catalog to enumerate all components that depend on the selected item as well as all components that this item depends on.

Figure 1-1 Catalog Items View with the search box and Catalog Item Dependencies window

Build Configuration Management

Windows Embedded CE supports multiple build configurations that you can modify separately. The two standard configurations are Release and Debug. These build con­figurations are automatically available when you create an OS design. In the Debug build configuration, the compiler generates debug information, maintains links to the source code in program database (.pdb) files, and, to facilitate debugging and step-by- step code execution, does not optimize the code. Windows Embedded CE run-time images compiled in Debug build configuration are generally 50 percent to 100 percent larger than images compiled by using the Release configuration. To choose a build configuration, open the Build menu in Visual Studio, click Configuration Manager, and then, in the Configuration Manager dialog box, select the desired build configuration under Active Solution Configuration. You can also select the desired build configuration by using the pull-down menu in the Standard toolbar.

OS Design Property Pages

For each build configuration, it is possible to configure a number of project properties, such as the locale, whether or not to include KITL, custom build actions, inclusion of subprojects in the binary image, and custom SYSGEN variables. To access these options, display the Property Pages dialog box by right-clicking the OS design node in Solution Explorer and selecting Properties. The OS design node is the first child object under the Solution top-level node. The caption corresponds to the project name, such as OSDesign1. If Solution Explorer is not visible, open the View menu and click Solution Explorer, and if Solution Explorer currently displays the Catalog Items View or the Class View, click the Solution Explorer tab to display the solution tree.

TIP Setting properties for multiple configurations

In the top left corner of the Property Pages dialog box, you can find a list box to select the build configuration. Among other options, you can select All Configurations or Multiple Configurations. These options are useful if you want to set properties for multiple build configurations at the same time.

Locale Options

In the Property Pages dialog box, under Configuration Properties, you can find the Locale node, which enables you to configure language settings for the Windows Embedded CE image, as illustrated in Figure 1-2. For most languages, the Locale property page covers all requirements to localize the OS design, but some languages, particularly East Asian languages such as Japanese, require additional catalog components. It is also important to note that some catalog components related to internationalization significantly increase the size of the run-time image.

Figure 1-2 Locale property page

The Locale property page enables you to configure the following options for the run-time image:

■Locales Selects the languages that will be available to localize the run-time image. If a selected language has a default ANSI and OEM code page, the code page is automatically added to the OS design, as indicated by a marked corresponding code page entry in the Codepages list

■ Default Locale Defines the default locale for the OS design. The default language is English (United States), which uses the default code page 437 (OEM-United States).

■ Code Pages Specifies the ANSI and OEM code pages that will be available in the OS design.

■ Localize The Build Instructs the build process to use localized string and image resources. Platform Builder performs the localization of the OS design during the make image step of the OS design build process. Localized resource files are integrated inside the binary files for the common components, through res2exe.

■ Strict Localization Checking In The Build Causes the build process to fail if localization resources are missing, rather than just using the resources based on the default locale.

Build Options

Directly under the Locale node in the Property Pages dialog box, you can find the Build Options node, which enables you to control event tracking, debugging, and other build options for the active OS design, as illustrated in Figure 1-3.

Figure 1-3 Build Options property page

The Build Options property page enables you to configure the following options for the run-time image:

■ Buffer Tracked Events In RAM Causes Platform Builder to include OSCapture.exe in the CE image. Also enables logging of operating system events tracked by OSCapture.exe in RAM so they can be flushed to a file and viewed later.

■ Enable Eboot Space In Memory Enables the Ethernet boot loader (EBOOT) to pass data to the Windows Embedded CE OS at start time.

■ Enable Event Tracking During Boot Enables CE event log data collection much earlier during the start process than it would normally be collected otherwise. If you activate this option, event tracking starts before most of the kernel and file system initialization is complete.

■ Enable Hardware-Assisted Debugging Support This is required for some third- party hardware debugging tools (JTAG probes compliant with exdi2).

■ Enable Kernel Debugger Enables the Windows Embedded CE debugger so you can step through the code in the run-time image. Kernel debugging requires KITL to communicate with Platform Builder at run time.

■ Enable KITL Adds KITL to the run-time image. KITL is a useful debugging feature that enables developers to use the kernel debugger, interact with the remote device's file system, registry, and other components, as well as run code. You should not include KITL in the final build of the operating system, because it introduces overhead and wastes time during the start process trying to connect to a host computer.

■ Enable Profiling Enables the kernel profiler in the run-time image, which you can use to collect and view timing and performance data. The kernel profiler is a useful tool for optimizing the performance of Windows Embedded CE on a target device.

■ Flush Tracked Events To Release Directory Adds CeLogFlush.exe to the runtime image, which automatically flushes log data collected by OSCapture.exe to the Celog.clg file in the release directory on the development computer.

■ Run-Time Image Can Be Larger Than 32 MB Enables you to build a larger-than-32-MB image. However, you should not use this option if you want to build an image larger than 64 MB. In this case, you must set an environment variable for the appropriate size (such as IMGRAM128).

■ Use Xcopy Instead Of Links To Populate Release Directory Creates actual copies of the files by using xcopy rather than copylink. Copylink might only create hard links to the files rather than copying them, and it requires the NTFS file system on the development computer.

■ Write Run-Time Image To Flash Memory Instructs EBOOT to write the run-time image to the flash memory of the target device.

Environment Options

The Property Pages dialog box provides an Environment option to configure environ­ment variables that will be used during the build process. You can enable most features in Windows Embedded CE 6.0 R2 by using catalog components, but for some options you need to set a SYSGEN variable so that Platform Builder compiles the necessary code and includes it in the run-time image. Setting environment variables that influence the build process can be helpful when developing a BSP. Environment variables are accessible during the Windows Embedded CE build process from the command line. You can also use environment variables to specify flexible information in the sources, binary image builder (.bib), and registry (.reg) files.

TIP

If it works in Debug but not in Release

If you can build a run-time image in the Debug configuration, but not in the Release configura­tion, display the Property Pages dialog box, select All Configurations from the Configuration list box, and then select the Environment option to set the environment variables for both Debug and Release to the same values.

Advanced OS Design Configurations

This section covers several advanced topics related to OS designs. Specifically, this section explains how to support multiple platforms with the same OS design and discusses the file locations and file types that an OS design typically includes.

Associating an OS Design with Multiple Platforms

When creating a new OS design project by using the OS Design Wizard, you can select one or more BSPs on the Board Support Packages wizard page. By associating an OS design with multiple BSPs, you can generate separate run-time images with identical content for multiple platforms. This is particularly useful in projects that include multiple development teams, especially if the final target hardware is currently not available. For example, you can generate a run-time image for an emulator-based platform so that the application development team can start before the final hardware is available. In terms of OS functionality, the application development team can use the application programming interfaces (APIs) before the final target platform is available. The APIs will be included in the final target because the two run-time images share the same set of components and configuration settings.

You can also add support for multiple platforms to an OS design after the initial creation. All you need to do is select the corresponding check boxes under BSP in the Catalog Items View of Solution Explorer. Selecting a BSP automatically adds the additional platform to the configuration for Release and Debug. You can then switch between the different platforms and build configurations by using Configuration Manager, which is available on the Build menu in Visual Studio. However, it is necessary to run the entire build process, including the time-consuming SYSGEN phase, for each platform individually.

OS Design Paths and Files

In order to use and redistribute your OS designs, you need to know exactly what files constitute an OS design and where they are located on your development computer. By default, you can find the OS designs in the %_WINCEROOT%\OSDesigns directory. Each project corresponds to a separate child directory. OS designs typically correspond to the following file and directory structure:

■ <Solution Name> The parent directory that Visual Studio created for the project.

 ■<Solution Name>.sln The Visual Studio solution (.sln) file to store settings specific to the OS design project. The file name is generally the same as that of your OS design.

 ■ <Solution Name>.suo The Visual Studio solution user options (.suo) file, which contains user-dependent information, such as the state of the Solution Explorer views. The file name is generally the same as your OS design.

 ■ <OS Design Name> The parent directory for the remaining files included in the OS design project.

  • <OS Design Name>.pbxml Your OS design's catalog file. This file contains references to selected catalog components and all the settings related to your OS design.

  • Subprojects This directory includes a separate subfolder for each subproject created as part of the OS design.

  • SDKs This directory includes the Software Development Kits (SDKs) created for the OS design.

  • Reldir This is the release directory. Platform Builder copies the files into this directory during the process of creating the run-time image that can then be downloaded to a target device.

  • WinCE600 This is where files are copied after the Sysgen phase is complete, including resource files and configuration files for the current OS design.

Source Control Software Considerations

Basically, an OS design is a set of configuration files for Platform Builder to generate a Windows Embedded CE run-time image. If you are using source control software to coordinate the work of your development team, you only have to store these configuration files in your source control repository. You do not need to include any files from the CESysgen folder (used during the build process of the run-time image) or Reldir directories, because they can be reconstituted on any workstation with Platform Builder and the BSP installed. Also, omit files ending in .user or .suo because those are user-specific settings for the IDE, and omit .ncb files because these files only contain IntelliSense® data.

Lesson Summary

Platform Builder for Windows Embedded CE 6.0 R2 includes an OS Design Wizard to help you accomplish the basic OS design creation steps quickly and conveniently. You can select one or multiple BSPs to include all hardware-specific device drivers and utilities for your target platform and a design template with possible template variants to include additional catalog items. After completing the OS Design Wizard, you can further customize the OS design. You can exclude unnecessary catalog items, include additional components, and configure project properties such as the Debug and Release build options. In the Debug build configuration, Platform Builder includes debug information in the run-time image, which leads to an increase of 50 percent to 100 percent in comparison to Release builds. However, Debug builds facilitate debugging and step-by-step code execution during the development process. Because you can configure Debug and Release build options separately, you might encounter a situation in which your OS design compiles in the Debug configuration but not in the Release configuration. In this case, it can be helpful to set environment variables in both Debug and Release to the same values. In order to distribute your OS designs, you need to locate the source files, which you can find by default in the %_WINCEROOT%\OSDesigns directory. You can use source control software to coordinate the work of a development team.

Lesson 2: Configuring Windows Embedded CE Subprojects

A subproject is a Visual Studio project inserted into a parent project to include relatively independent components in an overall solution. In our case, the parent project typically corresponds to an OS design. Subprojects can take the following forms:

■ An application (managed or unmanaged).

■ A dynamic-link library (DLL).

■ A static library.

■ An empty project containing only configuration settings.

Subprojects are a good way to include a particular application, device driver, or other code module in an OS design and to maintain this code and the OS design together as one solution.

After this lesson, you will be able to:

■ Create and configure subprojects.

■ Build and use subprojects.

Estimated lesson time: 20 minutes.

Windows Embedded Subprojects Overview

Platform Builder for Windows Embedded CE enables you to create subprojects as part of an OS design. Because subprojects are both modular and easily redistributable, they provide a convenient way to add applications, drivers, or other files to your OS design without manually including them in the build tree as part of the BSP. You can also create subprojects for internal test applications and development tools to make it quick and easy to build these tools and run them on a test device.

Types of Subprojects

Windows Embedded CE supports the following subproject types:

■ Applications Win32® applications with a graphical user interface (GUI), programmed in C or C++.

■ Console applications Win32 applications without a GUI, programmed in C or C++.

■ Dynamic-link library (DLL) Drivers or other code libraries, loaded and used at run time.

■ Static library Code modules in the form of library (.lib) files that you can link to from other subprojects or export as part of the OS design's SDK.

■ TUX dynamic-link library Windows Embedded CE custom test components for the Microsoft Windows CE Test Kit (CETK), as explained in more detail in Chapter 4.

Creating and Adding Subprojects to an OS Design

It is straightforward to create a new subproject or add an existing project as a sub- project to an OS design. For the most part, you can use the Windows Embedded CE Subproject Wizard to accomplish this task, which you can start by right-clicking the Subprojects folder in Solution Explorer and clicking Add New Subproject or Add Existing Subproject. However, an understanding of the details, including the purpose of the various subproject types, the files and settings created by the CE Subproject Wizard, the build process, and customization possibilities for subprojects, is still helpful.

The CE Subproject Wizard creates a subfolder in the OS design folder, which contains all the required configuration files, including:

■ <Name>.pbpxml An XML-based file that contains metadata information about the subproject. This file references the .bib, .reg, sources, and dirs files to build the subproject.

■ <Name>.bib A binary image builder (.bib) file used during the makeimg step in the build process to dictate files to include in the binary image.

■ <Name>.reg A registry file with settings to be included in the final run-time image.

■ Sources A Windows Embedded CE sources file. This is a makefile that contains build options to control the Windows Embedded CE build process.

■ Makefile A file used in association with the sources file in the Windows Embedded CE build process.

To make a copy of a subproject for later use, open your OSDesigns folder (%_WINCEROOT%\OSDesigns), and then open the solution folder for your OS design. The solution folder typically contains the <OS Design Name>.sln file and a folder named according to the OS design. Within this folder, you can find the definition file of the OS design <OS Design Name>.pbxml and several subdirectories. One of these subdirectories should be your Subproject folder, as illustrated in Figure 1-4. It is a good idea to back up this folder. You can then add it to any OS design later by right-clicking the Subprojects container in Solution Explorer and selecting Add Existing Subproject.

Figure 1-4 A subproject folder in an OS design project

Creating Windows Embedded CE Applications and DLLs

To add a Windows Embedded CE application or DLL to an OS design, use the CE Subproject Wizard to create the corresponding subproject. Although you can start with an empty subproject, it is generally more convenient to select a simple console or GUI application template, adding your own code afterward as necessary.

Creating Static Libraries

The CE Subproject Wizard also provides you with an option to create a static library, which you can then link to another subproject or export as part of an SDK. This is helpful for dividing up more sophisticated subprojects or providing more options to application developers who develop solutions for your hardware and firmware. If other subprojects in your OS design rely on a static library, you might have to adjust the build order of the subprojects to use the library efficiently. For example, if a Windows Embedded CE application uses your static library, you should build the library first so that the application build process uses the updated library.

Creating a Subproject to Add Files or Environment Variables to a Run-Time Image

Some subprojects do not necessarily include source code. For example, you can create an empty subproject by using the CE Subproject Wizard, modify the sources file, and set TARGETTYPE=NOTARGET to indicate you do not want to generate binary target files. You can then add files to the run-time image by adding corresponding references to the subproject's .bib file. You can also add registry settings to the subproject's .reg file and you can add SYSGEN variables by editing the subproject's Projsysgen.bat file. Although it is generally faster and more convenient to modify the .reg and .bib files and project properties of the OS design directly, creating a subproject for this purpose can be advantageous if you are planning to reuse customizations in multiple OS designs in the future.

Configuring a Subproject

Visual Studio provides a number of options in the project properties that you can con­figure to customize the build process for subprojects. To configure these settings, display the Property Pages dialog box for your OS design, as explained earlier in this chapter. You can then find the subproject properties under Subproject Image Settings. For each subproject added or created in the current OS design, you can configure the following parameters:

■ Exclude From Build Activating this option excludes the subproject from the build process of the OS design, meaning the build engine does not process the source files that belong to the selected subproject.

■ Exclude From Image Sometimes it can be time-consuming to deploy a run-time image when subprojects change. You have to disconnect from the target platform, rebuild the project, create a new image, reconnect to the target platform, and download the updated image every time a change is made with a subproject. To save time and effort when working on a subproject, you should exclude it from the run-time image by using the Exclude From Image option. In this case, you should also create a way to update the file on the device at run time through KITL, ActiveSync, or any other way you can transfer it to the device.

■ Always Build And Link As Debug By using this option, you build the subproject in Debug build configuration while your current OS design build process uses the Release configuration. In this way, you can debug the subproject code by using the Kernel Debugger while the operating system is running in the Release version (this option will not automatically enable the Kernel Debugger).

NOTE

Exclusion from the run-time image

When you exclude a subproject from the run-time image, you implicitly exclude the subproject's files from the Nk.bin file that is downloaded to the target device. Instead, Windows Embedded CE accesses the subproject's files on an as-needed basis directly from the Release directory over KITL (when KITL is enabled). This means that you can modify the code in a driver or application subproject without having to redeploy the run-time image. You should only need to verify that the remote device is not currently running the code, and then you can rebuild the code and run it again.

Lesson Summary

You can use Windows Embedded CE subprojects to add applications, drivers, DLLs, and static libraries to an OS design, which is useful if you want to manage a complex Windows Embedded CE development project that includes a large number of applications and components. For example, you can include a custom shell application or a device driver for a USB peripheral in the form of a subproject to an OS design, and then have different development teams implement these components. You can also use Windows Embedded CE subprojects to add registry settings, environment variables, or specific files to various OS designs, such as the run-time files for the Core Connectivity (CoreCon) interfaces or a test application. It is possible to back up subprojects individually and add them as existing subprojects to future OS designs.

Lesson 3: Cloning Components

Platform Builder for Windows Embedded CE 6.0 R2 comes with public source code that you can reuse and adapt for various purposes. You can analyze and modify the source code for most of the components included in Windows Embedded CE, from the shell to the serial driver's model device driver (MDD) layer. However, you must not modify the public source code directly. Instead, create a functional copy of the public code so that you can modify the desired components without affecting the original Windows Embedded CE 6.0 R2 code base.

After this lesson, you will be able to:

■ Identify components to clone.

■ Clone an existing component.

Estimated lesson time: 15 minutes.

Public Tree Modification and Component Cloning

Once you have discovered that the code you want to modify resides in the %_WINCEROOT%\Public folder, you might be tempted to modify this code in place and then build it without moving it to another folder first. However, there are a number of reasons not to modify the Public tree:

■ You have to back up the Public directory and maintain separate directory versions for each of your OS design projects, such as WINCE600\PUBLIC_Company1, WINCE600\PUBLIC_Company2, and WINCE600\PUBLIC_Backup.

■ Windows Embedded CE updates, patches provided by quick fix engineering (QFE), and service packs might overwrite or be incompatible with your modifications.

■ Redistributing your code is difficult and error-prone.

■ Worst of all, when you change code in the Public directory tree, you have to spend up to three hours building the operating system. If you already know the CE build process so well that you can rebuild just your particular code without having to rebuild the entire Public folder, you will also already know enough to clone the components.

CAUTION

Public code modifications

Never modify the contents of the Public folder tree.

At a first glance, component cloning might seem like a lot of trouble, but it will save you development time and effort in the long run.

Cloning Public Code

Platform Builder supports instant cloning for some Windows Embedded CE components. To clone these components, right-click the catalog item in the Catalog Items View of Solution Explorer and select Clone Catalog Item. Platform Builder will automatically create a subproject for the component you selected in your OS design with a copy of the code. Before using any other method, such as the Sysgen Capture tool, you should check to see if the desired catalog component supports the Clone Catalog Item option. If it does, then you are two mouse-clicks from completion, as illustrated in Figure 1-5.

Figure 1-5 Cloning a catalog item

If you cannot automatically clone a component by using the IDE, you have to do it manually. However, when you look at the sources file for a .dll or .exe file in the Public directory tree, you see that this file is not the same as the sources file in your platform directory or in a subproject directory. This is because the build process for the Public directory tree differs from the BSP build process. All the build instructions are defined in the makefile file, which is always located in the same directory as the associated sources file. The Public directory tree must support the Sysgen phase, where the required components are linked together relatively.

Converting a component from the Public directory tree to a BSP component or a sub- project requires a number of steps, which are outlined in detail in the Platform Builder for Microsoft Windows Embedded CE product documentation under "Using the Sysgen Capture Tool" at http://msdn2.microsoft.com/en-us/library/aa924385.aspx.

Basically, you need to perform the following steps:

1. Copy the code of the desired Public component into a new directory.

2. Edit the sources file in the new directory and add the line RELEASETYPE=PLATFORM or change the value to PLATFORM if the line already exists so that the build engine places the output from this build into the %_TARGETPLATROOT% folder.

3. Add WINCEOEM=1 to the sources file and build the component in the new directory. This might require further modifications to resolve all build errors.

4. Use the Sysgen Capture tool to create modular sources and dirs files.

5. Rename and use the files created by the Sysgen Capture tool along with a makefile to rebuild the new cloned module.

Once you apply all required modifications to the cloned component, you can modify and redistribute it as easily as any other code.

Lesson Summary

Windows Embedded CE includes a Public directory tree with the source code for most of the CE components, but you should not modify the source code in the Public directory tree directly. Instead, you should clone the items either automatically or manually. Modifying the source code in the Public directory tree causes more trouble now as well as in the future unless you already know the build system very well, in which case you already know all the good reasons why you should use the cloning method.

Lesson 4: Managing Catalog Items

One of Windows Embedded CE's most useful features is its catalog system. By using the catalog, developers can quickly and conveniently customize the Windows Embedded CE firmware to suit their needs. If you create a custom catalog item for each of your components, you can facilitate the installation and configuration of your components. This is a differentiating factor between ad-hoc and professional Windows Embedded CE solutions. For ad-hoc solutions, it might be sufficient to provide basic installation notes and a list of required SYSGEN variables, but professional software should include catalog items with proper values for SYSGEN variables and configuration settings.

After this lesson, you will be able to:

■ Customize the content of the catalog.

■ Add a new component entry to a BSP catalog.

Estimated lesson time: 20 minutes.

Catalog Files Overview

The Windows Embedded CE catalog uses files in Extensible Markup Language (XML) format with a .pbcxml file-name extension. The catalog includes a large number of .pbcxml files, located inside the WINCEROOT directory. Platform Builder automatically enumerates these files to generate the Catalog Items View in Solution Explorer.

Platform Builder parses the following directories to enumerate catalog items:

■ Public catalog files %_WINCEROOT%\Public\<any subdirectory>\Catalog\

■ BSP catalog files %_WINCEROOT%\Platform\<any subdirectory>\Catalog\

■ Third-party catalog files %_WINCEROOT%\3rdParty\<any subdirectory>\Catalog\

■ Common system-on-chip (SOC) files %_WINCEROOT%\Platform\Common\Src\soc\<any subdirectory>\Catalog\

NOTE

3rdParty folder

The 3rdParty folder usually contains standalone applications or source applications that can be included and distributed as part of an OS design. By enumerating the .pbcxml files in the 3rdParty folder, Platform Builder provides a way to add entries to the Catalog Items View for those components.

Creating and Modifying Catalog Entries

To add a new catalog item to the Windows Embedded CE catalog, you can create a copy of an existing catalog file (.pbcxml file) and then edit the file content by using the Catalog Editor provided with Platform Builder. You can also create a new catalog file in Platform Builder if you open the File menu in Visual Studio, point to New, and then select File. In the New File dialog box, under Platform Builder for CE 6.0 R2, select Platform Builder Catalog File, and then click Open.

NOTE

Editing catalog files

Always edit catalog files by using the Catalog Editor provided with Platform Builder. There are no settings that require you to work with a text editor such as Notepad. Opening and editing cata­log files manually outside of Platform Builder is unnecessarily time-consuming.

Catalog Entry Properties

Each catalog entry has a number of properties that you can modify in Platform Builder, as illustrated in Figure 1-6. The most important properties include the following

■ Unique Id A unique identifier string.

■ Name The name of the catalog component as it appears in the Catalog Items View.

■ Description An expanded description of the component, which appears when the user hovers the mouse pointer over the catalog item for several seconds.

■ Modules A list of files that belong to this catalog component.

■ Sysgen variable An environment variable for the catalog item. If your catalog component sets a SYSGEN variable, this is where to put it.

■ Additional Variables A collection of additional environment variables for the catalog item. This is possibly the most important part of the catalog component in a BSP, because this field enables you to set environment variables used in sources, .bib, and .reg files to control the build process. You can also use this field to generate dependencies on other components.

■ Platform directory The location of the catalog item files. For a new BSP, set this property to the name of the BSP's directory.

Figure 1-6 Catalog item properties

NOTE

Unique names

Each catalog component must have a unique ID, typically composed of the vendor and the component names. When you clone a BSP by using the Clone Catalog Item feature, Platform Builder creates a unique name for the cloned component automatically; however, when editing catalog files manually, be sure to use unique identifiers.

Adding a New Catalog Item to an OS Design

To use a new catalog file or catalog item, ensure that the corresponding .pbcxml file exists in a subfolder called Catalog under a subdirectory of the 3rdParty or Platform directories, and then click the Refresh Catalog Tree button in the Catalog Items View in Visual Studio. Platform Builder dynamically regenerates the catalog by traversing the 3rdParty and Platform directories and processing any existing catalog files. With the new component listed in the Catalog Items View, you can include it in the OS design by selecting its check box, as explained earlier in Lesson 1.

Using a Catalog Item for BSP Development

Now that you have added your new catalog component and learned how to set item- specific environment variables, you can use this technique to include the component in a BSP, set C/C++ build directives, and modify system registry settings in the runtime image. When other developers using this BSP select your catalog item in an OS design project, they will implicitly use the settings you specified. To include a catalog component in a BSP, you need to edit the BSP's Platform.bib file and add a conditional statement based on your settings. You can choose to include a component if a variable is or isn't defined by using if-else statements. Note that it might be necessary to run the Rebuild Current BSP and Subprojects command, which you can find in Visual Studio on the Build menu, under Advanced Build Commands, for changes to the .bib and .reg files to take effect. Chapter 2 covers the Rebuild Current BSP and Subprojects command in more detail.

To set a C/C++ directive based on an environment variable that you specified in the catalog item's properties, you can use a conditional statement in the sources file based on the variable and add a CDEFINES entry. You should generally try to avoid setting C/C++ build directives based on catalog item properties, as this approach will make it difficult to distribute a binary version of your BSP in the future.

You can also change entries in the system registry by using conditional statements. You only need to edit the .reg files to include or exclude certain registry files related to the new component.

Exporting a Catalog Item from the Catalog

Some catalog items do not support direct cloning. To clone these components, you must create either a new catalog file, if you are creating a new entry under the 3rdParty folder, or a new entry in a BSP's existing catalog file. In any case, you should verify that the original values for all SYSGEN and additional environment variables are preserved. Do not forget to change the ID, because each item in the catalog must have a unique ID, as mentioned earlier in this lesson.

Catalog Component Dependencies

The catalog in Platform Builder for Windows Embedded CE 6.0 R2 supports component dependencies. To specify that a component is dependent on another component, you must set the SYSGEN or Additional Variables field for the component of the catalog item, and then include this value in the form of an additional environment variable in the dependent component. For example, if you have catalog components in your BSP for both a display driver and a backlight driver for the display, you can set the Additional Variables field for the display driver to BSP_DISPLAY and the Additional Variables field for the backlight driver to BSP_BACKLIGHT. If you now want the display driver to be dependent on the backlight driver, you can edit the catalog entry for BSP_DISPLAY in the Catalog Editor and add BSP_BACKLIGHT to the additional environment variables. Then, whenever you include the display driver in an OS design, Platform Builder automatically includes the backlight driver as well. The Catalog Items View will show the check box of the backlight driver with a green square to indicate that this component is included as a dependency of the display driver.

Lesson Summary

Platform Builder for Windows Embedded CE 6.0 R2 comes with a file-based catalog system that you can use to contain your own catalog items by including them in separate catalog files in the Platform or 3rdParty directory within the %_WINCEROOT% directory tree. The file format of catalog files is XML and the file-name extension is .pbcxml. Platform Builder automatically enumerates the .pbcxml files when you start Visual Studio or refresh the Catalog Items View in Solution Explorer. To add a new catalog item to the Windows Embedded CE catalog, you can start with a new catalog file or create a copy of an existing catalog item and then edit the file content by using the Catalog Editor. There is no need to edit .pbcxml files by using a text editor, such as Notepad, because all settings are available directly within Platform Builder. Among other things, you can specify SYSGEN and additional environment variables for conditional C/C++ build directives, registry modifications, and dependency definitions.

Lesson 5: Generating a Software Development Kit

Developers who want to create applications for a target device require a Software Development Kit (SDK). An SDK will automatically correspond to your OS design so that the developers can only use those features that are actually available. The SDK includes features that are present in the OS design so that application developers do not accidentally create code that fails to run at run time due to an unsupported API.

After this lesson, you will be able to:

■ Identify the purpose of an SDK.

■ Generate an SDK.

■ Localize SDK files on your hard drive.

■ Use an SDK.

Estimated lesson time: 20 minutes.

Software Development Kit Overview

In order to compile and create valid applications for your OS design, developers need to include the necessary header files and link to the correct libraries in their development projects. You must ensure that the SDK for your OS design contains all required header files and libraries, including headers and libraries for any custom components you want to provide to application developers. Platform Builder for Windows Embedded CE 6.0 R2 enables you to create SDKs for your OS designs by exporting all the required header files and libraries.

SDK Generation

It is generally the task of the OS design creator to generate and distribute customized SDKs. Platform Builder provides an SDK export feature for this purpose. The SDK export feature creates the customized SDK for your OS design, along with a .msi file that includes the SDK Setup Wizard.

Configuring and Generating an SDK

To create and configure an SDK by using the SDK export feature of Platform Builder, follow these steps:

1. Configure your OS design and build it at least once in the Release configuration.

2. Display Solution Explorer, right-click SDKs, and select Add New to display the SDK Property Pages dialog box.

3. In the SDK Property Pages dialog box, configure the Install properties of the SDK and define the MSI Folder Path, MSI File Name, and Locale, as illustrated in Figure 1-7. You can also specify a number of custom settings.

4. To include additional files, select the Additional Folders node in the SDK Property Pages dialog box.

5. Click OK.

Figure 1-7 SDK Property Pages dialog box

Adding New Files to an SDK

You can add files to an SDK manually by either using the Additional Folders option in the SDK property pages or by copying them into the SDK directory for your OS design, typically in %_WINCEROOT%\OSDesigns\<Solution Name>\<OS Design Name>\WinCE600\<Platform Name>\SDK. It is also possible to automate that process by using .bat and sources files, so that the build engine copies the latest version of the files into the SDK each time you perform a build.

Make sure you copy the files into the following SDK subdirectories:

■ Inc Contains the header files included in the SDK.

■ Lib\<Processor Type>\<Build Type> Contains the libraries included in the SDK.

Installing an SDK

After completing the SDK build process, you can find the .msi file in the SDK subdirectory of your OS design folder. This is typically %_WINCEROOT%\OSDesigns\<Solution Name>\<OS Design Name>\SDKs\SDK1\MSI\<SDK Name>.msi. You can freely redistribute this MSI according to your licensing agreements for Platform Builder and any third-party components included.

You can install this MSI package on any computer with Visual Studio 2005 and use it to develop Windows Embedded CE applications for your target device. On a computer with the SDK installed, you can find the files under %PROGRAMFILES%\Windows Embedded CE Tools\WCE600.

Lesson Summary

Windows Embedded CE 6.0 R2 is a componentized operating system, which implies that application developers require a customized SDK that corresponds to your OS design in order to develop applications that will work on your target device. The custom SDK should not only include the required Windows Embedded CE components, but also the headers and libraries for any custom components that you included in the OS design, to avoid problems due to missing files or libraries at build and run time. Platform Builder provides an SDK export feature to generate SDKs and to create an MSI package for convenient SDK deployment on application development computers by means of an SDK Setup Wizard.

Lab 1: Creating, Configuring, and Building an OS Design

In this lab, you create an OS design, and then customize that design by adding components from the catalog. It is important to complete all the procedures in this lab, because it provides the foundation for subsequent exercises in other chapters of this Microsoft Windows Embedded CE 6.0 R2 Exam Preparation Kit.

NOTE

Detailed step-by-step instructions

To help you successfully master the procedures presented in this lab, see the document "Detailed Step-by-Step Instructions for Lab 1" in the companion material for this book.

►Create an OS Design

1. In Visual Studio 2005 with Platform Builder for Windows Embedded CE 6.0 R2, select the File menu, New submenu, and Project menu option, and then create a new OS design project.

2. Use the default OS design name (OSDesign1).

3. Visual Studio will launch the Windows Embedded CE 6.0 OS Design Wizard.

4. Select the check box for Device Emulator: ARMV4I in the BSP list and click Next.

5. From the list of available design templates, select PDA Device. From the list of available design variants select Mobile Handheld.

6. Deselect .NET Compact Framework 2.0 and ActiveSync on the next wizard page, as illustrated in Figure 1-8. The Internet Browser and Quarter VGA Resources- Portrait Mode check boxes should remain checked.

7. On the Networking Communications wizard page, deselect TCP/IPv6 Support and Personal Area Network (PAN) to exclude Bluetooth and Infrared Data Association (IrDA) support. Leave Local Area Network (LAN) selected.

8. Click Finish to complete the Windows Embedded CE 6.0 OS Design Wizard. On completion, Visual Studio opens your OS design project. The Solution Explorer tab should be active and show your new OS design project under the Solution container.

Figure 1-8 Creating an OS design for a PDA device

NOTE

Subsequent OS design changes

The OS Design Wizard creates the initial configuration for your OS design. You can make further changes to the OS design after completing the wizard.

►Inspect the OS Catalog

1. In Visual Studio, locate Solution Explorer and click the Catalog Items View tab.

2. Expand the individual container nodes to analyze the selected check boxes and icons in the catalog. Check boxes with a green check mark indicate items specif­ically added as a part of the OS design. Check boxes with a green square indicate items that are part of the OS design due to dependencies. Selection boxes that are not marked indicate items that are not included in the OS design but are available to be added.

3. Locate a catalog item with a green square in its check box.

4. Right-click this catalog item and choose Reasons For Inclusion Of Item. The Remove Dependent Catalog Item dialog box displays the catalog items that caused Platform Builder to include the selected catalog item in the OS design, as illustrated in Figure 1-9.

5. Expand the Core OS | CEBASE | Applications -End User | ActiveSync node in the catalog.

6. Right-click either of the ActiveSync system cpl items and select Display In Solution View. The view changes to the Solution Explorer tab to display the subproject containing the ActiveSync component. This is a great way to navigate through the source code that comes with Windows Embedded CE 6.0.

Figure 1-9 Reason for including a catalog item as a dependency

►Add Support for the Internet Explorer 6.0 Sample Browser Catalog Item

1. Select the Catalog Items View tab to display the OS design catalog. Verify that the filtering option is set to All Catalog Items In Catalog.

2. In the Search text box to the right of the Catalog Items View Filter button, type Internet Explorer 6.0 Sample and press Enter or click the green arrow.

3. Verify that the search locates the Internet Explorer 6.0 Sample Browser catalog item. Select the corresponding check box to include this catalog item in the OS design, as illustrated in Figure 1-10.

Figure 1-10 Including the Internet Explorer 6.0 Sample Browser catalog item in an OS design

►Add Support for Managed Code Development to Your OS Design

1. In the Search text box, type ipconfig and press Enter.

2. Verify that the search locates the Network Utilities (IpConfig, Ping, Route) cata­log item.

3. Add the Network Utilities (IpConfig, Ping, Route) catalog item to your OS design by selecting the corresponding check box.

4. In the Search text box, type wceload and press Enter.

5. Verify that the search locates the CAB File Installer/Uninstaller catalog item. The search can find this catalog item because the value of its SYSGEN variable is wceload.

6. Add the Cab File Installer/Uninstaller catalog item to your OS design.

7. Use the search feature in a similar way to locate the OS Dependencies for .NET Compact Framework 2.0 container. Verify that the OS Dependencies for .NET Compact Framework 2.0 catalog item is included in your OS design, as illustrated in Figure 1-11.

Figure 1-11 Adding the OS Dependencies for .NET Compact Framework 2.0 catalog item to an OS design

NOTE

Headless .NET Compact Framework

There are two separate components in this category. Be sure you select the one that does not have the -Headless modifier in its description, because the headless version is intended for devices with no display.

Chapter Review

In order to deploy Microsoft Windows Embedded CE 6.0 R2 on a target device, you must create an OS design that includes the necessary operating system (OS) components, features, drivers, and configuration settings. You can then use Platform Builder to build the corresponding run-time image for deployment. The most important tasks you must accomplish to create a customized OS design that suits your requirements include:

■ Creating an OS design project in Visual Studio by using the OS Design Wizard.

■ Adding and removing components from the OS manually and through dependencies.

■ Setting environment and SYSGEN variables through the Catalog Editor.

■ Configuring regional settings for localization of the OS design.

■ Cloning components from the catalog either automatically by clicking Clone Catalog Item or manually by using the Sysgen Capture tool.

■ Exporting a custom SDK for your OS design to facilitate application development for your target device.

Key Terms

Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book.

■ OS design

■ Component

■ SYSGEN variable

■ Environment Variable

■ Software Development Kit

Suggested Practice

To help you successfully master the exam objectives presented in this chapter, complete the following tasks:

Create a Custom OS Design

By using the OS Design Wizard, create an OS design based on the Device Emulator: ARMV4I BSP and the Custom Device design template. Perform the following tasks after OS design creation:

■ Add the .NET Compact Framework 2.0 Add this catalog item by using the search feature in the Catalog Items View.

■ Localize your run-time image Display the OS Design property pages and local­ize the OS design for the French language.

Generate and Test an SDK

Based on the OS design generated during Lab 1, perform the following tasks:

■ Build and generate the binary image Build and generate the binary image for the OS design generated in the Release build configuration.

■ Create and install the SDK Verify that the build process completes successfully, and then create a new SDK, build it, and install it on an application development computer.

■ Use the SDK Use another instance of Visual Studio and create a Win32 Smart Device application. Use your custom SDK as the reference SDK for the project and build the application.

Chapter 2

Building and Deploying a Run-Time Image

The Microsoft® Windows® Embedded CE 6.0 R2 build process is very complex. This process includes several phases and relies on a variety of tools to initialize the Windows Embedded CE build environment, compile the source code, copy modules and files to a common release directory, and create the run-time image. Batch files and build tools, such as the Sysgen tool (Sysgen.bat) and the Make Binary Image tool (Makeimg.exe), automate this process. You can run these tools directly at the command prompt or start the build process in Microsoft Platform Builder for Windows Embedded CE 6.0 R2. The Platform Builder integrated development environment (IDE) relies on the same processes and tools. In either case, a thorough understanding of the build process and the steps required to deploy the resulting runtime image is essential if you want to create run-time images efficiently, troubleshoot build errors, or deploy Board Support Packages (BSPs) and subprojects as part of a run-time image on a target device.

Exam objectives in this chapter:

■ Building run-time images

■ Analyzing build results and build files

■ Deploying a run-time image on a target device

Before You Begin

■ To complete the lessons in this chapter, you must have:

■ An understanding of operating system (OS) design aspects, including catalog items and the configuration of environment variables and SYSGEN variables, as explained in Chapter 1, "Customizing the Operating System Design."

■ At least some basic knowledge about Windows Embedded CE software development, including source code compilation and linking.

■ A development computer with Microsoft Visual Studio® 2005 Service Pack 1 and Platform Builder for Windows Embedded CE 6.0 R2 installed.

Lesson 1: Building a Run-Time Image

The Windows Embedded CE build process is the final step in the run-time image development cycle. Based on the settings defined in the OS design, Platform Builder compiles all components, including subprojects and the BSP, and then creates a runtime image that you can download to the target device. The build process entails several build phases, automated by means of batch files. You must understand these phases and the build tools if you want to configure build options correctly, create runtime images efficiently, and solve build-related issues.

After this lesson, you will be able to:

■ Understand the build process.

■ Analyze and fix build issues.

■ Deploy a run-time image to target hardware.

Estimated lesson time: 40 minutes.

Build Process Overview

The Windows Embedded CE build process includes four main phases, as illustrated in Figure 2-1. They follow each other sequentially, but you can also carry them out independently if you know the purpose and tools used for each phase. By selectively running the build tools, you can perform individual build steps in a targeted way, which helps to save build time and ultimately increases your efficiency.

Figure 2-1 Build phases and build tools

The build process includes the following key phases:

■ Compile phase Compiler and linker use source code and resource files to generate executable (.exe) files, static (.lib) libraries, dynamic-link library (.dll) files, and binary resource (.res) files according to the selected locales. For example, the build system compiles the source code in the Private and Public folders into .lib files during this phase. This process can take several hours to complete, but fortunately it is seldom required to rebuild these components because binaries are already provided by Microsoft. In any case, you should not modify the source code in the Private and Public folders.

■ Sysgen phase The build system sets or clears SYSGEN variables based on the catalog items and dependency trees included in the OS design, filters the header files and creates import libraries for the Software Development Kits (SDKs) defined in the OS design, creates a set of run-time image configuration files for the OS design, and builds the BSP based on the source files in the Platform directory.

■ Build phase The build system processes the source files of your Board Support Package and applications using the files generated during the Sysgen phase. At this time, hardware-linked drivers and the OEM adaptation layer (OAL) are built. Although the processes during the build phase are carried out automatically during the Sysgen phase, it is important to understand that if you modify only the BSP and subprojects, then you can rebuild the BSP and subprojects without running the Sysgen tool again.

■ Release Copy phase The build system copies all files required to create the runtime image to the OS design's release directory. This includes the .lib, .dll, and .exe files created during the Compile and Sysgen phases, as well as binary image builder (.bib) and registry (.reg) files. The build system might skip this phase if headers and libraries are up-to-date.

■ Make Run-time Image phase The build system copies project-specific files (Project.bib, Project.dat, Project.db, and Project.reg) to the release directory and assembles all files in the release directory into a run-time image. Directives based on environment variables specified in .reg and .bib files control which catalog items the build system includes in the final run-time image. The run-time image is typically a file named Nk.bin, which you can download and run on the target device.

Building Run-Time Images in Visual Studio

During the installation of Windows Embedded CE 6.0 R2 on your development workstation, Platform Builder integrates with Visual Studio 2005 and extends the Build menu so that you can control the build process directly in the Visual Studio IDE. Figure 2-2 shows the Platform Builder commands that are available on the Build menu when you select the OS design node in Solution Explorer.

Figure 2-2 Windows Embedded CE build commands in Visual Studio 2005

You can use the Platform Builder commands on the Build menu to perform selective build steps or a combined series of steps that span multiple build phases. For example, you can use the Copy Files To Release Directory command to ensure that the build system copies updated .bib and .reg files to the release directory even if header files and libraries have not changed. Otherwise, the build system skips the Release Copy phase and .bib file or .reg file changes are not applied to the run-time image.

Table 2-1 summarizes the purpose of the Windows Embedded CE build commands.

Table 2-1 Windows Embedded CE build and rebuild commands

Menu Option Description
Build Solution Equivalent to the Sysgen command on the Advanced Build Commands submenu.
Rebuild Solution Equivalent to the Clean Sysgen command on the Advanced Build Commands submenu.
Clean Solution Cleans the release directory by deleting all intermediate files.
Build <OS Design Name> Helpful in solutions that include multiple OS designs. In solutions with a single OS design, these options correspond to the Build Solution, Rebuild Solution, and Clean Solution commands.
Rebuild <OS Design Name>
Clean <OS Design Name>
Advanced Build Commands Sysgen Runs the Sysgen tool and links the .lib files in the Public and Private folders to create the files for the run-time image. The files remain in the WinCE folder of the OS design. Depending on global build settings, the build process can automatically advance to the Release Copy and then Make Run-time Image phases.
Clean Sysgen Cleans out intermediate files created during previous builds before running the Sysgen tool. Use this option if you added or removed files or catalog items after a previous Sysgen session to reduce the risk of build errors.
Build And Sysgen Compiles the entire contents of the Public and Private folders, and then links the files by using the settings in your OS design. This process takes several hours and is only necessary if you modified the contents of the Public folder. Unless you modi­fy the Windows Embedded CE code base (not recommended), you should not use this option.
Rebuild And Sysgen Cleans out intermediate files created during previous builds in the Public and Private folders, and then runs the Build and Sysgen steps. You should not use this option.
Build Current BSP And Subprojects Builds the files in the directory for the current BSP and any subprojects in the OS design, and then runs the Sysgen tool. Note that this option will build other BSPs than the ones used in the current OS design, so make sure your BSPs are compatible with each other or remove unused BSPs.
Rebuild Current BSP And Subprojects Cleans out intermediate files created during previous builds, and then runs the Build Current BSP And Subprojects steps.
Build All Subprojects Compiles and links all subprojects, skipping any files that are up-to-date.
Rebuild All Subprojects Cleans, compiles, and links all subprojects.
Build All SDKs Builds all SDKs in the project and creates corresponding Microsoft Installer (MSI) packages. Because there is generally no reason to create debug versions of MSI packages, use this option only for a Release build configuration.
Copy Files To Release Directory Copies the files generated for the BSP and other components during the Compile and Sysgen phases to the release directory in order to include these file in the run-time image.
Make Run-Time Image Takes all the files in the release directory to create the run-time image. Following this step, you can download the run-time image to a target device.
Open Release Directory In Build Window Opens a Command Prompt window, changes into the release directory, and sets all necessary environment variables to run batch files and build tools manually. Use this to perform build steps at the command prompt. The standard Command Prompt window does not initialize the development environment to run the build tools successfully.
Global Build Settings Copy Files To Release Directory After Build Enables or disables automatic advancement to the Release Copy phase for all commands.
Make Run-Time Image After Build Enables or disables automatic advancing to the Make Run-time Image phase after any build operation.
Targeted Build Settings Make Run-Time Image After Build Enables or disables the Make Run-time Image phase.
Batch Build Enables you to perform multiple builds sequentially.
Configuration Manager Enables you to add or remove build configurations.

The Advanced Build Commands submenu provides access to several Platform Builder-specific build commands that you might find useful on a regular basis. For example, you need to run the Sysgen or Clean Sysgen command when you add or remove catalog components to or from the OS design to create the binary versions for the run-time image. Exceptions to this rule are components that do not modify SYSGEN variables, such as components in the ThirdParty folder. It is not necessary to run Sysgen or Clean Sysgen when you select or deselect these items. Following the Sysgen phase, Platform Builder continues the build process similar to running the Build Current BSP And Subprojects command.

You can select the Build Current BSP And Subprojects or the Rebuild Current BSP And Subprojects commands in Visual Studio if you want to compile and link the source code in the Platform directory and any subprojects in the OS design and put the code into the target directory under Platform\<BSP Name>\Target and Platform\<BSP Name>\Lib. This is necessary, for instance, if you modify the source code in the Platform directory. Depending on the Copy Files To Release Directory After Build and Make Run-Time Image After Building options, Platform Builder copies the files to the release directory and creates the run-time image. You can also perform these steps individually either through the menu or by running the Buildrel.exe and Makeimg.exe tools at the command prompt.

CAUTION

Clean Sysgen affects multiple build configurations

If you run the Clean Sysgen command in one build configuration, you also have to run Sysgen for the other build configurations later. Keep in mind that the Clean Sysgen command deletes all files generated for other build configurations, as well as for the current build configuration.

Building Run-Time Images from the Command Line

The Platform Builder for CE6 R2 plug-in for Visual Studio 2005 provides convenient access to batch files and build tools, but you can also run these batch files and build tools directly at the command prompt. Each build command in Visual Studio with Platform Builder corresponds to a specific build command, as listed in Table 2-2. Remember, however, to use the Open Build Window command in Visual Studio to open a Command Prompt window for this purpose. The standard command prompt does not initialize the development environment. The build process will fail without the presence of the required environment variables.

Table 2-2 Build commands and command line equivalents

Build Command Command Line Equivalent
Build blddemo -q
Rebuild blddemo clean -q
Sysgen blddemo -q
Clean Sysgen blddemo clean -q
Build And Sysgen* Blddemo
Rebuild And Clean Sysgen* blddemo clean cleanplat -c
Build Current BSP And Subprojects blddemo -qbsp
Rebuild Current BSP And Subprojects blddemo -c -qbsp

* Not recommended

Windows Embedded CE Run-Time Image Content

As illustrated in Figure 2-3, the run-time image includes all items and components that you want to deploy and run on a target device as part of the OS design, such as kernel components, applications, and configuration files. The most important configuration files for developers are binary image builder (.bib) files, registry (.reg), database (.db), and file system (.dat) files. These files determine the memory layout and specify how Platform Builder initializes the file system and the system registry. It is important to know how to work with these files. For example, you can modify the .reg and .bib files for a BSP directly in the OS design or create a subproject to add custom settings to the run-time image in a more componentized way. As mentioned in Chapter 1, it is generally faster and more convenient to modify the .reg and .bib files of an OS design directly, yet subprojects facilitate the reuse of customizations across multiple OS designs.

Figure 2-3 Contents of a run-time image

Binary Image Builder Files

The Windows Embedded CE build process relies on .bib files to generate the content of the run-time image and to define the final memory layout of the device. At the end of the build process, during the Make Run-time Image phase, the Make Binary Image tool (Makeimg.exe) calls the File Merge tool (Fmerge.exe) to combine all applicable .bib files, such as Config.bib and Platform.bib from the Platform\<BSP Name>\Files folder, Project.bib, Common.bib, and any subproject .bib files, into a file named Ce.bib in the release directory. The Make Binary Image tool then calls the ROM Image Builder tool (Romimage.exe) to process this file and determine which binaries and files to include in the run-time image.

A .bib file can include the following sections:

■ MEMORY Defines the parameters for the memory layout. You can typically find this section in the Config.bib file for your BSP, such as C:\Wince600\Platform\DeviceEmulator\Files\Config.bib.

■ CONFIG Defines configuration options for Romimage.exe to customize the binary run-time image. You can typically find this section in the Config.bib file for your BSP. This section is optional.

■ MODULES Specifies a list of files that Romimage.exe marks to be loaded into RAM or executed in place (XIP). Only uncompressed object modules can execute directly from read-only memory. You can list native executable files in this section, but not managed binaries, because the Common Language Runtime (CLR) must convert the Microsoft Intermediate Language (MSIL) content into native machine code at run time.

■ FILES References executables and other files that the operation system should load into RAM for execution. You should specify managed code modules in this section.

.BIB File MEMORY Section The MEMORY section in the Config.bib file defines reserved memory regions, assigning each region a name, address, size, and type. A good example is the MEMORY section that you can find in the Config.bib file in the Device Emulator BSP. This Device Emulator BSP is available with Platform Builder for CE 6.0 R2 out-of-the-box. You can find Config.bib in the PLATFORM\<BSP Name>\FILES directory. Figure 2-4 shows this MEMORY section in Visual Studio 2005.

Figure 2-4 MEMORY section from a .bib file

The fields in the MEMORY section define the following parameters:

■ Name This is the name of the MEMORY section. The name must be unique.

■ Address This hexadecimal number represents the starting address of the memory section.

■ Size This hexadecimal number defines the total length of the memory section in bytes.

■ Type This field can have one of the following values:

 ■ RESERVED Indicates that this area is reserved. Romimage.exe skips these sections during image creation. For example, the Ce.bib file shown in Figure 2-4 includes several RESERVED sections, such as an ARGS section to provide a shared memory area for the boot loader (EBOOT) to pass data to the system after startup (ARGS) and a DISPLAY section for a display buffer. The Ce.bib file of other OS designs might include different RESERVED sections for memory areas that the kernel is not supposed to use as system memory.

 ■ RAMIMAGE Defines the memory area that the system can use to load the kernel image and any modules you specified in the MODULES and FILES sections of .bib files. A run-time image can only have one RAMIMAGE section and the address range must be contiguous.

 ■ RAM Defines a memory area for the RAM file system and for running applications. This memory section must be contiguous. If you need a noncontiguous memory section, such as for extension dynamic RAM (DRAM) present on the device, you can allocate noncontiguous memory by implementing the OEMGetExtensionDRAM function in the OAL of the BSP. Windows Embedded CE supports up to two sections of physical noncontiguous memory.

.BIB File CONFIG Section The CONFIG section defines additional parameters for the run-time image, including the following options:

■ AUTOSIZE Automatically combines RAMIMAGE and RAM sections and allocates any unused memory in the RAMIMAGE section to RAM, or if necessary takes memory from the RAM section and provides it to the RAMIMAGE.

■ BOOTJUMP If specified, moves the boot jump page to a specific area within the RAMIMAGE section, rather than by using the default area.

■ COMPRESSION Automatically compresses writable memory sections in the image. The default value for this option is ON.

■ FIXUPVAR Initializes a kernel global variable during the Make Binary Image phase.

■ FSRAMPERCENT Sets the percentage of RAM used for the RAM file system.

■ KERNELFIXUPS Instructs Romimage.exe to relocate memory writable by the kernel. This option is generally enabled (ON).

■ OUTPUT Changes the directory that Romimage.exe uses as the output directory for the Nk.bin file.

■ PROFILE Specifies whether the image includes the profiler.

■ RAM_AUTOSIZE Expands the size of RAM to the end of the last XIP section.

■ RESETVECTOR Relocates the jump page to a specified location. This is required for MIPS processors to boot from 9FC00000.

■ ROM_AUTOSIZE Resizes XIP regions, taking into account the ROMSIZE_AUTOGAP setting.

■ ROMFLAGS Configures the following options for the kernel:

 ■ Demand paging Fully copying a file into RAM before executing it or paging in parts of it.

 ■ Full kernel mode Run every OS thread in kernel mode, which leaves the sys­tem vulnerable to attack but improves performance.

 ■ Trust only ROM modules Marks only files in ROM as trusted.

 ■ Flush the X86 TLB onX86 systems Improves performance but adds a security risk.

 ■ Honor the /base linker setting Defines whether or not to use the /base linker setting in DLLs.

■ ROMOFFSET Enables you to run the run-time image in a memory location that is different from the storage location. For example, you can store the run-time image in FLASH memory, and then copy and run it from RAM.

■ ROMSIZE Specifies the size of the ROM in bytes.

■ ROMSTART Specifies the ROM's starting address.

■ ROMWIDTH Specifies the number of data bits and how Romimage.exe splits the run-time image. Romimage.exe can put the entire run-time image into one file, split the run-time image into two files of even and odd 16-bit words, or create four files of even and odd 8-bit bytes.

■ SRE Determines whether Romimage.exe generates a .sre file. Motorola S-record (SRE) is a file format recognized by most ROM burners.

■ X86BOOT Specifies whether or not to add a JUMP instruction at the x86 reset vector address.

■ XIPSCHAIN Enables the creation of Chain.bin and Chain.lst files to set up an XIP chain, so that you can split an image into multiple files.

.BIB File MODULES and FILES Sections BSP and OS design developers must frequently edit the MODULES and FILES sections of a .bib file to add new components to a run-time image. The format for the MODULES and FILES section is practically identical, although the MODULES section supports more configuration options. The key difference is that the MODULES section lists files not compressed in memory to support XIP, while the FILES section lists files that are compressed. The operating system must decompress the data when accessing the files.

The following listing shows two small MODULES and FILES sections from a Platform.bib file. For a complete example, check out the Platform.bib file of the Device Emulator BSP.

MODULES

; Name                  Path                                Memory Type

; --------------        ----------------------------        -----------

; @CESYSGEN IF CE_MODULES_DISPLAY

IF BSP_NODISPLAY !

DeviceEmulator_lcd.dll $(_FLATRELEASEDIR)\DeviceEmulator_lcd.dll NK SHK

IF BSP_NOBACKLIGHT !

backlight.dll          $(_FLATRELEASEDIR)\backlight.dll          NK SHK

ENDIF BSP_NOBACKLIGHT !

ENDIF BSP_NODISPLAY !

; @CESYSGEN ENDIF CE_MODULES_DISPLAY

FILES

; Name           Path                         Memory Type

; -------------- ---------------------------- -----------

; @CESYSGEN IF CE_MODULES_PPP

dmacnect.lnk     $(_FLATRELEASEDIR)\dmacnect.lnk    NK SH

; @CESYSGEN ENDIF CE_MODULES_PPP

You can define the following options for file references in MODULES and FILES sections:

■ Name The name of the module or file as it appears in the memory table. This name is usually the same as the file name in the run-time image.

■ Path The complete path to the file that Romimage.exe incorporates into the run-time image.

■ Memory References the name of a memory area in the MEMORY section of the Config.bib file into which Romimage.exe loads the module or file. It is usually set to NK to integrate the file in the NK area defined in the MEMORY section.

■ Section Override Enables you to specify modules in a FILES section and files in a MODULES section. Essentially, Romimage.exe ignores the section in which the entry resides, and treats the entry as a member of the specified section. This parameter is optional.

■ Type Specifies the file type and can be a combination of flags, as shown in Table 2-3.

Table 2-3 File type definitions for MODULES and FILES sections

MODULES and FILES Sections MODULES Section Only
■ S The file is a system file. ■ K Instructs Romimage.exe to assign a fixed virtual address to the DLL's public exports and runs the module in kernel mode rather than user mode. Drivers must run in kernel mode to have direct access to the underlying hardware.
■ H The file is hidden. ■ R Compress resource files.
■ U The file is uncompressed. (The default setting for files is compressed.) ■ C Compress all data in the file. If the file is already in RAM, it will be decompressed again into a new section of RAM, which results in higher RAM consumption.
■ N The module is not trusted. ■ P Do not check the CPU type on a per-module basis.
■ D The module cannot be debugged. ■ X Sign the module and include the signature in the ROM.
■ M Signals that the kernel must not page the module on demand. (See Chapter 3 for more information on the effects of demand paging.)
■ L Instructs Romimage.exe not to split the ROM DLL.

Conditional .bib File Processing It is important to note that .bib files support conditional statements based on environment variables and SYSGEN variables. You can set environment variables through catalog items, and then check these variables in IF statements in a .bib file to include or exclude certain modules or other files. For SYSGEN variables, use @CESYSGEN IF statements instead.

The MODULES and FILES listing in the previous section illustrates the use of @CESYSGEN IF and IF statements for processing conditions based on SYSGEN and environment variables. For example, the @CESYSGEN IF CE_MODULES_DISPLAY statement in the MODULES sections specifies that the BSP should automatically include the display driver if the OS design includes a display component. You can verify that Platform Builder adds the display component to the BSP automatically if you display the Catalog Items View in Visual Studio for an OS design that uses a display, as illustrated in Figure 2-5.

Figure 2-5 Core OS components that depend on the display item

Registry Files

Registry (.reg) files are used to initialize the system registry on the remote device. These files are almost identical to registry files of Windows desktop operating systems, except that the CE .reg files do not start with a header and version information. If you accidentally double-click a CE .reg file on your development computer and confirm that you want to add the settings to the desktop registry, a dialog box appears to inform you that the .reg file is not a valid registry script. Another difference is that CE .reg files can include conditional statements similar to .bib files, so that you can import registry settings according to the selected catalog items. The following snippet from the Platform.reg file of the Device Emulator BSP illustrates the use of preprocessing conditions.

; Our variables

#define BUILTIN_ROOT HKEY_LOCAL_MACHINE\Drivers\BuiltIn

;#define PCI_BUS_ROOT $(BUILTIN_ROOT)\PCI

#define DRIVERS_DIR $(_PUBLICROOT)\common\oak\drivers

; @CESYSGEN IF CE_MODULES_RAMFMD

; @CESYSGEN IF FILESYS_FSREGHIVE

; HIVE BOOT SECTION

[HKEY_LOCAL_MACHINE\init\BootVars]

"Flags"=dword:1 ; see comment in common.reg

; END HIVE BOOT SECTION

; @CESYSGEN ENDIF FILESYS_FSREGHIVE

; @CESYSGEN IF CE_MODULES_PCCARD

; @XIPREGION IF DEFAULT_DEVICEEMULATOR_REG

IF BSP_NOPCCARD !

#include "$(_TARGETPLATROOT)\src\drivers\pccard\pcc_smdk2410.reg"

#include "$(DRIVERS_DIR)\pccard\mdd\pcc_serv.reg"

[HKEY_LOCAL_MACHINE\Dri vers\PCCARD\PCMCIA\TEMPLATE\PCMCIA]

"Dll"="pcmcia.dll"

"NoConfig"=dword:1

"NoISR"=dword:1 ; Do not load any ISR.

"IClass"=multi_sz:"{6BEAB08A-8914-42fd-B33F-61968B9AAB32}=

PCMCIA Card Services"

ENDIF ; BSP_NOPCCARD !

; @XIPREGION ENDIF DEFAULT_DEVICEEMULATOR_REG

; @CESYSGEN ENDIF CE_MODULES_PCCARD

Database Files

Windows Embedded CE relies on database (.db) files to set up the default object store. The object store is a transaction-based storage mechanism. In other words, it is a repository for databases in RAM that operating system and applications can use for persistent data storage. For example, the operating system uses the object store to manage the stack and memory heap, to compress and decompress files, and to integrate ROM-based applications and RAM-based data. The transaction-oriented nature of the storage mechanism ensures data integrity even in the event of a sudden power loss while data is being written to the object store. When the system restarts, Windows Embedded CE either completes the pending transaction, or reverts to the last known good configuration prior to the interruption. For system files, the last known good configuration can mean that Windows Embedded CE must reload the initial settings from ROM.

File System Files

File system (.dat) files, specifically Platform.dat and Project.dat, contain settings to initialize the RAM file system. When you cold start the run-time image on a target device, Filesys.exe processes these .dat files to create the RAM file system directories, files, and links on the target device. The Platform.dat file is typically used for hardware-related entries while the Project.dat file applies to the OS design, yet you can use any existing .dat file to define file system settings because the build system eventually merges all .dat files into one file named Initobj.dat.

For example, by customizing the Project.dat file, you can define root directories in addition to the Windows directory for a run-time image. By default, items placed in the ROM image appear in the Windows directory, yet by using a .dat file, you can make files also appear outside the Windows directory. You can also copy or link to files in the ROM Windows directory. This is particularly useful if you want to place shortcuts on the desktop or add links to your applications to the Start menu. Similar to .reg and .bib files, you can use IF and IF ! (if not) conditional blocks in .dat files.

The following listing illustrates how to use a Project.dat file to create two new root directories named Program Files and My Documents, create a My Projects subdirectory under Program Files, and map the Myfile.doc file from the Windows directory into the My Documents directory.

Root:-Directory("Program Files")

Root:-Directory("My Documents")

Directory("\Program Files"):-Directory("My Projects")

Directory("\My Documents"):-File("MyFile.doc", "\Windows\Myfile.doc")

Lesson Summary

A thorough understanding of the build system can help to decrease development time and therefore project costs. You must know the steps performed during each phase of the build process if you want to test source code changes quickly and without unnecessary compilation cycles. You must also know the purpose and location of the run-time image configuration files, such as .reg, .bib, .db, and .dat files, to create and maintain OS designs efficiently.

The Windows Embedded CE build system combines the various .reg, .bib, .db, and .dat files during the Make Run-time Image phase into consolidated files that the build system then uses to configure the final run-time image. It is a good idea to check these files if you want to verify that a specific setting or file made it into the final image without having to load the run-time image on the target device. You can find the various run-time image configuration files in the release directory of the OS design. If you discover that expected entries are missing, check the conditional statements and the environment variables and SYSGEN variables defined in your catalog items.

The build system creates the following run-time image configuration files during the Make Run-time Image phase:

■ Reginit.ini Combines the Platform.reg, Project.reg, Common.reg, IE.reg, Wceapps.reg, and Wceshell.reg files.

■ Ce.bib Combines the Config.bib, Platform.bib, Project.bib, and Subproject bib files.

■ Initdb.ini Combines the Common.db, Platform.db, and Project.db files.

■ Initobj.dat Combines the Common.dat, Platform.dat, and Project.dat files.

Lesson 2: Editing Build Configuration Files

In addition to run-time image configuration files, Windows Embedded CE also uses build configuration files to compile and link source code into functional binary components. Specifically, the build system relies on three types of source code configuration files: Dirs, Sources, and Makefile. These files provide the Build tool (Build.exe) and the compiler and linker (Nmake.exe) with information about the source-code directories to traverse, the source code files to compile, and what type of binary components to build. As a CE developer, you frequently must edit these files, such as when cloning public catalog items, by following the procedures discussed in Chapter 1.

After this lesson, you will be able to:

■ Identify the source code configuration files used during the build process.

■ Edit build configuration files to generate applications, DLLs, and static libraries.

■ Estimated lesson time: 25 minutes.

Dirs Files

Dirs files identify directories that contain source-code files to be included in the build process. When Build.exe finds a Dirs file in the folder in which it is run, it traverses the subdirectories referenced in the Dirs file to build the source code in these subdirectories. Among other things, this mechanism enables you to update parts of a run-time image selectively. If you make changes to the source code in Subprojectl, you can rebuild this subproject selectively by running Build.exe in the Subprojectl directory. You can also exclude directories in the source code tree from the build process by removing the corresponding directory references from the Dirs file, or by using conditional statements.

Dirs files are text files with a straightforward content structure. You can use the DIRS, DIRS_CE, or OPTIONAL_DIRS keyword, and then specify the list of subdirectories on a single line, or on multiple lines if you terminate each line with a backslash to continue on the next line. Directories referenced by using the DIRS keyword are always included in the build process. If you use the DIRS_CE keyword instead, Build.exe only builds the source code if the source code is written specifically for a Windows Embedded CE run-time image. The OPTIONAL_DIRS keyword designates optional directories. Keep in mind, however, that Dirs files can contain only one DIRS directive. Build.exe processes the directories in the order they are listed, so be sure to list prerequisites first. It is also possible to use the wildcard "*" to include all directories.

The following listing, taken from default Windows Embedded CE components, illustrates how to include source code directories in the build process by using Dirs files.

# C:\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\Dirs

# ---------------------------------------------------

DIRS=common \

 drivers \

 apps    \

 kitl    \

 oal     \

 bootloader

# C:\WINCE600\PLATFORM\H4SAMPLE\SRC\DRIVERS\Dirs

# ---------------------------------------------------

DIRS= \

# @CESYSGEN IF CE_MODULES_DEVICE

 buses \

 dma \

 triton \

# @CESYSGEN IF CE_MODULES_KEYBD

 keypad \

# @CESYSGEN ENDIF CE_MODULES_KEYBD

# @CESYSGEN IF CE_MODULES_WAVEAPI

 wavedev \

# @CESYSGEN ENDIF CE_MODULES_WAVEAPI

# @CESYSGEN IF CE_MODULES_POINTER

 touch \

 tpagent \

# @CESYSGEN ENDIF CE_MODULES_POINTER

# @CESYSGEN IF CE_MODULES_FSDMGR

 nandflsh \

# @CESYSGEN ENDIF CE_MODULES_FSDMGR

# @CESYSGEN IF CE_MODULES_SDBUS

 sdhc \

# @CESYSGEN ENDIF CE_MODULES_SDBUS

# @CESYSGEN IF CE_MODULES_DISPLAY

 backlight \

# @CESYSGEN ENDIF CE_MODULES_DISPLAY

# @CESYSGEN IF CE_MODULES_USBFN

 usbd \

# @CESYSGEN ENDIF CE_MODULES_USBFN

# @CESYSGEN ENDIF CE_MODULES_DEVICE

# @CESYSGEN IF CE_MODULES_DISPLAY

 display \

# @CESYSGEN ENDIF CE_MODULES_DISPLAY

NOTE

Editing Dirs files in Solution Explorer

The Solution Explorer in Visual Studio with Platform Builder for Windows Embedded CE 6.0 R2 uses Dirs files to generate a dynamic view of the Windows Embedded CE directory structure in an OS design project. However, you should not add or remove directories in Solution Explorer, because editing Dirs files in Solution Explorer can lead to a changed build order, which can result in build errors that require a second build to resolve.

Sources Files

If you check the folders and files of a standard OS design, such as C:\Wince6OO\OSDesigns\OSDesignl, you will find that the project includes no Dirs files by default. If you include subprojects for custom components and applications, you will find a Sources file in each subproject's root folder instead. The Sources file provides more detailed information about the source-code files, including build directives, which a Dirs file cannot provide. However, a source-code directory can only contain one Dirs file or one Sources file, not both. That means that a directory with a Sources file cannot contain subdirectories with more code. During the build process, Nmake.exe uses the Sources files to determine what file type to build (.lib, .dll, or .exe), and how to build it. Similar to Dirs files, Sources files expect you to specify declarations in a single line, unless you terminate the line with a backslash to continue the declaration on the next line.

The following listing shows the content of a Sources file in the Device Emulator BSP. By default, you can find this file in the C:\Wince600\Platform\DeviceEmulator \Src\Drivers\Pccard folder.

WINCEOEM=1

TARGETNAME=pcc_smdk2410

TARGETTYPE=DYNLINK

RELEASETYPE=PLATFORM

TARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib \

           $(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib

SOURCELIBS=$(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\pcc_com.lib

DEFFILE=pcc_smdk2410.def

DLLENTRY=_DllEntryCRTStartup

INCLUDES=$(_PUBLICROOT)\common\oak\drivers\pccard\common;$(INCLUDES)

SOURCES= \

Init.cpp \

PDSocket.cpp \

PcmSock.cpp \

PcmWin.cpp

#xref VIGUID {549CAC8D_8AF0_4789_9ACF_2BB92 599470D}

#xref VSGUID {0601CE65_BF4D_453A_966B_E20250AD2E8E}

You can define the following directives in a Sources file:

■ TARGETNAME This is the name of the target file, without file name extension.

■ TARGETTYPE Defines the type of file to be built, as follows:

■ DYNLINK A dynamic-link library (.dll).

■ LIBRARY A static-link library (.lib).

■ PROGRAM An executable file (.exe).

■ NOTARGET Build no file.

■ RELEASETYPE Specifies the directory where Nmake.exe places the target file, as follows:

 ■ PLATFORM PLATFORM\<BSP Name>\<Target>.

 ■ OAK, SDK, DDK %_PROJECTROOT%\Oak\<Target>.

 ■ LOCAL The current directory.

 ■ CUSTOM A directory specified in TARGETPATH.

 ■ MANAGED %_PROJECTROOT%\Oak\<Target>\Managed.

■ TARGETPATH Defines the path for RELEASETYPE=CUSTOM.

■ SOURCELIBS Specifies libraries to be linked with the target file specified in TARGETNAME to create the final binary output. This option is typically used for creating a .lib file but not .dll or .exe files.

■ TARGETLIBS Specifies additional libraries and object files to link to the final binary output, typically used for creating .dll or .exe files but not for .lib files.

■ INCLUDES Lists additional directories to search for include files.

■ SOURCES Defines the source files to be used for this particular component.

■ ADEFINES Specifies parameters for the assembler.

■ CDEFINES Specifies parameters for the compiler, which can be used as additional DEFINE statements for use in IFDEF statements.

■ LDEFINES Sets linker definitions.

■ RDEFINES Specifies DEFINE statements for the resource compiler.

■ DLLENTRY Defines the entry point for a DLL.

■ DEFFILE Defines the .def file which contains a DLL's exported symbols.

■ EXEENTRY Sets the entry point of an executable file.

■ SKIPBUILD Marks the build of the target as successful without an actual build of the target.

■ WINCETARGETFILE0 Specifies nonstandard files that should be built before building the current directory.

■ WINCETARGETFILES This macro definition specifies nonstandard target files that Build.exe should build after Build.exe links all other targets in the current directory.

■ WINCE_OVERRIDE_CFLAGS Defines compiler flags to override default settings.

■ WINCECPU Specifies that the code requires a certain CPU type and should only be built for that particular CPU.

NOTE

Performing specific actions before and after the build

In addition to the standard directives, Windows Embedded CE Sources files support the direc­tives PRELINK_PASS_CMD and POSTLINK_PASS_CMD. You can use these directives to perform custom actions based on command-line tools or batch files before and after the build process, such as PRELINK_PASS_CMD = pre_action.bat and POSTLINK_PASS_CMD = post_action.bat. This is useful, for example, if you want to copy additional files to the release directory when developing a custom application.

Makefile Files

If you look closer at the contents of a subproject folder, you can also find a file named Makefile to provide default preprocessing directives, commands, macros, and other expressions to Nmake.exe. However, in Windows Embedded CE, this Makefile includes only a single line that references %_MAKEENVROOT%\Makefile.def. By default, the environment variable %_MAKEENVROOT% points to the C:\Wince6OO\Public\Common\Oak\Misc folder and the Makefile.def file in this location is the standard Makefile for all CE components, so you should not modify this file. Among other things, the Makefile.def file contains include statements to pull in Sources file, such as !INCLUDE $(MAKEDIR)\sources, which specify the Sources file from the subproject folder. You should edit the Sources file in the subproject folder to adjust the way Nmake.exe builds the target file.

Lesson Summary

The Windows Embedded CE 6.0 R2 development environment relies on Makefile, Sources, and Dirs files to control how Build.exe and Nmake.exe compile and link source code into functional binary components for the run-time image. You can use Dirs files to define the source code directories included in the build process or Sources files to specify compile and build directives in greater detail. The Makefile, on the other hand, requires no customization. It merely references the default Makefile.def file with general preprocessing directives, commands, macros, and other processing instructions for the build system. You must thoroughly understand the purpose of files and how they control the build process if you want to clone public catalog items or create new components efficiently.

Lesson 3: Analyzing Build Results

You are certain to encounter build errors during the software-development cycle. In fact, it is not uncommon to use compile errors as a syntax check for source code, although IntelliSense® and other coding aids available in Visual Studio 2005 help to reduce the amount of typos and other syntax errors. Syntax errors are relatively uncomplicated to fix because you can double-click the corresponding error message in the Output window and jump right to the critical line in the source code file. However, compiler errors are only one type of build errors that can occur. Other common build errors are math errors, expression evaluation errors, linker errors, and errors related to run-time image configuration files. In addition to error messages, the build system also generates status messages and warnings to help you analyze and diagnose build issues. The amount of information generated during the build process can be overwhelming. You need to know the different types and general format of build messages if you want to identify, locate, and solve build errors efficiently.

After this lesson, you will be able to:

■ Locate and analyze build reports.

■ Diagnose and solve build issues.

Estimated lesson time: 15 minutes.

Understanding Build Reports

When you perform a build either in the Visual Studio IDE or the command prompt, the build process outputs a significant amount of build information. The build system tracks this information in a Build.log file. Details about compilation or linker warnings and errors also can be found in the Build.wrn and Build.err files. If you started a complete build or a Sysgen operation for an OS design by using one of the corresponding commands on the Build menu in Visual Studio, the build system writes these files in the %_WINCEROOT% folder (by default, C:\Wince600). On the other hand, if you perform a build for only a particular component, such as by right- clicking a subproject folder in Solution Explorer and clicking the Build command from the context menu, the build system writes these files in that specific directory. In either case, the Build.wrn and Build.err files only exist if you encounter warnings and errors during the build process. However, you do not need to open and parse through these files in Notepad or another plain-text editor. Visual Studio 2005 with Platform Builder for CE 6.0 R2 displays this information during the build process in the Output window. You can also examine status messages, warnings, and errors in the Error List window that you can display by clicking Error List, which is available under Other Windows on the View menu.

Figure 2-6 shows the Output window and the Error List window in undocked view. The Output window displays the Build.log content if you select Build from the Show Output From list box. The Error List window displays the contents Build.wrn and Build.err files.

Figure 2-6 Output window and Error List window with build information in Visual Studio

Specifically, you can find the following information in the build log files:

■ Build.log Contains information about the individual commands issued within each phase during the build process. This information is useful for analyzing both the build process in general, and build errors in particular.

■ Build.wrn Contains information about warnings generated during the build process. If possible, try to eliminate or at least identify the reasons for the warnings. The information in Build.wrn is also included in Build.log.

■ Build.err Contains specific information about build errors encountered during the build process. This information is also available with additional details in Build.log. This file is created only when an error occurs.

NOTE

Identifying the build step

The build system keeps track of skipped and entered build phases in the Build.log file. For example, the entry CEBUILD: Skipping directly to SYSGEN phase indicates that the build system skipped the Compile phase for a component. You can determine where the Sysgen phase begins, how the build process transitions from SYSGEN to BUILD, and how BUILD eventually leads to MAKEIMG.

Troubleshooting Build Issues

While analyzing build log files can give you great insight into the build process in general, it is most useful when troubleshooting build errors. If an error message is related to a source code file, you can jump to the relevant line of code by double- clicking the message entry in the Error List window. However, not all build errors are related to source code. Linker errors due to missing library references, sysgen errors due to missing component files, copy errors due to exhausted disk capacities, and make run-time image errors due to incorrect settings in run-time image configuration files can also cause a build process to fail.

Errors during the Sysgen Phase

Sysgen errors are generally the result of missing files. The Build.log file might provide detailed information about the reason. Components that you recently added to or removed from an OS design can cause this type of error if the required dependencies are unavailable. To diagnose a Sysgen error, it is a good idea to verify all changes related to catalog items and their dependencies. Also note that some components require you to perform a clean Sysgen build instead of a regular Sysgen cycle. Typically, you should not use the Clean Sysgen command because performing a clean Sysgen in Release or Debug build configuration requires you to perform a regular Sysgen in the other build configuration as well. However, when adding or removing catalog items and encountering Sysgen build errors afterward, during the next regular Sysgen, you might have to perform a clean Sysgen build to solve the issue.

Errors during the Build Phase

Build errors are typically caused by compiler errors or linker errors. Compiler errors are syntax errors, missing or illegal parameters in function calls, divisions by zero and similar issues that prevent the compiler from generating valid binary code. By double- clicking a compiler error, you can jump to the critical line of code. Keep in mind, however, that compiler errors can be the results of other compiler errors. For example, an incorrect variable declaration can cause numerous compiler errors if the variable is used in many places. It is generally a good idea to start at the top of the error list, fix the code, and recompile. Even small code changes can often eliminate a very large number of errors from the list.

Linker errors are harder to troubleshoot than compiler errors. They are typically the result of missing or incompatible libraries. Incorrectly implemented APIs can also result in linker errors if the linker cannot resolve external references to exported DLL functions. Another common cause has its root in incorrectly initialized environment variables. Build files, specifically the Sources file, use environment variables instead of hard-coded directory names to point to referenced libraries. If these environment variables are not set, the linker will not be able to locate the libraries. For example, %_WINCEROOT% must point to C:\Wince600 if you installed Windows Embedded CE in the default configuration and %_FLATRELEASEDIR% must point to the current release directory. To verify the values of environment variables, open the Build menu in Visual Studio and select Open Release Directory in Build Window, and then at the command prompt use the set command with or without an environment variable, such as set _winceroot. Running the set command without parameters displays all environment variables, but be aware that this list is long.

Errors during the Release Copy Phase

Buildrel errors encountered during the Release Copy phase are generally a sign of inadequate hard drive space. During the Release Copy phase, the build system copies files to the release directory. It might be necessary to free up hard drive space or place the OS design folder on a different drive. Make sure that the new path to the OS design folder contains no spaces because spaces in the path or in the OS design name cause errors during the build process.

Errors during the Make Run-Time Image Phase

Errors encountered during this final phase in the build process generally result from missing files. This can happen if a component failed to build in an earlier step, but the build process nevertheless continued to proceed to the Make Run-time Image phase. Syntax errors in .reg files or .bib files can lead to this situation when the build system is unable to create the Reginit.ini file or Ce.bib file. Makeimg.exe calls the FMerge tool (FMerge.exe) during the build process to create these files, and if this fails, such as due to incorrect conditional statements, you encounter a make-image error. Another possible error is Error: Image Exceeds (X), which means the image is larger than the maximum possible size specified in Config.bib.

Lesson Summary

Platform Builder for Windows Embedded CE 6.0 R2 integrates with the build-logging system of Visual Studio 2005 to provide you with convenient access to status information, warnings, and error messages generated during the build process and tracked in Build.log, Build.wrn, and Build.err files. Depending on how you start the build process in Visual Studio, these files reside either in the %_WINCEROOT% folder or in a subproject directory, yet the actual location of the files is not important because you can analyze the content from these files directly in the Output window and the Error List window in Visual Studio. It is not necessary to open these files in Notepad or another text editor.

By analyzing build log files, you can gain a better understanding of the build process in general and build issues in particular. Typical build issues you might encounter occasionally are compiler errors, linker errors, Sysgen errors, build errors, and other errors generated during the Release Copy and Make Run-time Image phases. If a build error is related directly to a line in a source code file, you can double-click the message entry in the Error List window, and Visual Studio automatically opens the source-code file and jumps to the critical line. Other issues, such as buildrel errors due to inadequate hard drive space, require you to perform troubleshooting steps outside of the Visual Studio IDE.

Lesson 4: Deploying a Run-Time Image on a Target Platform

Having solved all build issues and successfully generated a run-time image, you are ready to deploy Windows Embedded CE on the target device. There are several ways to accomplish this task. The method you choose depends on the startup process you use to load Windows Embedded CE on the target device. There are several ways you can start a Windows Embedded CE 6.0 run-time image. You can start an image directly from ROM, in which case you must deploy the run-time image on the target device by using a ROM tool. You can also use a boot loader, and then either download the run-time image every time the device starts or store the image in persistent memory for reuse. Windows Embedded CE 6.0 R2 comes with generic boot-loader code that you can customize according to your specific needs. It is also straightforward to implement a third-party boot loader. Essentially, Windows Embedded CE can accommodate almost any start environment, and makes it easy to download new run-time images quickly and conveniently during the development cycle and for release to the end user.

After this lesson, you will be able to:

■ Decide how to deploy a run-time image on a target device.

■ Configure Platform Builder to select the correct deployment layer.

Estimated lesson time: 15 minutes.

Choosing a Deployment Method

In order to deploy a run-time image, you must establish a connection to the target device. This requires you to configure several communication parameters that determine how Platform Builder communicates with the device.

The Core Connectivity infrastructure of Windows Embedded CE supports various download methods and transport mechanisms to accommodate hardware platforms with varying communication capabilities. To define the communication parameters for your target device, open the Target menu in Visual Studio and select Connectivity Options, which displays the Target Device Connectivity Options dialog box. By default, Platform Builder provides a target device named CE Device in the Target Device list box, as illustrated in Figure 2-7, but you can also create additional devices with unique names by clicking the Add Device link.

Figure 2-7 Target Device Connectivity Options window

Download Layer

The Download list box and associated Settings button enable you to configure the download service used for downloading the run-time image to your target device. The Core Connectivity infrastructure supports the following download layers for deploying a run-time image:

■ Ethernet Downloads the run-time image over an Ethernet connection. Use the Settings button to configure the Ethernet download service. The development workstation and the target device must be on the same subnet; otherwise, you cannot connect to the target device.

■ Serial Downloads the run-time image over an RS232 connection. Use the Settings button to configure the port, baud rate, and other serial communication parameters.

■ Device Emulator (DMA) Downloads the run-time image to a device emulator through Direct Memory Access (DMA). Use the Settings button to configure the device emulator.

■ USB Downloads the run-time image over a Universal Serial Bus (USB) connection. There are no settings to configure.

■ Image Update Updates the image in the device's flash memory. There are no settings to configure.

■ None Select this option if you do not want to download or update the run-time image.

Transport Layer

After transferring the run-time image to the remote device, you can attach to the device if you enabled Kernel Independent Transport Layer (KITL) in the OS design. In general, the selected kernel transport service should match the download service that you selected in the Download list box. The Core Connectivity infrastructure supports the following transport layer options:

■ Ethernet Communicates with the target device over an Ethernet connection. The connection uses the same settings as the download service.

■ Serial Communicates with the target device over an RS232 connection. The connection uses the same settings as the download service.

■ Device Emulator (DMA) Communicates with a device emulator through DMA.

■ USB Communicates with the target device over a USB connection.

■ None Disables communication with the target device.

Debugger Options

If you enabled support for one or more debuggers in the OS design, the debugger names will appear as options in the Debugger list box. By default, the following debugger options are available:

■ Sample Device Emulator eXDI2 Driver This is a sample Extensible Resource Identifier (XRI) Data Interchange (XDI) driver included in Windows Embedded CE 6.0 R2. XDI is a standard hardware-debugging interface.

■ KdStub This is the Kernel Debugger. KdStub stands for kernel debugger stub, which instructs Platform Builder and Visual Studio to use the software debugger.

■ CE Dump File Reader If you added the Error Report Generator catalog item to your OS design, you can use this option for postmortem debugging.

■ None Select this option if you do not want to use a debugger.

Attaching to a Device

Having configured the device connection, you are ready to transfer the run-time image to the target device or device emulator by using the Core Connectivity infrastructure. This is accomplished in Visual Studio 2005 by using the Attach Device command that is available on the Target menu. Even if you do not plan to use KITL or the Core Connectivity infrastructure for debugging, you must attach to the device so that Platform Builder can download the run-time image.

Following the image download, the start process commences, KITL becomes active if enabled on the target device, and you can use the Kernel Debugger to follow the start process, and debug operating system components and application processes. By using KITL, you can also exploit remote tools available in Visual Studio with Platform Builder on the Target menu, such as File Viewer to interact with the device's file system, Registry Editor to access the device's registry settings, Performance Monitor to analyze resource utilization and response times, and Kernel Tracker and other remote tools to view detailed information on the running system. You can find more information about system debugging in Chapter 4, "Debugging and Testing the System."

Lesson Summary

Windows Embedded CE supports run-time image deployment over a variety of device connections to accommodate hardware platforms with varying requirements and capabilities, including Ethernet connections, serial connections, DMA, and USB connections. For example, DMA is the right choice if you want to deploy CE 6.0 R2 on a Device Emulator. You only need to configure the communication parameters and you are ready to deploy Windows Embedded CE by clicking the Attach Device command on the Target menu in Visual Studio 2005 with Platform Builder.

EXAM TIP

To pass the certification exam, you must be familiar with the varioust ways to deploy a Windows Embedded CE run-time image. In particular, make sure you know how to deploy a run-time image for a Device Emulator.

Lab 2: Building and Deploying a Run-Time Image

In this lab, you build and deploy an OS design based on the Device Emulator BSP, analyze the build information in the Visual Studio Output window to identify the start of the various build phases, and then configure a connection to a target device in order to download the run-time image. To demonstrate how to customize a target device, you modify the Device Emulator configuration to support a larger screen resolution and to enable network communication. In a final step, you download the run-time image and attach to the target device with the Kernel Debugger, so you can examine the Windows Embedded CE start process in detail. To create the initial OS design in Visual Studio, follow the procedures outlined in Lab 1, "Creating, Configuring, and Building an OS Design."

NOTE

Detailed step-by-step instructions

To help you successfully master the procedures presented in this Lab, see the document "Detailed Step-by-Step Instructions for Lab 2" in the companion material for this book.

Build a Run-Time Image for an OS Design

1. After completing Lab 1, select Sysgen under Advanced Build Commands on the Build menu in Visual Studio, as illustrated in Figure 2-8. Alternatively, you can select Build Solution under the Build menu, which will perform a build starting with the Sysgen step.

TIP

Sysgen operations

Sysgen operations can take up to 30 minutes to complete. To save time, do not run Sysgen every time you change the OS design. Instead, run Sysgen after adding and removing all desired components.

2. Follow the build process in the Output window. Examine the build information to identify the SYSGEN, BUILD, BUILDREL, and MAKEIMG steps. You can press Ctrl+F to display the Find And Replace dialog box, and then search for the following text to identify the start of these phases:

 a. Starting Sysgen Phase For Project The SYSGEN steps start.

 b. Build Started With Parameters The BUILD steps start.

 c. C:\WINCE600\Build.log The BUILDREL steps start.

 d. BLDDEMO: Calling Makeimg — Please Wait The MAKEIMG steps starts.

3. Open the C:\Wince600 folder in Windows Explorer. Verify that Build.* files exist.

4. Open the Build.* files in a text editor, such as Notepad, and examine the content.

Figure 2-8 Building an OS design

Configure Connectivity Options

1. In Visual Studio, open the Target menu and select Connectivity Options to display the Target Device Connectivity Options dialog box.

2. Verify that CE Device is selected in the Target Device list box.

3. Select Device Emulator (DMA) from the Download list box.

4. Select Device Emulator (DMA) from the Transport list box.

5. Select KdStub from the Debugger list box, as illustrated in Figure 2-9.

Figure 2-9 Setting Target Device Connectivity Options

Change the Emulator Configuration

1. Next to the Download list box, click the Settings button.

2. In the Emulator Properties dialog box, switch to the Display tab.

3. Change the Screen Width to 640 pixels and the Screen Height to 480 pixels.

4. Switch to the Network tab.

5. Select the Enable NE2000 PCMCIA Network Adapter And Bind To check box, then select the Connected Network Card option from the list box, as illustrated in Figure 2-10, and then click OK.

6. Click Apply to save the new device configuration.

7. Click Close to close the Target Device Connectivity Options dialog box.

Figure 2-10 Device Emulator network options

Test a Run-Time Image on the Device Emulator

1. In Visual Studio, open the Target menu, and then click Attach Device.

2. Verify that Visual Studio downloads the run-time image to the target device. The download can take several minutes to complete.

3. Follow the debug messages during the start process in the Visual Studio Output window.

4. Wait until Windows Embedded CE has completed the start process, and then interact with the Device Emulator and test the features of your OS design, as illustrated in Figure 2-11.

Figure 2-11 Windows Embedded CE device emulator

Chapter Review

The Windows Embedded CE build process includes several phases and relies on a variety of build and run-time image configuration files to compile the source code and create the run-time image. It includes a compile phase to generate .exe files, static libraries, DLLs, and binary resource (.res) files for the BSP and subprojects; a Sysgen phase to filter and copy source code based on SYSGEN variables from the Public folder for catalog items selected in the OS design, and create a set of run-time image configuration files; a Release Copy phase to copy the files from the BSP and subprojects required to build the run-time image into the release directory; and finally a Make Run-time Image phase to create the run-time image from the content in the release directory according to the setting specified in .bib, .reg, .db, and .dat files.

You can examine the build process if you analyze the information that Platform Builder generates and tracks in Build.log, Build.wrn, and Build.err files. The Build.log file contains detailed information about every build command issued in each build phase. Build.wrn and Build.err contain the same information, but filtered for warnings and errors encountered during the build process. You do not need to open these text files directly in Notepad. It is more convenient to work with build status information and error messages in Visual Studio. The Output window and the Error List window provide convenient access.

Build errors can occur for a variety of reasons. The most common causes are compiler and linker errors. For example, running build commands in an incorrectly initialized build environment will lead to linker errors when environment variables that identify library directories in the Sources file point to invalid locations. Other important build configuration files, such as Dirs files and Makefile.def, can also rely on SYSGEN variables and environment variables in conditional statements and in directory paths.

Having successfully generated a run-time image, you can deploy Windows Embedded CE on a target device. This requires you to configure a device connection based on the Core Connectivity infrastructure. The final deployment step is simply to click the Attach Device command that you can find on the Target menu in Visual Studio with Platform Builder for Windows Embedded CE 6.0 R2.

It is important that you are familiar with the following configuration files, which control the Windows Embedded CE build process:

■ Binary image builder (.bib) files Configure the memory layout and determine the files included in the run-time image.

■ Registry (.reg) files Initialize the system registry on the target device.

■ Database (.db) files Set up the default object store.

■ File system (.dat) files Initialize the RAM file system layout at start time.

■ Dirs files Determine which directories to include in the build process.

■ Sources files Define preprocessing directives, commands, macros, and other processing instructions for the compiler and linker. Take the place of Makefile files in the Visual Studio with Platform Builder IDE.

■ Makefile files Reference the default Makefile.def file and should not be edited.

Key Terms

Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book.

■ Sysgen

■ Buildrel

■ Flat release directory

■ Connectivity options

■ KITL

Suggested Practice

To help you successfully master the exam objectives presented in this chapter, complete the following tasks:

Start the Build Process from the Command Line

To increase your understanding of the Windows Embedded CE build processes, perform the following steps:

■ Sysgen process Run sysgen -q from the command line after setting an environment variable in a catalog item.

■ Build process Open a Command Prompt window, change into the current BSP folder (%_TARGETPLATROOT%) and run build-c with and without setting the WINCEREL environment variable to 1 (set WINCEREL=1). Check the content of the %_FLATRELEASEDIR% folder before and after the build.

Deploy Run-Time Images

Deploy a Windows Embedded CE run-time image to a Device Emulator by using different download, transport, and debugging settings for the target device in Platform Builder.

Clone a Public Catalog Component Manually

Clone a component from the %_WINCEROOT%\Public folder by copying the source files to a BSP, as explained in Chapter 1, "Customizing the Operating System Design." Next, run sysgen_capture to create a Sources file that defines the component dependencies. Modify the new Sources file to build the component as part of your BSP. For detailed step-by-step information to accomplish this advanced development task, read the section "Using the Sysgen Capture Tool" in the Platform Builder for Microsoft Windows Embedded CE product documentation available on the Microsoft MSDN® website at http://msdn2.microsoft.com/en-us/library/aa924385.aspx.

Chapter 3

Performing System Programming

System performance is critical for user productivity. It directly influences the user's perception of a device. In fact, it is not uncommon for users to judge the usefulness of a device based on the performance of the system and the look and feel of the user interface. By providing too complex of an interface, you can confuse users and open your device to potential security risks or unexpected user manipulations. By using the incorrect APIs, or incorrect applications architecture in a multithreaded environment, you may significantly impact performance. Performance optimization and system customization are real challenges for firmware providers. This chapter discusses the tools and highlights best practices to achieve optimal system response times on target devices.

Exam objectives in this chapter:

■ Monitoring and optimizing system performance

■ Implementing system applications

■ Programming with threads and thread synchronization objects

■ Implementing exception handling in drivers and applications

■ Supporting power management at the system level

Before You Begin

To complete the lessons in this chapter, you must have the following:

■ A thorough understanding of real-time systems design concepts, such as scheduler functionality in an operating system, interrupts, and timers.

■ Basic knowledge of multithreaded programming, including synchronization objects.

■ A development computer with Microsoft® Visual Studio® 2005 Service Pack 1 and Platform Builder for Microsoft Windows® Embedded CE 6.0 installed.

Lesson 1: Monitoring and Optimizing System Performance

Performance monitoring and optimization are important tasks in the development of small-footprint devices. The need for optimized system performance remains critical because of an ever-growing number of increasingly complex applications and the requirement for intuitive and therefore resource-intensive user interfaces. Performance optimization requires firmware architects and software developers to constrain resource consumption within their system components and applications so that other components and applications can use the available resources. Whether developing device drivers or user applications, optimized processing algorithms can help to save processor cycles, and efficient data structures can preserve memory. Tools exist at all system levels to identify performance issues within and between drivers, applications, and other components.

After this lesson, you will be able to:

■ Identify the latency of an interrupt service routine (ISR).

■ Improve the performance of a Windows Embedded CE system.

■ Log and analyze system performance information.

Estimated lesson time: 20 minutes.

Real-Time Performance

Drivers, applications, and OEM adaptation layer (OAL) code impact system and real­time performance. Although Windows Embedded CE may be used in real-time and non-real-time configurations, it is important to note that using non-real-time components and applications can decrease system performance in a real-time operating system (OS) configuration. For example, you should keep in mind that demand paging, device input/output (I/O), and power management are not designed for real-time devices. Use these features carefully.

Demand Paging

Demand paging facilitates memory sharing between multiple processes on devices with limited RAM capacity. When demand paging is enabled, Windows Embedded CE discards and removes memory pages from active processes under low-memory conditions. However, to keep the code of all active processes in memory, disable demand paging for the entire operating system or for a specific module, such as a dynamic-link library (DLL) or device driver.

You can disable demand paging by using the following methods:

■ Operating system Edit the Config.bib file and set the ROMFLAGS option in the CONFIG section.

■ DLLs Use the LoadDriver function instead of the LoadLibrary function to load the DLL into memory.

■ Device drivers Add the DEVFLAGS_LOADLIBRARY flag to the Flags registry entry for the driver. This flag causes Device Manager to use the LoadLibrary function instead of the LoadDriver function to load the driver.

Windows Embedded CE allocates and uses memory as usual, but does not discard it automatically when you disable demand paging.

System Timer

The system timer is a hardware timer that generates system ticks at a frequency of one tick per millisecond. The system scheduler uses this timer to determine which threads should run at what time on the system. A thread is the smallest executable unit within a process that is allocated processor time to execute instructions in the operating system. You can stop a thread for an amount of time by using the Sleep function. The minimum value that you can pass to the Sleep function is 1 (Sleep(1)), which stops the thread for approximately 1 millisecond. However, the sleep time is not exactly 1 millisecond because the sleep time includes the current system timer tick plus the remainder of the previous tick. The sleep time is also linked to the priority of the thread. The thread priority determines the order in which the operating system schedules the threads to run on the processor. For those reasons, you should not use the Sleep function if you need accurate timers for real-time applications. Use dedicated timers with interrupts or multimedia timers for real-time purposes.

Power Management

Power management can affect system performance. When the processor enters the Idle power state, any interrupt generated by a peripheral or the system scheduler causes the processor to exit this state, restore the previous context, and invoke the scheduler. Power context switching is a time-consuming process. For detailed information about the power management features of Windows Embedded CE, see the section "Power Management" in the Windows Embedded CE 6.0 documentation available on the Microsoft MSDN® website at http://msdn2.microsoft.com/en-us/library/aa923906.aspx.

System Memory

The kernel allocates and manages system memory for heaps, processes, critical sections, mutexes, events, and semaphores. Yet, the kernel does not completely free the system memory when releasing these kernel objects. Instead, the kernel holds on to the system memory to reuse it for the next allocation. Because it is faster to reuse allocated memory, the kernel initializes the system memory pool during the startup process and allocates further memory only if no more memory is available in the pool. System performance can decrease depending how processes use virtual memory, heap objects, and the stack.

Non-Real-Time APIs

When calling system APIs, or Graphical Windows Event System (GWES) APIs, be aware that some APIs rely on non-real-time features, such as for window drawing. Forwarding calls to non-real-time APIs may dramatically decrease system performance. Consequently, you should make sure that your APIs in real-time applications are real-time compliant. Other APIs, such as ones used for accessing a file system or hardware, can have an impact on performance because these APIs may use blocking mechanisms, such as mutexes or critical sections, to protect resources.

NOTE

Non-real-time APIs

Non-real-time APIs can have a measurable impact on real-time performances and, unfortu­nately, the Win32® API documentation provides little detail on real-time issues. Practical experi­ence and performance testing can help you choose the right functions.

Real-Time Performance Measurement Tools

Windows Embedded CE includes many performance monitoring and troubleshooting tools that can be used to measure the impact of Win32 APIs on system performance. These tools are helpful when identifying inefficient memory use, such as an application not releasing the system memory it allocates.

The following Windows Embedded CE tools are particularly useful to measure the real-time performance of your system components and applications:

■ ILTiming Measures Interrupt Service Routine (ISR) and Interrupt Service Thread (IST) latencies.

■ OSBench Measures system performance by tracking the time the kernel spends managing kernel objects.

■ Remote Performance Monitor Measures system performance, including memory usage, network throughput, and other aspects.

Interrupt Latency Timing (ILTiming)

The ILTiming tool is particularly useful for Original Equipment Manufacturers (OEMs) who want to measure ISR and IST latencies. Specifically, ILTiming enables you to measure the time it takes to invoke an ISR after an interrupt occurred (ISR latency) and the time between when the ISR exits and the IST actually starts (IST latency). This tool uses a system hardware tick timer by default, but it is also possible to use alternative timers (high-performance counters).

NOTE

Hardware timer restrictions

Not all hardware platforms provide the required timer support for the ILTiming tool.

The ILTiming tool relies on the OALTimerIntrHandler function in the OAL to implement the ISR for managing the system tick interrupt. The timer interrupt handler stores the current time and returns a SYSINTR_TIMING interrupt event, which an ILTiming application thread waits to receive. This thread is the IST. The time elapsed between the reception of the interrupt in the ISR and the reception of the SYSINTR_TIMING event in the IST is the IST latency that the ILTiming tool measures.

You can find the ILTiming tool's source code in the %_WINCEROOT%\Public\Common\Oak\Utils folder on your development computer if you have installed Microsoft Platform Builder for Windows Embedded CE 6.0 R2. The ILTiming tool supports several command-line parameters that you can use to set the IST priority and type according to the following syntax:

iltiming [-i0] [-i1] [-i2] [-i3] [-i4] [-p priority] [-ni] [-t interval] [-n interrupt] [-all] [-o file_name] [-h]

Table 3-1 describes the individual ILTiming command-line parameters in more detail.

Table 3-1 ILTiming parameters

Command-Line Parameter Description
-i0 No idle thread. This is equivalent to using the -ni parameter.
-i1 One thread spinning without performing any actual processing.
-i2 One thread spinning, calling SetThreadPriority (THREAD_PRIORITY_IDLE).
-i3 Two threads alternating SetEvent and WaitForSingleObject with a 10-second timeout.
-i4 Two threads alternating SetEvent and WaitForSingleObject with an infinite timeout.
-i5 One thread spinning, calling either VirtualAlloc (64 KB), VirtualFree, or both. Designed to flush the cache and the translation look-aside buffer (TLB).
-p priority Specifies the IST priority (zero through 255). The default setting is zero for highest priority.
-ni Specifies no idle priority thread. The default setting is equal to the number of idle priority thread spins. This is equivalent to using the -i0 parameter.
-t interval Specifies the SYSINTR_TIMING timing interval, with clock ticks in milliseconds. The default setting is five.
-n interrupt Specifies the number of interrupts. Using this parameter you can specify how long the test will run. The default setting is 10.
-all Specifies to output all data. The default setting is to output the summary only.
-o file_name Specifies to output to file. The default setting is to output to the debugger message window.

NOTE

Idle threads

ILTiming may create idle threads (command-line parameters: -i1, -i2, -i3, and -i4) to generate activity on the system. This enables the kernel to be in a non-preemptive kernel call that must be finished before handling the IST. It can be useful to enable idle threads in background tasks.

Operating System Benchmark (OSBench)

The OSBench tool can help you measure system performance by identifying the time that the kernel spends managing kernel objects. Based on the scheduler, OSBench collects timing measurements by means of scheduler performance-timing tests. A scheduler performance-timing test measures how much time basic kernel operations, such as thread synchronization, require.

OSBench enables you to track timing information for the following kernel operations:

■ Acquiring or releasing a critical section.

■ Waiting for or signaling an event.

■ Creating a semaphore or mutex.

■ Yielding a thread.

■ Calling system APIs.

NOTE

OSBench test

To identify performance issues in different system configurations, use OSBench in conjunction with a stress test suite, such as the Microsoft Windows CE Test Kit (CETK).

The OSBench tool supports several command-line parameters that you can use according to the following syntax to collect timing samples for kernel operations:

osbench [-all] [-t test_case] [-list] [-v] [-n number] [-m address] [-o file_name] [-h]

Table 3-2 describes the individual OSBench command-line parameters in more detail.

Table 3-2 OSBench parameters

Command-Line Parameter Description
-all Run all tests (default: run only those specified by -t option):
TestId 0: CriticalSections.
TestId 1: Event set-wakeup.
TestId 2: Semaphore release-acquire.
TestId 3: Mutex.
TestId 4: Voluntary yield.
TestId 5: PSL API call overhead.
TestId 6: Interlocked API's (decrement, increment, testexchange, exchange).
-t test_case ID of test to run (need separate -t for each test).
-list List test ID's with descriptions.
-v Verbose: show extra measurement details.
-n number Number of samples per test (default=100).
-m address Virtual address to write marker values to (default=<none>).
-o file_name Output to comma-separated values (CSV) file (default: output only to debug).

Check out the OSBench source code to identify the test content. You can find the source code at the following locations:

■ %_WINCEROOT%\Public\Common\Oak\Utils\Osbench

■ %_WINCEROOT%\Public\Common\Oak\Utils\Ob_load

Test results are by default sent to the debug output, but can be redirected to a CSV file.

NOTE

OSBench requirements

The OSBench tool uses system timers. The OAL must therefore support the QueryPerformanceCounter and QueryPerformanceFrequency functions initialized in the OEMInit function.

Remote Performance Monitor

The Remote Performance Monitor application can track the real-time performance of the operating system as well as memory usage, network latencies, and other elements. Each system element is associated with a set of indicators that provide information on usage, queue length, and delays. Remote Performance Monitor can analyze log files generated on a target device.

As the name suggests, the Remote Performance Monitor application is a remote tool. The application monitors devices both under development and out in the field, as long as you have a way to connect to the device and deploy the application.

The Remote Performance Monitor monitors the following objects:

■ Remote Access Server (RAS).

■ Internet Control Message Protocol (ICMP).

■ Transport Control Protocol (TCP).

■ Internet Protocol (IP).

■ User Datagram Protocol (UDP).

■ Memory.

■ Battery.

■ System.

■ Process.

■ Thread.

This list is extended by implementing your own Remote Performance Monitor extension DLL. For sample code, look in the %COMMONPROGRAMFILES%\Microsoft Shared\Windows CE Tools\Platman\Sdk\WCE600\Samples\CEPerf folder.

Similar to the Performance tool on a Windows workstation, Remote Performance Monitor can create performance charts, configure alerts triggered at specified thresholds, write raw log files, and compile performance reports based on the performance objects available on the target device. Figure 3-1 shows a performance chart example.

Figure 3-1 A performance chart in Remote Performance Monitor

Hardware Validation

ILTiming tool, OSBench, and Remote Performance Monitor cover most performance monitoring needs. However, some cases may require other methods of gathering system performance information. For example, if you want to obtain exact interrupt latency timings, or if your hardware platform does not provide the required timer support for the ILTiming tool, you must use hardware-based performance measuring methods based on the General Purpose Input/Output (GPIO) interface of the processor and a waveform generator.

By using a waveform generator on a GPIO, it is possible to generate interrupts that are handled through ISRs and ISTs. These ISRs and ISTs then use another GPIO to generate a waveform in response to the received interrupt. The time elapsed between the two waveforms — the input waveform from the generator and the output waveform from the ISR or IST — is the latency time of the interrupt.

Lesson Summary

Windows Embedded CE provides many tools that can be employed in a development environment to measure the system performance and validate real-time device performance. The ILTiming tool is useful for measuring interrupt latencies. The OSBench tool enables you to analyze how the kernel manages system objects. Remote Performance Monitor provides the means to gather performance and statistical data in charts, logs, as well as report on devices under development and out in the field. Remote Performance Monitor has the ability to generate alerts based on configurable performance thresholds. Beyond the capabilities of these tools, you have the option to use hardware monitoring for latency and performance-measurement purposes.

Lesson 2: Implementing System Applications

As discussed in Chapter 1 "Customizing the Operating System Design", Windows Embedded CE acts as a componentized operating system and a development platform for a wide variety of small-footprint devices. These range from devices with restricted access for dedicated tasks, such as mission-critical industrial controllers, to open platforms offering access to the complete operating system, including all settings and applications, such as personal digital assistant (PDA). However, practically all Windows Embedded CE devices require system applications to provide an interface to the user.

After this lesson, you will be able to:

■ Launch an application at startup.

■ Replace the default shell.

■ Customize the shell.

Estimated lesson time: 25 minutes.

System Application Overview

Developers distinguish between system applications and user applications to emphasize that these applications have different purposes. In the context of Windows Embedded CE devices, the term system application generally refers to an application that provides an interface between the user and the system. In contrast, a user application is a program that provides an interface between the user and application- specific logic and data. Like user applications, system applications can implement a graphical or command-line interface, but system applications are typically started automatically as part of the operating system.

Start an Application at Startup

You can configure applications to start automatically as part of the Windows Embedded CE initialization process. This feature can be set in several ways, depending on whether you want to run the applications before or after Windows Embedded CE loads the shell user interface (UI). One method is to manipulate several registry settings that control the application startup behavior. Another common method is to place a shortcut to the application in the Startup folder so that the standard shell can start the application.

HKEY_LOCAL_MACHINE\INIT Registry Key

The Windows Embedded CE registry includes several registry entries to start operating system components and applications at startup time, such as Device Manager and Graphical Windows Event System (GWES). These registry entries are located under the HKEY_LOCAL_MACHINE\INIT registry key, as illustrated in Figure 3-2. You can create additional entries at this location to run your own applications included in the run-time image without having to load and run these applications manually on your target device. Among other things, automatically starting an application can facilitate debugging activities during software development.

Figure 3-2 The HKEY_LOCAL_MACHINE\INIT registry key

Table 3-3 lists three examples of registry entries to start typical Windows Embedded CE components when the run-time image starts.

Table 3-3 Startup registry parameter examples

Location HKEY_LOCAL_MACHINE\INIT
Component Device Manager GWES Explorer
Binary Launch20="Device.dll" Launch30="Gwes.dll" Launch50="Explorer.exe"
Dependencies Depend20=hex:0a,00 Depend30=hex:14,00 Depend50=hex:14,00,1e,00
Description The LaunchXX registry entry specifies the binary file of the application and the DependXX registry entry defines the dependencies between applications.

If you look at the Launch50 registry entry in Table 3-3, you can see that the Windows Embedded CE standard shell (Explorer.exe), will not run until process 0x14 (20) and process 0x1E (30) have started successfully, which happen to be Device Manager and GWES. The hexadecimal values in the DependXX entry refer to decimal launch numbers XX, specified in the name of the LaunchXX entries.

Implementing the SignalStarted API helps the kernel manage process dependencies between all applications registered under the HKEY_LOCAL_MACHINE\INIT registry key. The application can then use the SignalStarted function to inform the kernel that the application has started and initialization is complete, as illustrated in the following code snippet.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow) {

 // Perform initialization here...

 // Initialization complete,

 // call SignalStarted...

 SignalStarted(_wtol(lpCmdLine));

 // Perform application work and eventually exit.

 return 0;

}

Dependency handling is straightforward. The kernel determines the launch number from the Launch registry entry, uses it as a sequence identifier, and passes it as a startup parameter in lpCmdLine to the WinMain entry point. The application performs any required initialization work and then informs the kernel that it has finished this part by calling the SignalStarted function. The call to the _wtol function in the SignalStarted code line performs a conversion of the launch number from a string to a long integer value because the SignalStarted function expects a DWORD parameter. For example, Device Manager must pass a SignalStarted value of 20 and GWES must pass a value of 30 back to the kernel for the kernel to start Explorer.exe.

The Startup Folder

If you are using the standard shell on your target device, you can drop the application or a shortcut to the application into the Windows\Startup folder of the device. Explorer.exe examines this folder and starts all found applications.

NOTE

StartupProcessFolder function

Only use the Windows\Startup folder if your target device runs the Windows Embedded CE standard shell. If you are not using the standard shell, then create a custom launch application for the same purpose and initiate it at start time based on entries under the HKEY_LOCAL_MACHINE\INIT registry key. For sample code that demonstrates how to examine the Startup folder and launch the applications, find the Explorer.cpp file in the %_WINCEROOT%\Public\Shell\OAK\HPC\Explorer\Main folder. Look for a function called StartupProcessFolder and use it as a starting point for your own implementation.

The Windows Embedded CE standard shell can handle executable and shortcut files. Windows Embedded CE shortcut files differ from the shortcut files of Windows XP, but provide similar functionality. CE shortcut files are text files with an .lnk file-name extension. They contain the command-line parameters for the linked target according to the following syntax:

nn# command [optional parameters]

The placeholder nn stands for the number of characters followed by a pound sign (#), and the actual command, such as 27#\Windows\iexplore.exe -home to start Internet Explorer® and open the home page. After creating and adding the desired .lnk file to the run-time image, edit the Platform.dat or Project.dat file to map the .lnk file to the Startup folder, similar to the following .dat file entry:

Directory("\Windows\Startup"):-File("Home Page.lnk", "\Windows\homepage.lnk")

Chapter 2 covers these configuration tasks in more detail.

NOTE

Startup folder restriction

The key advantage of the Startup folder is that the applications placed in this folder do not need to implement the SignalStarted API to inform the kernel that the initialization and start process completed successfully. However, this also implies that the operating system cannot manage dependencies between applications or enforce a specific startup sequence. The operating system starts all applications in the Startup folder concurrently.

Delayed Startup

Another interesting option to start applications automatically is to leverage the services host process (Services.exe). Although Windows Embedded CE does not include a full-featured Service Control Manager (SCM), it does include built-in services and also comes with a sample service called Svcstart that can be used to start applications.

Svcstart is particularly useful for applications with dependencies on system components and services that are not immediately available after the startup process finishes. For example, it might take a few seconds to obtain an Internet Protocol (IP) address from a Dynamic Host Configuration Protocol (DHCP) server for a network interface card (NIC) or initialize a file system. To accommodate these scenarios, the Svcstart service supports a Delay parameter that specifies the time to wait before starting an application. You can find the Svcstart sample code in the %_WINCEROOT%\Public\Servers\SDK\Samples\Services\Svcstart folder. Compile the sample code into Svcstart.dll, add this DLL to your run-time image, and then run the sysgen -p servers svcstart command to register the Svcstart service with the operating system. Load it by using Services.exe.

Table 3-4 lists the registry settings that the Svcstart service supports to start applications.

Table 3-4 Svcstart registry parameters

Location HKEY_LOCAL_MACHINE\Software\Microsoft\Svcstart\1
Application Path @="iexplore.exe"
Command-line Parameters Args="-home"
Delay Time Delay=dword:4000
Description Starts the application with the specified command-line parameters after a delay time defined in milliseconds. See the Svcstart.cpp file for more details.

Windows Embedded CE Shell

By default, Platform Builder provides three shells to implement the interface between the target device and the user: the command processor shell, the standard shell, and a thin client shell. Each shell supports different features to interact with the target device.

Command Processor Shell

The command processor shell provides console input and output with a limited set of commands. This shell is available for both display-enabled devices and headless devices without keyboard and display screen. For display-enabled devices, include the Console Window component (Cmd.exe) so that the command processor shell can handle input and output through a command-prompt window. Headless devices, on the other hand, typically use a serial port for input and output.

Table 3-5 lists registry settings that you must configure on the target device to use a serial port in conjunction with the command processor shell.

Table 3-5 Console registry parameters

Location HKEY_LOCAL_MACHINE\Drivers\Console
Registry Entry OutputTo COMSpeed
Type REG_DWORD REG_DWORD
Default Value None 19600
Description Defines which serial port the command processor shell uses for input and output. Specifies the data transfer rate of the serial port in bits per second (bps).
■ Setting this value to -1 will redirect input and output to a debug port.
■ Setting this value to zero specifies no redirection.
■ Setting this value to a number greater than zero and less than 10 will redirect input and output to a serial port.

Windows Embedded CE Standard Shell

The standard shell provides a graphical user interface (GUI) similar to the Windows XP desktop. The primary purpose of the standard shell is to start and run user applications on the target device. This shell includes a desktop with Start menu and taskbar that enables the user to switch between applications, from one window to another. The standard shell also includes a system notification area to display additional information, such as the status of network interfaces and the current system time.

Windows Embedded CE Standard Shell is a required catalog item if you select the Enterprise Terminal design template when creating an OS design project in Visual Studio by using the OS Design Wizard. If you want to clone and customize this shell, you can find the source code in the %_WINCEROOT\Public\Shell\OAK\HPC folder. Chapter 1 explains how to clone catalog items and add them to an OS design.

Thin Client Shell

The thin client shell, also called the Windows-based Terminal (WBT) shell in the product documentation, is a GUI shell for thin-client devices that do not run user applications locally. You can add Internet Explorer to a thin-client OS design, yet all other user applications must run on a Terminal server in the network. The thin client shell uses the Remote Desktop Protocol (RDP) to connect to the server and display the remote Windows desktop. By default, the thin client shell displays the remote desktop in full-screen mode.

Taskman

You can also implement your own shell by cloning and customizing the Windows Task Manager (TaskMan) shell application. The source code in the %_WINCEROOT%\Public\Wceshellfe\Oak\Taskman folder is a good starting point.

Windows Embedded CE Control Panel

The Control Panel is a special repository for central access to system and application configuration tools. The product documentation refers to these configuration tools as applets, to indicate the fact that they are embedded in the Control Panel. Each applet serves a specific and targeted purpose and does not depend on other applets. You can customize the content of the Control Panel by adding your own applets or by removing existing Control Panel applets included with Windows Embedded CE.

Control Panel Components

The Control Panel is a configuration system that relies on the following three key components:

■ Front-End (Control.exe) This application displays the user interface and facilitates starting Control Panel applets.

■ Host Application (Ctlpnl.exe) This application loads and runs the Control Panel applets.

■ Applets These are the individual configuration tools, implemented in form of .cpl files listed with icon and name in the Control Panel user interface.

For details regarding the implementation of the Windows Embedded CE Control Panel, check out the source code in the %_WINCEROOT%\Public\Wceshellfe\Oak\Ctlpnl folder. You can clone the Control Panel code and customize it to implement your own Control Panel version

Implementing Control Panel Applets

As mentioned, a Control Panel applet is a configuration tool for a system component or user application implemented in form of a .cpl file and located in the Windows folder on the target device. Essentially, a .cpl file is a DLL that implements the CPlApplet API. A single .cpl file can contain multiple Control Panel applications, yet a single applet cannot span multiple .cpl files. Because all .cpl files implement the CPlApplet API, it is a straightforward process for Control.exe to obtain detailed info about the implemented applets at startup in order to display the set of available applets in the user interface. Control.exe only needs to enumerate all .cpl files in the Windows folder and to call the CPlApplet function in each file.

According to the DLL nature and CPlApplet API requirements, .cpl files must implement the following two public entry points:

■ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) Used to initialize the DLL. The system calls DllMain to load the DLL. The DLL returns true if initialization succeeded or false if initialization failed.

■ LONG CALLBACK CPlApplet(HWND hwndCPL, UINT message, LPARAM lParam1, LPARAM lParam2) A callback function that serves as the entry point for the Control Panel to perform actions on the applet.

NOTE

DLL entry points

You must export the DllMain and CPlApplet entry points so that the Control Panel application can access these functions. Non-exported functions are private to the DLL. Make sure the function definitions are in export "C" { } blocks to export the C interface.

The Control Panel calls the CPlApplet function to initialize the applet, obtain information, provide information about user actions, and to unload the applet. The applet must support several Control Panel messages, listed in Table 3-6, to implement a fully functional CPlApplet interface:

Table 3-6 Control Panel messages

Control Panel Message Description
CPL_INIT The Control Panel sends this message to perform global initialization of the applet. Memory initialization is a typical task performed at this step.
CPL_GETCOUNT The Control Panel sends this message to determine the number of Control Panel applications implemented in the .cpl file.
CPL_NEWINQUIRE The Control Panel sends this message for all the Control Panel applications specified by CPL_GETCOUNT. At this step each Control Panel application must return a NEWCPLINFO structure to specify the icon and title to display in the Control Panel user interface.
CPL_DBLCLK The Control Panel sends this message when the user double-clicks on an icon of the applet in the Control Panel user interface.
CPL_STOP The Control Panel sends this message once for each instance specified by CPL_GETCOUNT.
CPL_EXIT The Control Panel sends this message once for the applet before the system releases the DLL.

NOTE

NEWCPLINFO information

Store the NEWCPLINFO information for each Control Panel application that you implement in a Control Panel applet in a resource embedded in the .cpl file. This facilitates the localization of icons, names, and applet descriptions returned in response to CPL_NEWINQUIRE messages.

Building Control Panel Applets

To build a Control Panel applet and generate the corresponding .cpl file, find the source code folder of the applet subproject and add the following CPL build directive on a new line at the end of the Sources file:

CPL=1

You also must add the path to the Control Panel header file to the Include Directories entry on the C/C++ tab in the applet subproject settings in Visual Studio, as illustrated in Figure 3-3:

$(_PROJECTROOT)\CESysgen\Oak\Inc

Figure 3-3 Include Directories entry for a Control Panel applet

Enabling Kiosk Mode

Many Windows Embedded CE devices, such as medical monitoring devices, automated teller machines (ATM), or industrial control systems are dedicated to a single task. The standard graphical shell is not useful for these devices. Removing the standard shell restricts access to the Control Panel configuration settings and also protects users from starting additional applications. The result is a device in kiosk mode that opens an application according to the special purpose of the target device directly with no shell access.

Kiosk applications for Windows Embedded CE are developed in native code or managed code. The only requirement is to start this application in place of the standard shell (Explorer.exe). The system then starts a black shell, meaning no shell application is running on the device. You only need to configure the registry entries under the HKEY_LOCAL_MACHINE\Init key to implement this configuration. As mentioned earlier in this chapter, the LaunchXX entry for Explorer.exe is Launch50. Replace Explorer.exe with your custom kiosk application and consider the job completed, as shown in Table 3-7. Keep in mind that your custom kiosk application must implement the SignalStarted API for the kernel to manage the application dependencies correctly.

Table 3-7 Startup registry parameter examples

Location HKEY_LOCAL_MACHINE\INIT
Component Custom Kiosk Application
Binary Launch50="myKioskApp.exe"
Dependencies Depend50=hex:14,00,1e,00
Description To enable kiosk mode replace the Launch50 entry for Explorer.exe in the device registry with an entry that points to a custom kiosk application.

NOTE

Kiosk Mode for managed applications

To run a managed application in place of the standard shell, include the binary file in the runtime image and edit the .bib file that belongs to the managed application. Specifically, you must define binary files in a FILES section for the system to load the application inside the Common Language Runtime (CLR).

Lesson Summary

Windows Embedded CE is a componentized operating system with a broad palette of items and customizable features. One such feature enables you to configure automatic launching of applications at start time, which is particularly useful for installation and configuration tools. You can also customize the Control Panel by adding your own applets, implemented in custom .cpl files, which are DLLs that adhere to the CPlApplet API so that the Control Panel can call into the applets. For special-purpose devices, such as ATMs, ticket machines, medical monitoring devices, airport check-in terminals, or industrial control systems, you can further customize the user environment by replacing the standard shell with your kiosk application. You do not need to customize the code base or start process of the Windows Embedded CE operating system. Enabling kiosk mode is merely a task of replacing the default Launch50 registry entry with a custom Launch50 entry that points to your standard or managed code application.

Lesson 3: Implementing Threads and Thread Synchronization

Windows Embedded CE is a multithreaded operating system. The processing model differs from UNIX-based embedded operating systems because processes can include multiple threads. You need to know how to manage, schedule, and synchronize these threads within a single process and between processes in order to implement and debug multithreaded applications and drivers and to achieve optimal system performance on your target devices.

After this lesson, you will be able to:

■ Create and stop a thread.

■ Manage thread priorities.

■ Synchronize multiple threads.

■ Debug thread synchronization issues.

Estimated lesson time: 45 minutes.

Processes and Threads

A process is a single instance of an application. It has a processing context, which can include a virtual address space, executable code, open handles to system objects, a security context, a unique process identifier, and environment variables. It also has a primary thread of execution. A thread is the basic unit of execution managed by the scheduler. In a Windows process, a thread can create additional threads. There is no hard-coded maximum number of threads per process. The maximum number depends on available memory resources because every thread uses memory and the physical memory is limited on the platform. The maximum number of processes on Windows Embedded CE is limited to 32,000.

Thread Scheduling on Windows Embedded CE

Windows Embedded CE supports preemptive multitasking to run multiple threads from various processes simultaneously. Windows Embedded CE performs thread scheduling based on priority. Each thread on the system has a priority ranging from zero to 255. Priority zero is the highest priority. The scheduler maintains a priority list and selects the thread to run next according to the thread priority in a round-robin fashion. Threads of the same priority run sequentially in a random order. It is important to note that thread scheduling relies on a time-slice algorithm. Each thread can only run for a limited amount of time. The maximum possible time slice that a thread can run is called the quantum. Once the quantum has elapsed, the scheduler suspends the thread and resumes the next thread in the list.

Applications can set the quantum on a thread-by-thread basis to adapt thread scheduling according to application needs. However, changing the quantum for a thread does not affect threads with a higher priority because the schedule selects threads with higher priority to run first. The scheduler even suspends lower-priority threads within their time slice if a higher-priority thread becomes available to run.

Process Management API

Windows Embedded CE includes several process management functions as part of the core Win32 API. Three important functions are listed in Table 3-8 that are useful for creating and ending processes.

Table 3-8 Process management functions

Function Description
CreateProcess Starts a new process.
ExitProcess Ends a process with cleanup and unloading DLLs.
TerminateProcess Terminates a process without cleanup or unloading DLLs.

MORE INFO

Process management API

For more information about process management functions and complete API documentation, see the Core OS Reference for Windows Mobile® 6 and Windows Embedded CE 6.0, available on the Microsoft MSDN website at http://msdn2.microsoft.com/en-us/library/aa910709.aspx.

Thread Management API

Each process has at least one thread called the primary thread. This is the main thread of the process, which means that exiting or terminating this thread also ends the process. The primary thread can also create additional threads, such as worker threads, to perform parallel calculations or accomplish other processing tasks. These additional threads can create more threads if necessary by using the core Win32 API. Table 3-9 lists the most important functions to use in applications that work with threads on Windows Embedded CE.

Table 3-9 Thread management functions

Function Description
CreateThread Creates a new thread.
ExitThread Ends a thread.
TerminateThread Stops a specified thread without running cleanup or other code. Use this function only in extreme cases because terminating a thread can leave memory objects behind and cause memory leaks.
GetExitCodeThread Returns the thread exit code.
CeSetThreadPriority Sets the thread priority.
CeGetThreadPriority Gets the current thread priority.
SuspendThread Suspends a thread.
ResumeThread Resumes a suspended thread.
Sleep Suspends a thread for a specified amount of time.
SleepTillTick Suspends a thread until the next system tick.

MORE INFO

Thread management API

For more information about thread management functions and complete API documentation, see the Core OS Reference for Windows Mobile 6 and Windows Embedded CE 6.0, available on the Microsoft MSDN website at http://msdn2.microsoft.com/en-us/library/aa910709.aspx.

Creating, Exiting, and Terminating Threads

The CreateThread function used to create a new thread expects several parameters that control how the system creates the thread and the instructions that the thread runs. Although it is possible to set most of these parameters to null or zero, it is necessary to provide at least a pointer to an application-defined function that the thread is supposed to execute. This function typically defines the core processing instructions for the thread, although you can also call other functions from within this function. It is important to pass the core function as a static reference to CreateThread because the linker must be able to determine the core function's starting address at compile time. Passing a non-static function pointer does not work.

The following code listing is copied from the Explorer.cpp file that you can find in the %_WINCEROOT%\Public\Shell\OAK\HPC\Explorer\Main folder. It illustrates how to create a thread.

void DoStartupTasks() {

 HANDLE hThread = NULL;

 // Spin off the thread which registers and watches the font dirs

 hThread = CreateThread(NULL, NULL, FontThread, NULL, 0, NULL);

 if (hThread) {

  CloseHandle(hThread);

 }

 // Launch all applications in the startup folder

 ProcessStartupFolder();

}

This code specifies FontThread as the new thread's core function. It immediately closes the returned thread handle because the current thread does not need it. The new thread runs parallel to the current thread and implicitly exits upon returning from the core function. This is the preferred way to exit threads because it enables C++ function cleanup to occur. It is not necessary to explicitly call ExitThread.

However, it is possible to explicitly call the ExitThread function within a thread routine to end processing without reaching the end of the core function. ExitThread invokes the entry point of all attached DLLs with a value indicating that the current thread is detaching, and then deallocates the current thread's stack to terminate the current thread. The application process exits if the current thread happens to be the primary thread. Because ExitThread acts on the current thread, it is not necessary to specify a thread handle. However, you must pass a numeric exit code, which other threads can retrieve by using the GetExitCodeThread function. This process is useful to identify errors and reasons for the thread exiting. If ExitThread is not explicitly called, the exit code corresponds to the return value of the thread function. If GetExitCodeThread returns the value STILL_ACTIVE, the thread is still active and running.

Although you should avoid it, there can be rare situations that leave you no other way to terminate a thread except for calling the TerminateThread function. A malfunctioning thread destroying file records might require this function. Formatting a file system might need you to call TerminateThread in debugging sessions while your code is still under development. You need to pass the handle to the thread to be terminated and an exit code, which you can retrieve later by using the GetExitCodeThread function. Calling the TerminateThread function should never be part of normal processing. It leaves the thread stack and attached DLLs behind, abandons critical sections and mutexes owned by the terminated thread, and leads to memory leaks and instability. Do not use TerminateThread as part of the process shutdown procedure. Threads within the process can exit implicitly or explicitly by using the ExitThread function.

Managing Thread Priority

Each thread has a priority value ranging from zero to 255, which determines how the system schedules the thread to run in relationship to all other threads within the process and between processes. On Windows Embedded CE, the core Win32 API includes four thread management functions that set the priority of a thread as follows.

■ Base priority levels Use the SetThreadPriority and SetThreadPriority functions to manage the thread priority at levels compatible with early versions of Windows Embedded CE (zero through seven).

■ All priority levels Use the CeSetThreadPriority and CeGetThreadPriority functions to manage the thread priority at all levels (zero through 255).

NOTE

Base priority levels

The base priority levels zero through seven of earlier versions of Windows Embedded CE are now mapped to the eight lowest priority levels 248 through 255 of the CeSetThreadPriority function.

It is important to keep in mind that thread priorities define a relationship between threads. Assigning a high thread priority can be detrimental to the system if other important threads run with lower priority. You might achieve better application behavior by using a lower priority value. Performance testing with different priority values is a reliable manner of identifying the best priority level for a thread in an application or driver. However, testing 256 different priority values is not efficient. Choose an appropriate priority range for your threads according to the purpose of your driver or application as listed in Table 3-10.

Table 3-10 Thread priority ranges

Range Description
zero through 96 Reserved for real-time drivers.
97 through 152 Used by default device drivers.
153 through 247 Reserved for real-time below drivers.
248 through 255 Maps to non-real-time priorities for applications.

Suspending and Resuming Threads

It can help system performance to delay certain conditional tasks that depend on time-consuming initialization routines or other factors. After all, it is not efficient to enter a loop and check 10,000 times if a required component is finally ready for use. A better approach is to put the worker thread to sleep for an appropriate amount of time, such as 10 milliseconds, check the state of the dependencies after that time, and go back to sleep for another 10 milliseconds or continue processing when conditions permit. Use the Sleep function from within the thread itself to suspend and resume a thread. You can also use the SuspendThread and ResumeThread functions to control a thread through another thread.

The Sleep function accepts a numeric value that specifies the sleep interval in milliseconds. It is important to remember that the actual sleep interval will likely exceed this value. The Sleep function relinquishes the remainder of the current thread's quantum and the scheduler will not give this thread another time slice until the specified interval has passed and there are no other threads with higher priority. For example, the function call sleep(0) does not imply a sleep interval of zero milliseconds. Instead, sleep(0) relinquishes the remainder of the current quantum to other threads. The current thread will only continue to run if the scheduler has no other threads with the same or higher priority on the thread list.

Similar to the sleep(0) call, the SleepTillTick function relinquishes the remainder of the current thread's quantum and suspends the thread until the next system tick. This is useful if you want to synchronize a task on a system tick basis.

The WaitForSingleObject or WaitForMultipleObjects functions suspend a thread until another thread or a synchronization object is signaled. For example, a thread can wait for another thread to exit without having to enter a loop with repeated Sleep and GetExitCodeThread calls if the WaitForSingleObject function is enabled instead. This approach results in a better use of resources and improves code readability. It is possible to pass a timeout value in milliseconds to the WaitForSingleObject or WaitForMultipleObjects functions.

Thread Management Sample Code

The following code snippet illustrates how to create a thread in suspended mode, specify a thread function and parameters, change the thread priority, resume the thread, and wait for the thread to finish its processing and exit. In the last step, the following code snippet demonstrates how to check the error code returned from the thread function.

// Structure used to pass parameters to the thread.

typedef struct {

 BOOL bStop;

} THREAD_PARAM_T

// Thread function

DWORD WINAPI ThreadProc(LPVOID lpParameter) {

 // Perform thread actions...

 // Exit the thread.

 return ERROR_SUCCESS;

}

BOOL bRet = FALSE;

THREAD_PARAM_T threadParams;

threadParams.bStop = FALSE;

DWORD dwExitCodeValue = 0;

// Create the thread in suspended mode.

HANDLE hThread = CreateThread(NULL, 0, ThreadProc,

 (LPVOID) &threadParams, CREATE_SUSPENDED, NULL);

if (hThread == NULL) {

 // Manage the error...

} else {

 // Change the Thread priority.

 CeSetThreadPriority(hThread, 200);

 // Resume the thread, the new thread will run now.

 ResumeThread(hThread);

 // Perform parallel actions with the current thread...

 // Wait until the new thread exits.

 WaitForSingleObject(hThread, INFINITE);

 // Get the thread exit code

 // to identify the reason for the thread exiting

 // and potentially detect errors

 // if the return value is an error code value.

 bRet = GetExitCodeThread(hThread, &dwExitCodeValue);

 if (bRet && (ERROR_SUCCESS == dwExitCodeValue)) {

  // Thread exited without errors.

 } else {

  // Thread exited with an error.

 }

 // Don't forget to close the thread handle

 CloseHandle(hThread);

}

Thread Synchronization

The real art of multithreaded programming lies in avoiding deadlocks, protecting access to resources, and ensuring thread synchronization. Windows Embedded CE provides several kernel objects to synchronize resource access for threads in drivers or applications, such as critical sections, mutexes, semaphores, events, and interlocks functions. Yet, the choice of the object depends on the task that you want to accomplish.

Critical Sections

Critical sections are objects that synchronize threads and guard access to resources within a single process. A critical section cannot be shared between processes. To access a resource protected by a critical section, a thread calls the EnterCriticalSection function. This function blocks the thread until the critical section is available.

In some situations, blocking the thread execution might not be efficient. For example, if you want to use an optional resource that might never be available, calling the EnterCriticalSection function blocks your thread and consumes kernel resources without performing any processing on the optional resource. It is more efficient in this case to use a critical section without blocking by calling the TryEnterCriticalSection function. This function attempts to grab the critical section and returns immediately if the critical section cannot be used. The thread can then continue along an alternative code path, such as to prompt the user for input or to plug in a missing device.

Having obtained the critical section object through EnterCriticalSection or TryEnterCriticalSection, the thread enjoys exclusive access to the resource. No other thread can access this resource until the current thread calls the LeaveCriticalSection function to release the critical section object. Among other things, this mechanism highlights why you should not use the TerminateThread function to terminate threads. TerminateThread does not perform cleanup. If the terminated thread owned a critical section, the protected resource becomes unusable until the user restarts the application.

Table 3-11 lists the most important functions that you can use to work with critical section objects for thread synchronization purposes.

Table 3-11 Critical Section API

Function Description
InitializeCriticalSection Create and initialize a critical section object.
DeleteCriticalSection Destroy a critical section object.
EnterCriticalSection Grab a critical section object.
TryEnterCriticalSection Try to grab a critical section object.
LeaveCriticalSection Release a critical section object.

Mutexes

Whereas critical sections are limited to a single process, mutexes can coordinate mutually exclusive access to resources shared between multiple processes. A mutex is a kernel object that facilitates inter-process synchronization. Call the CreateMutex function to create a mutex. The creating thread can specify a name for the mutex object at creation time, although it is possible to create an unnamed mutex. Threads in other processes can also call CreateMutex and specify the same name. However, these subsequent calls do not create new kernel objects, but instead return a handle to the existing mutex. At this point, the threads in the separate processes can use the mutex object to synchronize access to the protected shared resource.

The state of a mutex object is signaled when no thread owns it and non-signaled when one thread has ownership. A thread must use one of the wait functions, WaitForSingleObject or WaitForMultipleObjects, to request ownership. You can specify a timeout value to resume thread processing along an alternative code path if the mutex does not become available during the wait interval. On the other hand, if the mutex becomes available and ownership is granted to the current thread, do not forget to call ReleaseMutex for each time that the mutex satisfied a wait in order to release the mutex object for other threads. This is important because a thread can call a wait function multiple times, such as in a loop, without blocking its own execution. The system does not block the owning thread to avoid a deadlock situation, but the thread must still call ReleaseMutex as many times as the wait function to release the mutex.

Table 3-12 lists the most important functions that you can use to work with mutex objects for thread synchronization purposes.

Table 3-12 Mutex API

Function Description
CreateMutex Create and initialize a named or unnamed mutex object. To protect resources shared between processes, you must use named mutex objects.
CloseHandle Closes a mutex handle and deletes the reference to the mutex object. All references to the mutex must be deleted individually before the kernel deletes the mutex object.
WaitForSingleObject Waits to be granted ownership of a single mutex object.
WaitForMultipleObjects Waits to be granted ownership for a single or multiple mutex objects.
ReleaseMutex Releases a mutex object.

Semaphores

Apart from kernel objects that enable you to provide mutually exclusive access to resources within a process and between processes, Windows Embedded CE also provides semaphore objects that enable concurrent access to a resource by one or multiple threads. These semaphore objects maintain a counter between zero and a maximum value to control the number of threads accessing the resource. The maximum value amount is specified in the CreateSemaphore function call.

The semaphore counter limits the number of threads that can access the synchronization object concurrently. The system will keep decrementing the counter every time a thread completes a wait for the semaphore object until the counter reaches zero and enters the nonsignaled state. The counter cannot decrement past zero. No further thread can gain access to the resource until an owning thread releases the semaphore by calling the ReleaseSemaphore function, which increments the counter by a specified value and again switches the semaphore object back into signaled state.

Similar to mutexes, multiple processes can open handles of the same semaphore object to access resources shared between processes. The first call to the CreateSemaphore function creates the semaphore object with a specified name. You can also construct unnamed semaphores, but these objects are not available for interprocess synchronization. Subsequent calls to the CreateSemaphore function with the same semaphore name do not create new objects, but open a new handle of the same semaphore.

Table 3-13 lists the most important functions that work with semaphore objects for thread synchronization purposes.

Table 3-13 Semaphore API

Function Description
CreateSemaphore Creates and initializes a named or unnamed semaphore object with a counter value. Use named semaphore objects to protect resources shared between processes.
CloseHandle Closes a semaphore handle and deletes the reference to the semaphore object. All references to the semaphore must be closed individually before the kernel deletes the semaphore object.
WaitForSingleObject Waits to be granted ownership of a single semaphore object.
WaitForMultipleObjects Waits to be granted ownership for a single or multiple semaphore objects.
ReleaseSemaphore Releases a semaphore object.

Events

The Event object is another kernel object that synchronizes threads. This object enables applications to signal other threads when a task is finished or when data is available to potential readers. Each event has signaled/non-signaled state information used by the API to identify the state of the event. Two types of events, manual events and auto-reset events, are created according to the behavior expected by the event.

The creating thread specifies a name for the event object at creation time, although it is also possible to create an unnamed event. It is possible for threads in other processes to call CreateMutex and specify the same name, but these subsequent calls do not create new kernel objects.

Table 3-14 lists the most important functions for event objects for thread synchronization purposes.

Table 3-14 Event API

Function Description
CreateEvent Creates and initializes a named or unnamed event object.
SetEvent Signal an event (see below).
PulseEvent Pulse and signal the event (see below).
ResetEvent Reset a signaled event.
WaitForSingleObject Waits for an event to be signaled.
WaitForMultipleObjects Waits to be signaled by a single or multiple event objects.
CloseHandle Releases an Event object.

The behavior of the events API is different according to the type of events. When you use SetEvent on a manual event object, the event will stay signaled until ResetEvent is explicitly called. Auto-reset events only stay signaled until a single waiting thread is released. At most, one waiting thread is released when using the PulseEvent function on auto-reset events before it immediately transitions back to the non-signaled state. In the case of manual threads, all waiting threads are released and immediately transition back to a non-signaled state.

Interlocked Functions

In multithread environments, threads can be interrupted at any time and resumed later by the scheduler. Portions of code or applications resources can be protected using semaphores, events, or critical sections. In some applications, it could be too time consuming to use those kinds of system objects to protect only one line of code like this:

// Increment variable

dwMyVariable = dwMyVariable + 1;

The sample source code above in C is one single instruction, but in assembly it could be more than that. In this particular example, the thread can be suspended in the middle of the operation and resumed later, but errors can potentially be encountered in the case of another thread using the same variable. The operation is not atomic. Fortunately, it is possible in Windows Embedded CE 6.0 R2 to increment, decrement, and add values in multithreading-safe, atomic operations without using synchronization objects. This is done by using interlocked functions.

Table 3-15 lists the most important interlocked functions that are used to atomically manipulate variables.

Table 3-15 Interlock API

Function Description
InterlockedIncrement Increment the value of a 32 bit variable.
InterlockedDecrement Decrement the value of a 32 bit variable.
InterlockedExchangeAdd Perform atomic addition on a value.

Troubleshooting Thread Synchronization Issues

Multithreaded programming enables you to structure your software solutions based on separate code execution units for user interface interaction and background tasks. It is an advanced development technique that requires careful implementation of thread synchronization mechanisms. Deadlocks can happen, especially when using multiple synchronization objects in loops and subroutines. For example, thread One owns mutex A and waits for mutex B before releasing A, while thread Two waits for mutex A before releasing mutex B. Neither thread can continue in this situation because each depends on a resource being released by the other. These situations are hard to locate and troubleshoot, particularly when threads from multiple processes are accessing shared resources. The Remote Kernel Tracker tool identifies how threads are scheduled on the system and enables you to locate deadlocks.

The Remote Kernel Tracker tool enables you to monitor all processes, threads, thread interactions, and other system activities on a target device. This tool relies on the CeLog event-tracking system to log kernel and other system events in a file named Celog.clg in the %_FLATRELEASEDIR% directory. System events are classified by zone. The CeLog event-tracking system can be configured to focus on a specific zone for data logging.

If you have Kernel Independent Transport Layer (KITL) enabled on the target device, the Remote Kernel Tracker visualizes the CeLog data and analyzes the interactions between threads and processes. This is illustrated in Figure 3-4. While KITL sends the data directly to the Remote Kernel Tracker tool, it is also possible to analyze collected data offline.

Figure 3-4 The Remote Kernel Tracker tool

MORE INFO:

CeLog event tracking and filtering

For more information about CeLog event tracking and CeLog event filtering, see the section "CeLog Event Tracking Overview" in the Windows Embedded CE 6.0 documentation available on the Microsoft MSDN website at http://msdn2.microsoft.com/en-us/library/aa935693.aspx.

Lesson Summary

Windows Embedded CE is a multithreaded operating system that provides several process management functions to create processes and threads, assign thread priorities ranging from zero through 255, suspend threads, and resume threads. The Sleep function is useful in suspending a thread for a specified period of time, but the WaitForSingleObject or WaitForMultipleObjects functions can also be used to suspend a thread until another thread or a synchronization object is signaled. Processes and threads are ended in two ways: with and without cleanup. As a general rule, always use ExitProcess and ExitThread to give the system a chance to perform cleanup. Use TerminateProcess and TerminateThread only if you have absolutely no other choice.

When working with multiple threads, it is beneficial to implement thread synchronization in order to coordinate access to shared resources within and between processes. Windows Embedded CE provides several kernel objects for this purpose, specifically critical sections, mutexes, and semaphores. Critical sections guard access to resources within a single process. Mutexes coordinate mutually exclusive access to resources shared among multiple processes. Semaphores implement concurrent access by multiple threads to resources within a process and between processes. Events are used to notify the other threads, and interlocked functions for the manipulation of variables in a thread-safe atomic way. If you happen to encounter thread synchronization problems during the development phase, such as deadlocks, use the CeLog event-tracking system and Remote Kernel Tracker to analyze the thread interactions on the target device.

EXAM TIP

To pass the certification exam, make sure you understand how to use the various synchronization objects in Windows Embedded CE 6.0 R2.

Lesson 4: Implementing Exception Handling

Target devices running Windows Embedded CE include exceptions as part of system and application processing. The challenge is to respond to exceptions in the appropriate manner. Handling exceptions correctly ensures a stable operating system and positive user experience. For example, instead of unexpectedly terminating a video application, you might find it more useful to prompt the user to connect a Universal Serial Bus (USB) camera if the camera is currently disconnected. However, you should not use exception handling as a universal solution. Unexpected application behavior can be the result of malicious code tampering with executables, DLLs, memory structures, and data. In this case, terminating the malfunctioning component or application is the best course of action to protect the data and the system.

After this lesson, you will be able to:

■ Understand the reason for exceptions.

■ Catch and throw exceptions.

Estimated lesson time: 30 minutes.

Exception Handling Overview

Exceptions are events resulting from error conditions. These conditions can arise when the processor, operating system, and applications are executing instructions outside the normal flow of control in kernel mode and user mode. By catching and handling exceptions, you can increase the robustness of your applications and ensure a positive user experience. Strictly speaking, however, you are not required to implement exception handlers in your code because structured exception handling is an integral part of Windows Embedded CE.

The operating system catches all exceptions and forwards them to the application processes that caused the events. If a process does not handle its exception event, the system forwards the exception to a postmortem debugger and eventually terminates the process in an effort to protect the system from malfunctioning hardware or software. Dr. Watson is a common postmortem debugger that creates a memory dump file for Windows Embedded CE.

Exception Handling and Kernel Debugging

Exception handling is also the basis for kernel debugging. When you enable kernel debugging in an operating system design, Platform Builder includes the kernel debugging stub (KdStub) in the run-time image to enable components that raise exceptions to break into the debugger. Now you can analyze the situation, step through the code, resume processing, or terminate the application process manually. However, you need a KITL connection to a development workstation in order to interact with the target device. Without a KITL connection, the debugger ignores the exception and lets the application continue to run so that the operating system can use another exception handler as if no debugger was active. If the application does not handle the exception, then the operating system gives the kernel debugger a second chance to perform postmortem debugging. In this context, it is often called just in time (JIT) debugging. The debugger must now accept the exception and waits for a KITL connection to become available for the debug output. Windows Embedded CE waits until you establish the KITL connection and start debugging the target device. Developer documentation often uses the terms first-chance exception and second-chance exception because the kernel debugger has two chances to handle an exception in this scenario, but they are, in fact, referring to the same exception event. For more information about debugging and system testing, read Chapter 5, "Debugging and Testing the System."

Hardware and Software Exceptions

Windows Embedded CE uses the same structured exception handling (SEH) approach for all hardware and software exceptions. The central processing unit (CPU) can raise hardware exceptions in response to invalid instruction sequences, such as division by zero or an access violation caused by an attempt to access an invalid memory address. Drivers, system applications, and user applications, on the other hand, can raise software exceptions to invoke the operating system's SEH mechanisms by using the RaiseException function. For example, you can raise an exception if a required device is not accessible (such as a USB camera or a database connection), if the user specified an invalid command-line parameter, or for any other reason that requires you to run special instructions outside the normal code path. You can specify several parameters in the RaiseException function call to specify information that describes the exception. This specification can then be used in the filter expression of an exception handler.

Exception Handler Syntax

Windows Embedded CE supports frame-based structured exception handling. It is possible to enclose a sensitive sequence of code in braces ({}) and mark it with the __try keyword to indicate that any exceptions during the execution of this code should invoke an exception handler that follows in a section marked by using the __except keyword. The C/C++ compiler included in Microsoft Visual Studio supports these keywords and compiles the code blocks with additional instructions that enable the system either to restore the machine state and continue thread execution at the point at which the exception occurred, or to transfer control to an exception handler and continue thread execution in the call stack frame in which the exception handler is located.

The following code fragment illustrates how to use the __try and __except keywords for structured exception handling:

__try {

 // Place guarded code here.

} __except (filter-expression) {

 // Place exception-handler code here.

}

The __except keyword supports a filter expression, which can be a simple expression or a filter function. The filter expression can evaluate to one of the following values:

■ EXCEPTION_CONTINUE_EXECUTION The system assumes that the exception is resolved and continues thread execution at the point at which the exception occurred. Filter functions typically return this value after handling the exception to continue processing as normal.

■ EXCEPTION_CONTINUE_SEARCH The system continues its search for an appropriate exception handler.

■ EXCEPTION_EXECUTE_HANDLER The system thread execution continues sequentially from the exception handler rather than from the point of the exception.

NOTE

Exception handling support

Exception handling is an extension of the C language, but it is natively supported in C+ + .

Termination Handler Syntax

Windows Embedded CE supports termination handling. As a Microsoft extension to the C and C++ languages, it enables you to guarantee that the system always runs a certain block of code not matter how the flow of control leaves the guarded code block. This code section is called a termination handler, and is used to perform cleanup tasks even if an exception or some other error occurs in the guarded code. For example, you can use a termination handler to close thread handles that are no longer needed.

The following code fragment illustrates how to use the __try and __finally keywords for structured exception handling:

__try {

 // Place guarded code here.

} __finally {

 // Place termination code here.

}

Termination handling supports the __leave keyword within the guarded section. This keyword ends thread execution at the current position in the guarded section and resumes thread execution at the first statement in the termination handler without unwinding the call stack.

NOTE

Using __try, __except, and __finally blocks

A single __try block cannot have both an exception handler and a termination handler. If you must use both __except and __finally, use an outer try-except statement and an inner try-finally statement.

Dynamic Memory Allocation

Dynamic memory allocation is an allocation technique that relies on structured exception handling to minimize the total number of committed memory pages on the system. This is particularly useful if you must perform large memory allocations. Precommitting an entire allocation can cause the system to run out of committable pages and result in virtual memory allocation failures.

The dynamic memory allocation technique is as follows:

1. Call VirtualAlloc with a base address of NULL to reserve a block of memory. The system reserves this memory block without committing the pages.

2. Try to access a memory page. This raises an exception because you cannot read from or write to a non-committed page. This illegal operation results in a page fault exception. Figure 3-5 and Figure 3-6 show the outcome of an unhandled page fault in an application called PageFault.exe.

3. Implement an exception handler based on a filter function. Commit a page in the filter function from the reserved region. If successful, return EXCEPTION_CONTINUE_EXECUTION to continue thread execution in the __try block at the point where the exception occurred. If the page allocation failed, return EXCEPTION_EXECUTE_HANDLER to invoke the exception handler in the __except block and release the entire region of reserved and committed pages.

Figure 3-5 An unhandled page fault exception from a user's perspective

Figure 3-6 An unhandled page fault exception's debug output over KITL in Visual Studio 2005

The following code snippet illustrates the dynamic memory allocation technique based on page fault exception handling:

#define PAGESTOTAL 42 // Max. number of pages

LPTSTR lpPage;    // Page to commit

DWORD dwPageSize; // Page size, in bytes

INT ExceptionFilter(DWORD dwCode) {

 LPVOID lpvPage;

 if (EXCEPTION_ACCESS_VIOLATION != dwCode) {

  // This is an unexpected exception!

  // Do not return EXCEPTION_EXECUTE_HANDLER

  // to handle this in the application process.

  // Instead, let the operating system handle it.

  return EXCEPTION_CONTINUE_SEARCH;

 }

 // Allocate page for read/write access.

 lpvPage = VirtualAlloc((LPVOID) lpPage,

 dwPageSize, MEM_COMMIT, PAGE_READWRITE);

 if (NULL == lpvPage) {

  // Continue thread execution

  // in __except block.

  return EXCEPTION_EXECUTE_HANDLER;

 }

 // Set lpPage to the next page.

 lpPage = (LPTSTR) ((PCHAR) lpPage + dwPageSize);

 // Continue thread execution in __try block.

 return EXCEPTION_CONTINUE_EXECUTION;

}

VOID DynamicVirtualAlloc() {

 LPVOID lpvMem;

 LPTSTR lpPtr;

 DWORD i;

 BOOL bRet;

 // Get page size on computer.

 SYSTEM_INFO sSysInfo;

 GetSystemInfo(&sSysInfo);

 dwPageSize = sSysInfo.dwPageSize;

 // Reserve memory pages without committing.

 lpvMem = VirtualAlloc(NULL, PAGESTOTAL*dwPageSize,

  MEM_RESERVE, PAGE_NOACCESS);

 lpPtr = lpPage = (LPTSTR) lpvMem;

 // Use structured exception handling when accessing the pages.

 for (i=0; i < PAGESTOTAL*dwPageSize; i++) {

  __try {

   // Write to memory.

   lpPtr[i] = 'x';

  } __except (ExceptionFilter(GetExceptionCode())) {

   // Filter function unsuccessful. Abort mission.

   ExitProcess( GetLastError() );

  }

 }

 // Release the memory.

 bRet = VirtualFree(lpvMem, 0, MEM_RELEASE);

}

Lesson Summary

Windows Embedded CE supports exception handling and termination handling natively. Exceptions in the processor, operating system, and applications are raised in response to improper instruction sequences, attempts to access an unavailable memory address, inaccessible device resources, invalid parameters, or any other operation that requires special processing, such as dynamic memory allocations. You can use try-except statements to react to error conditions outside the normal flow of control and try-finally statements to run code no matter how the flow of control leaves the guarded __try code block.

Exception handling supports filtering expressions and filtering functions, which enable you to control how you respond to raised events. It is not advisable to catch all exceptions because unexpected application behavior can be the result of malicious code. Only handle those exceptions that need to be dealt with directly for reliable and robust application behavior. The operating system can forward any unhandled exceptions to a postmortem debugger to create a memory dump and terminate the application.

EXAM TIP

To pass the certification exam, make sure you understand how to use exception handling and termination handling in Windows Embedded CE 6.0 R2.

Lesson 5: Implementing Power Management

Power management is essential for Windows Embedded CE devices. By lowering power consumption, you extend battery life and ensure a long-lasting, positive user experience. This is the ultimate goal of power management on portable devices. Stationary devices also benefit from power management. Regardless of equipment size, you can reduce operating costs, heat dissipation, mechanical wear and tear, and noise levels if you switch the device into a low-power state after a period of inactivity. And of course, implementing effective power-management features helps to lessen the burden on our environment.

After this lesson, you will be able to:

■ Enable power management on a target device.

■ Implement power-management features in applications.

Estimated lesson time: 40 minutes.

Power Manager Overview

On Windows Embedded CE, Power Manager (PM.dll) is a kernel component that integrates with the Device Manager (Device.exe) to implement power management features. Essentially, Power Manager acts as a mediator between the kernel, OEM adaptation layer (OAL), and drivers for peripheral devices and applications. By separating the kernel and OAL from drivers and applications, drivers and applications can manage their own power state separately from the system state. Drivers and applications interface with Power Manager to receive notifications about power events and to perform power management functions. Power Manager has the ability to set the system power state in response to events and timers, control driver power states, and respond to OAL events that require a power state change, such as when the battery power state is critical.

Power Manager Components and Architecture

Power Manager exposes a notification interface, an application interface, and a device interface according to its tasks. The notification interface enables applications to receive information about power management events, such as when the system state or a device power state changes. In response to these events, power management-enabled applications use the application interface to interact with Power Manager to communicate their power management requirements or change the system power state. The device interface, on the other hand, provides a mechanism to control the power level of device drivers. Power Manager can set device power states separately from the system power state. Similar to applications, device drivers may use the driver interface to communicate their power requirements back to Power Manager. The important point is that Power Manager and the Power Manager APIs centralize power management on Windows Embedded CE, as illustrated in Figure 3-7.

Figure 3-7 Power Manager and power management interaction

Power Manager Source Code

Windows Embedded CE comes with source code for Power Manager, which is found in the %_WINCEROOT%\Public\Common\Oak\Drivers\Pm folder on your development computer. Customizing this code provides personalized power handling mechanisms on a target device. For example, an Original Equipment Manufacturer (OEM) can implement additional logic to shut down special components before calling the PowerOffSystem function. See Chapter 1, "Customizing the Operating System Design" for techniques to clone and customize standard Windows Embedded CE components.

Driver Power States

Applications and device drivers are able to use the DevicePowerNotify function to control the power state of peripheral devices. For instance, you can call DevicePowerNotify to inform Power Manager that you want to change the power level of a backlight driver, such as BLK1:. Power Manager expects you to specify the desired power state at the one of the following five different power levels, according to the hardware device capabilities:

■ D0 Full On; the device is fully functional.

■ D1 Low On; the device is functional, but the performance is reduced.

■ D2 Standby; the device is partially powered and will wake up on requests.

■ D3 Sleep; the device is partially powered. In this state the device still has power and can raise interrupts that will wake up the CPU (device-initiated wakeup).

■ D4 Off; device has no power. The device should not consume any significant power in this state.

NOTE

CE device power state levels

The device power states (D0 through D4) are guidelines to help OEMs implement power management functions on their platforms. Power Manager does not impose restrictions on device power consumption, responsiveness, or capabilities in any of the states. As a general rule, higher-numbered states should consume less power than lower numbered states and power states D0 and D1 should be for devices perceived as operational from the perspective of the user. Device drivers that manage the power level of a physical device with fewer granularities can implement a subset of the power states. DO is the only required power state.

System Power States

In addition to sending power-state change notifications to device drivers in response to application and device driver requests, Power Manager can also transition the power state of the entire system in response to hardware-related events and software requests. Hardware events enable Power Manager to respond to low and critical battery levels and transitions from battery power to AC power. Software requests enable applications to request a change of the system power state in a call to Power Manager's SetSystemPowerState function.

The default Power Manager implementation supports the following four system power states:

■ On The system is fully operational and on full power.

■ UserIdle The user is passively using the device. There was no user input for a configurable period of time.

■ SystemIdle The user is not using the device. There was no system activity for a configurable period of time.

■ Suspend The device is powered down, but supports device-initiated wakeup.

It is important to keep in mind that system power states depend on the requirements and capabilities of the target device. OEMs can define their own or additional system power states, such as InCradle and OutOfCradle. Windows Embedded CE does not impose a limit on the number of system power states that can be defined, but all system power states eventually translate into one of the device power states, mentioned earlier in this lesson.

Figure 3-8 illustrates the relationship between the default system power states and the device power states.

Figure 3-8 Default system power states and associated device power states

Activity Timers

System state transitions are based on activity timers and corresponding events. If a user is not using the device, a timer eventually expires and raises an inactivity event, which in turn causes Power Manager to transition the system into Suspend power state. When the user returns and interacts with the system again by providing input, an activity event occurs causing Power Manager to transition the system back into an On power state. However, this simplified model does not take into account prolonged periods of user activity without input, such as a user watching a video clip on a personal digital assistant (PDA). This simplified model also does not take into account target devices without any direct user input methods, as in the case of display panels. To support these scenarios, the default Power Manager implementation distinguishes between user activity and system activity and accordingly transitions the system power state, as illustrated in Figure 3-9.

Figure 3-9 Activity timers, events, and system power state transitions

To configure system-activity and user-activity timeouts, use the Power Control Panel applet. You can also implement additional timers and set their timeouts by editing the registry directly. Windows Embedded CE does not limit the number of timers you can create. At startup, Power Manager reads the registry keys, enumerates the activity timers, and creates the associated events. Table 3-16 lists the registry settings for the SystemActivity timer. OEMs can add similar registry keys and configure these values for additional timers.

Table 3-16 Registry settings for activity timers

Location HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power\ActivityTimers\SystemActivity
Entry Timeout WakeSources
Type REG_DWORD REG_MULTI_SZ
Value A (10 minutes) 0x20
Description The Timeout registry entry defines the timer threshold in minutes. The WakeSources registry entry is optional and defines a list of identifiers for possible wake sources. During device-initiated wakeup, Power Manager uses the IOCTL_HAL_GET_WAKE_SOURCE input and output control (IOCTL) code to determine the wake source and sets associated activity timers to active.

NOTE

Activity timers

Defining activity timers causes the Power Manager to construct a set of named events for resetting the timer and for obtaining activity status. For more information, see the section "Activity Timers" in the Windows Embedded CE 6.0 Documentation available on the Microsoft MSDN website at http://msdn2.microsoft.com/en-us/library/aa923909.aspx.

Power Management API

As mentioned earlier in this lesson, Power Manager exposes three interfaces to enable applications and drives for power management: notification interface, driver interface, and application interface.

Notification Interface

The notification interface provides two functions that applications can use to register and deregister for power notifications through message queues, as listed in Table 3-17. It is important to note that power notifications are multicast messages, which means that Power Manager sends these notification messages only to registered processes. In this way, power management-enabled applications can seamlessly coexist on Windows Embedded CE with applications that do not implement the Power Management API.

Table 3-17 Power Management notification interface

Function Description
RequestPowerNotifications Registers an application process with Power Manager to receive power notifications. Power Manager then sends the following notification messages:
■ PBT_RESUME The system resumes from Suspend state.
■ PBT_POWERSTATUSCHANGE The system transitions between AC power and battery power.
■ PBT_TRANSITION The system changes to a new power state.
■ PBT_POWERINFOCHANGE The battery status changes. This message is only valid if a battery driver is loaded.
StopPowerNotifications Unregisters an application process so it no longer receives power notifications.

The following sample code illustrates how to use power notifications:

// Size of a POWER_BROADCAST message.

DWORD cbPowerMsgSize =

 sizeof POWER_BROADCAST + (MAX_PATH * sizeof TCHAR);

// Initialize a MSGQUEUEOPTIONS structure.

MSGQUEUEOPTIONS mqo;

mqo.dwSize = sizeof(MSGQUEUEOPTIONS);

mqo.dwFlags = MSGQUEUE_NOPRECOMMIT;

mqo.dwMaxMessages = 4;

mqo.cbMaxMessage = cbPowerMsgSize;

mqo.bReadAccess = TRUE;

//Create a message queue to receive power notifications.

HANDLE hPowerMsgQ = CreateMsgQueue(NULL, &mqo);

if (NULL == hPowerMsgQ) {

 RETAILMSG(1, (L"CreateMsgQueue failed: %x\n", GetLastError()));

 return ERROR;

}

// Request power notifications.

HANDLE hPowerNotifications = RequestPowerNotifications(hPowerMsgQ,

 PBT_TRANSITION | PBT_RESUME | PBT_POWERINFOCHANGE);

// Wait for a power notification or for the app to exit.

while(WaitForSingleObject(hPowerMsgQ, FALSE, INFINITE) == WAIT_OBJECT_0) {

 DWORD cbRead;

 DWORD dwFlags;

 POWER_BROADCAST *ppb = (POWER_BROADCAST*) new BYTE[cbPowerMsgSize];

 // Loop through in case there is more than 1 msg.

 while(ReadMsgQueue(hPowerMsgQ, ppb, cbPowerMsgSize, &cbRead, 0, &dwFlags)) {

  // Perform action according to the message type.

 }

}

Device Driver Interface

In order to integrate with the Power Manager, device drivers must support a set of I/O controls (IOCTLs). Power Manager uses these to query device-specific power capabilities as well as to set and change the device's power state, as illustrated in Figure 3-10. Based on the Power Manager IOCTLs, the device driver should put the hardware device into a corresponding power configuration.

Figure 3-10 Power Manager and device driver interaction

Power Manager uses the following IOCTLs to interact with device drivers:

■ IOCTL_POWER_CAPABILITIES Power Manager checks the power management capabilities of the device driver. The returned information should reflect the capabilities of the hardware and the driver managing the hardware device. The driver must return only supported Dx states.

■ IOCTL_POWER_SET Power Manager forces the driver to switch to a specified Dx state. The driver must perform the power transition.

■ IOCTL_POWER_QUERY Power Manger checks to see if the driver is able to change the state of the device.

■ IOCTL_POWER_GET Power Manager wants to determine the current power state of the device.

■ IOCTL_REGISTER_POWER_RELATIONSHIP Power Manager notifies a parent driver to register all child devices that it controls. Power Manager sends this IOCTL only to devices that include the POWER_CAP_PARENT flag in the Flags member of the POWER_CAPABILITIES structure.

NOTE

Internal power state transitions

To ensure reliable power management, device drivers should not change their own internal power state without the involvement of Power Manager. If a driver requires a power state tran­sition, the driver should use the DevicePowerNotify function to request the power state change. The driver can then change its internal power state when Power Manager sends a power state change request back to the driver.

Application Interface

The application interface provides functions that applications can use to manage the power state of the system and of individual devices through Power Manager. Table 3-18 summarizes these power management functions.

Table 3-18 Application interface

Function Description
GetSystemPowerState Retrieves the current system power state.
SetSystemPowerState Requests a power state change. When switching to Suspend mode, the function will return after the resume because suspend is transparent to the system. After the resume, you can analyze the notification message to identify that the system resumed from suspend.
SetPowerRequirement Requests a minimal power state for a device.
ReleasePowerRequirement Releases a power requirement previously set with the SetPowerRequirement function and restores the original device power state.
GetDevicePower Retrieves the current power state of a specified device.
SetDevicePower Requests a power state change for a device.

Power State Configuration

As illustrated in Figure 3-8, Power Manager associates system power states with device power states to keep system and devices synchronized. Unless configured otherwise, Power Manager enforces the following default system-state-to-device-state mappings: On = D0, UserIdle = D1, SystemIdle = D2, and Suspend = D3. Overriding this association for individual devices and device classes can be accomplished by means of explicit registry settings.

Overriding the Power State Configuration for an Individual Device

The default Power Manager implementation maintains system-state-to-device-state mappings in the registry under the HKEY_LOCAL_MACHINE\System\CurrentControlSet\State key. Each system power state corresponds to a separate subkey and you can create additional subkeys for OEM-specific power states.

Table 3-19 shows a sample configuration for the system power state On. This configuration causes Power Manager to switch all devices, except the backlight driver BLK1: driver, into the D0 device power state. The backlight driver BLK1: can only go to the D2 state.

Table 3-19 Default and driver-specific power state definitions for system power state On

Location HKEY_LOCAL_MACHINE\System\CurrentControlSet\State\On
Entry Flags Default BKL1:
Type REG_DWORD REG_DWORD REG_DWORD
Value 0x00010000 (POWER_STATE_ON) 0 (D0) 2 (D2)
Description Identifies the system power state associated with this registry key. For a list of possible flags, see the Pm.h header file in the Public\Common\Sdk\Inc folder. Sets the power state for drivers by default to the D0 state when the system power state is On. Sets the backlight driver BLK1: to the D2 state when the system power state is On.

Overriding the Power State Configuration for Device Classes

Defining device power state for multiple system power states individually can be a tedious task. Power Manager facilitates the configuration by supporting device classes based on IClass values, which can be used to define the power management rules. The following three default class definitions are found under the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power\Interfaces registry key.

■ {A3292B7-920C-486b-B0E6-92A702A99B35} Generic power management-enabled devices.

■ {8DD679CE-8AB4-43c8-A14A-EA4963FAA715} Power-management-enabled block devices.

■ {98C5250D-C29A-4985-AE5F-AFE5367E5006} Power-management-enabled Network Driver Interface Specification (NDIS) miniport drivers.

Table 3-20 shows a sample configuration for the NDIS device class, which specifies that NDIS drivers only go as high as the D4 state.

Table 3-20 Sample power state definition for NDIS device class

Location HKEY_LOCAL_MACHINE\SystemCurrentControlSet\Control\Power\State\On\{98C5250D-C29A-4985-AE5F-AFE5367E5006}
Entry Default
Type REG_DWORD
Value 4 (D4)
Description Sets the device power state for NDIS drivers to the D4 state when the system power state is On.

Processor Idle State

In addition to power-management-enabled applications and device drivers, the kernel also contributes to power management. The kernel calls the OEMIdle function, part of the OAL, when no threads are ready to run. This action switches the processor into idle state, which includes saving the current context, placing the memory into a refresh state, and stopping the clock. The processor idle state reduces power consumption to the lowest possible level while retaining the ability to return from the idle state quickly.

It is important to keep in mind that the OEMIdle function does not involve Power Manager. The kernel calls the OEMIdle function directly and it is up to the OAL to switch the hardware into an appropriate idle or sleep state. The kernel passes a DWORD value (dwReschedTime) to OEMIdle to indicate the maximum period of idle time. When this time passes or the maximum delay supported by the hardware timer is reached, the processor switches back to non-idle mode, the previous state is restored, and the scheduler is invoked. If there still is no thread ready to run, the kernel immediately calls OEMIdle again. Driver events, as in the response to user input via keyboard or stylus, may occur at any time and cause the system to stop idling before the system timer starts.

The scheduler is, by default, based on a static timer and system ticks at a one-millisecond frequency. However, the system can optimize power consumption by using dynamic timers and by setting the system timer to the next timeout identified by using the scheduler table content. The processor will then not switch back out of idle mode with every tick. Instead, the processor switches only to non-idle mode after the timeout defined by dwReschedTime expires or an interrupt occurres.

Lesson Summary

Windows Embedded CE 6.0 R2 provides a default Power Manager implementation with a set of power-management APIs that you can use to manage the power state of the system and its devices. It also provides the OEMIdle function, which it executes when the system does not have any threads scheduled in order to provide original equipment manufacturers (OEMs) a chance to put the system into a low power idle state for a specified period of time.

Power Manager is a kernel component that exposes a notification interface, an application interface, and a device interface. It acts as a mediator between kernel and OAL on one side and device drivers and applications on the other side. Applications and device drivers can use the DevicePowerNotify function to control the power state of peripheral devices at five different power levels. Device power states may also associate with default and custom system power states to keep system and devices synchronized. Based on activity times and corresponding events, Power Manger can automatically perform system state transitions. The four default system power states are On, UserIdle, SystemIdle, and Suspend. Customizations for system-state-to- device-state mapping take place in the registry settings of individual devices and device classes.

In addition to Power Manager, the kernel supports power management by means of the OEMIdle function. Switching the processor into idle state reduces power consumption to the lowest possible level while retaining the ability to return from the idle state quickly. The processor will return to non-idle state periodically or when interrupts occur, as when it responds to user input or when a device requests access to memory for data transfer.

You can significantly reduce the power consumption of a device if you implement power management properly using Power Manager and OEMIdle, thereby increasing battery life, decreasing operating costs, and extending device lifetime.

Lab 3: Kiosk Mode, Threads, and Power Management

In this lab, you develop a kiosk application and configure a target device to run this application instead of the standard shell. You then extend this application to run multiple threads in parallel in the application process and analyze the thread execution by using the Remote Kernel Tracker tool. Subsequently, you enable this application for power management.

NOTE

Detailed step-by-step instructions

To help you successfully master the procedures presented in this lab, see the document "Detailed Step-by-Step Instructions for Lab 3" in the companion material for this book.

► Create a Thread

1. Using the New Project Wizard, create a new WCE Console Application named HelloWorld. Use the Typical Hello_World Application option.

2. Before the _tmain function, implement a thread function named ThreadProc:

DWORD WINAPI ThreadProc( LPVOID lpParameter) {

 RETAILMSG(1,(TEXT("Thread started")));

 // Suspend Thread execution for 3 seconds

 Sleep(3000);

 RETAILMSG(1,(TEXT("Thread Ended")));

 // Return code of the thread 0,

 // usually used to indicate no errors.

 return 0;

}

3. By using the CreateThread function, start a thread:

HANDLE hThread = CreateThread( NULL, 0, ThreadProc, NULL, 0, NULL);

4. Check the returned value of CreateThread to verify that the thread was created successfully.

5. Wait for the thread to reach the end of the thread function and exit:

WaitForSingleObject(hThread, INFINITE);

6. Build the run-time image and download it to the target device.

7. Launch Remote Kernel Tracker and analyze how threads are managed on the system.

8. Start the HelloWorld application and follow the thread execution in the Remote Kernel Tracker window, as illustrated in Figure 3–11.

Figure 3-11 Tracking thread execution in Remote Kernel Tracker tool

► Enable Power Management Notification Messages

1. Continue to use the HelloWorld application in Visual Studio.

2. Generate power-management notifications in more frequent intervals by going into the subproject registry settings and setting the registry entry for the UserIdle timeout in AC power mode (ACUserIdle) to five seconds:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\Timeouts]

 "ACUserIdle"=dword:5 ; in seconds

3. In the ThreadProc function, create a message queue object:

// Size of a POWER_BROADCAST message.

DWORD cbPowerMsgSize =

 sizeof POWER_BROADCAST + (MAX_PATH * sizeof TCHAR);

// Initialize our MSGQUEUEOPTIONS structure.

MSGQUEUEOPTIONS mqo;

mqo.dwSize = sizeof(MSGQUEUEOPTIONS);

mqo.dwFlags = MSGQUEUE_NOPRECOMMIT;

mqo.dwMaxMessages = 4;

mqo.cbMaxMessage = cbPowerMsgSize;

mqo.bReadAccess = TRUE;

//Create a message queue to receive power notifications.

HANDLE hPowerMsgQ = CreateMsgQueue(NULL, &mqo);

if (NULL == hPowerMsgQ) {

 RETAILMSG(1, (L"CreateMsgQueue failed: %x\n", GetLastError()));

 return -1;

}

4. Request to receive notifications from Power Manager and check the received messages:

// Request power notifications

HANDLE hPowerNotifications = RequestPowerNotifications(hPowerMsgQ,

 PBT_TRANSITION | PBT_RESUME | PBT_POWERINFOCHANGE);

DWORD dwCounter = 20;

// Wait for a power notification or for the app to exit

while(dwCounter-- &&

 WaitForSinglObject(hPowerMsgQ, INFINITE) == WAIT_OBJECT_0) {

 DWORD cbRead;

 DWORD dwFlags;

 POWER_BROADCAST *ppb =

  (POWER_BROADCAST*) new BYTE[cbPowerMsgSize];

 // loop through in case there is more than 1 msg.

 while(ReadMsgQueue(hPowerMsgQ, ppb, cbPowerMsgSize,

  &cbRead, 0, &dwFlags)) {

  switch(ppb->Message) {

  case PBT_TRANSITION: {

   RETAILMSG(1,(L"Notification: PBT_TRANSITION\n"));

   if(ppb->Length) {

    RETAILMSG(1,(L"SystemPowerState: %s\n", ppb->SystemPowerState));

   }

   break;

  }

  case PBT_RESUME: {

   RETAILMSG(1,(L"Notification: PBT_RESUME\n"));

   break;

  }

  case PBT_POWERINFOCHANGE: {

   RETAILMSG(1,(L"Notification: PBT_POWERINFOCHANGE\n"));

   break;

  }

  default:

   break;

  }

 }

 delete[] ppb;

}

5. Build the application and rebuild the run-time image.

6. Start the run-time image.

7. You generate user activity by moving the mouse cursor. After five seconds of inactivity, Power Manager should notify the application, as illustrated in Figure 3-12.

Figure 3-12 Received Power Management notifications

► Enable Kiosk Mode

1. Create a WCE Application named Subproject_Shell using the Subproject Wizard. Use the Typical Hello_World Application option.

2. Before the first LoadString line, add a SignalStarted instruction.

// Initialization complete,

// call SignalStarted...

SignalStarted(_wtol(lpCmdLine));

3. Build the application.

4. Add a registry key in the subproject .reg file to launch the application at startup. Add the following lines, which create the corresponding Launch99 and Depend99 entries:

[HKEY_LOCAL_MACHINE\INIT]

"Launch99"="Subproject_Shell.exe"

"Depend99"=hex:14,00,1e,00

5. Build and start the run-time image.

6. Verify that the Subproject_Shell application starts automatically.

7. Replace the reference to Explorer.exe in the Launch50 registry key with a reference to the Subproject_Shell application, as follows:

[HKEY_LOCAL_MACHINE\INIT]

"Launch50"="Subproject_Shell.exe"

"Depend50"=hex:14,00,1e,00

8. Build and start the run-time image.

9. Verify that the target device runs the Subproject_Shell application in place of the standard shell, as illustrated in Figure 3-13.

Figure 3-13 Replacing the standard shell with a Subproject_Shell application

Chapter Review

Windows Embedded CE provides a variety of tools, features, and APIs that you can use to ensure optimal system performance and power consumption on your target devices. Performance tools, such as ILTiming, OSBench, and Remote Performance Monitor, can help identify performance issues within and between drivers, applications, and OAL code, such as deadlocks or other issues related to thread synchronization. The Remote Kernel Tracker enables you to examine process and thread execution in great detail, while relying on structured exception handling, which Windows Embedded CE supports natively.

Windows Embedded CE is a componentized operating system. You can include or exclude optional components and even replace the standard shell with a custom application. Replacing the standard shell with an application configured to start automatically lays the groundwork for enabling a kiosk configuration. Windows Embedded CE runs with a black shell in a kiosk configuration, meaning that the user cannot start or switch to other applications on your device.

Regardless of the shell, you can implement power management functions in your device drivers and applications to control energy usage. The default Power Manager implementation covers the typical needs, but OEMs with special requirements add custom logic. The Power Manager source code is included with Windows Embedded CE. The power management framework is flexible and supports any number of custom system power states that can map device power states by means of registry settings.

Key Terms

Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book.

■ ILTiming

■ Kiosk Mode

■ Synchronization Objects

■ Power Manager

■ RequestDeviceNotifications

Suggested Practices

Complete the following tasks to help you successfully master the exam objectives presented in this chapter:

Use the ILTiming and OSBench Tools

Use Iltiming and OSBench on the emulator device to examine the emulated ARMV4 processor's performance.

Implement a Custom Shell

Customize the look and feel of the target device by using Task Manager, included in Windows Embedded CE with source code, to replace the shell.

Experiment with Multithreaded Applications and Critical Sections

Use critical section objects in a multithreaded application to protect access to a global variable. Complete the following tasks:

1. Create two threads in the main code of the applications and in the thread functions wait two seconds (Sleep(2000)) and three seconds (Sleep(3000)) in an infinite loop. The primary thread of the application should wait until both threads exit by using the WaitForMultipleObjects function.

2. Create a global variable and access it from both threads. One thread should write to the variable and the other thread should read the variable. By accessing the variable before and after the first Sleep and displaying the values, you should be able visualize concurrent access.

3. Protect access to the variable by using a CriticalSection object shared between both threads. Grab the critical section at the beginnings of the loops and release it at the ends of the loops. Compare the results with the previous output.

Chapter 4

Debugging and Testing the System

Debugging and system testing are vital tasks during the software-development cycle, with the ultimate goal to identify and solve software-related and hardware-related defects on a target device. Debugging generally refers to the process of stepping through the code and analyzing debug messages during code execution in order to diagnose root causes of errors. It can also be an efficient tool to study the implementation of system components and applications in general. System testing, on the other hand, is a quality-assurance activity to validate the system in its final configuration in terms of typical usage scenarios, performance, reliability, security, and any other relevant aspects. The overall purpose of system testing is to discover product defects and faults, such as memory leaks, deadlocks, or hardware conflicts, whereas debugging is a means to get to the bottom of these problems and eliminate them. For many developers of small-footprint and consumer devices, locating and eliminating system defects is the hardest part of software development, with a measurable impact on productivity. This chapter covers the debugging and testing tools available in Microsoft® Visual Studio® 2005 with Platform Builder for Microsoft Windows® Embedded CE 6.0 R2 and in the Windows Embedded CE Test Kit (CETK) to help you automate and accelerate these processes so that you can release your systems faster and with fewer bugs. The better you master these tools, the more time you can spend writing code instead of fixing code.

Exam objectives in this chapter:

■ Identifying requirements for debugging a run-time image

■ Using debugger features to analyze code execution

■ Understanding debug zones to manage the output of debug messages

■ Utilizing the CETK tool to run default and user-defined tests

■ Debugging the boot loader and operating system (OS)

Before You Begin

To complete the lessons in this chapter, you must have the following:

■ At least some basic knowledge about Windows Embedded CE software development and debugging concepts.

■ A basic understanding of the driver architectures supported in Windows Embedded CE.

■ Familiarity with OS design and system configuration concepts.

■ A development computer with Microsoft Visual Studio 2005 Service Pack 1 and Platform Builder for Windows Embedded CE 6.0 R2 installed.

Lesson 1: Detecting Software-Related Errors

Software-related errors range from simple typos, uninitialized variables, and infinite loops to more complex and profound issues, such as critical race conditions and other thread synchronization problems. Fortunately, the vast majority of errors are easy to fix once they are located. The most cost-effective way to find these errors is through code analysis. You can use a variety of tools on Windows Embedded CE devices to debug the operating system and step through drivers and applications. A good understanding of these debugging tools will help you accelerate your code analysis so that you can fix software errors as efficiently as possible.

After this lesson, you will be able to:

■ Identify important debugging tools for Windows Embedded CE.

■ Control debug messages through debug zones in drivers and applications.

■ Use the target control shell to identify memory issues.

Estimated lesson time: 90 minutes.

Debugging and Target Device Control

The primary tool to debug and control a Windows Embedded CE target device is by using Platform Builder on the development workstation, as illustrated in Figure 4-1. The Platform Builder integrated development environment (IDE) includes a variety of tools for this purpose, such as a system debugger, CE target control shell (CESH), and debug message (DbgMsg) feature, that you can use to step through code after reaching a breakpoint or to display information on memory, variables, and processes. Moreover, the Platform Builder IDE includes a collection of remote tools, such as Heap Walker, Process Viewer, and Kernel Tracker, to analyze the state of the target device at run time.

Figure 4-1 CE debugging and target control architecture

In order to communicate with the target device, Platform Builder relies on the Core Connectivity (CoreCon) infrastructure and debugging components deployed on the target device as part of the run-time image. The CoreCon infrastructure provides OS Access (OsAxS), target control, and DbgMsg services to Platform Builder on one side, and interfaces with the target device through the Kernel Independent Transport Layer (KITL) and the bootstrap service on the other side. On the target device itself, the debugging and target control architecture relies on KITL and the boot loader for communication purposes. If the run-time image includes debugging components, such as the kernel debugger stub (KdStub), hardware debugger stub (HdStub), and the OsAxS library, you can use Platform Builder to obtain kernel run-time information and perform just-in-time (JIT) debugging. Platform Builder also supports hardware- assisted debugging through Extended Debugging Interface (eXDI), so that you can debug target device routines prior to loading the kernel.

Kernel Debugger

The Kernel Debugger is the CE software-debugging engine to debug kernel components and CE applications. On the development workstation, you work directly in Platform Builder, such as to insert or remove breakpoints in the source code and run the application, yet you must include support for KITL and debugging libraries (KdStub and OsAxS) in the run-time image so that Platform Builder can capture debugging information and control the target device. Lesson 2, "Configuring the Run-Time Image to Enable Debugging," later in this chapter provides detailed information about system configurations for kernel debugging.

The following target-side components are essential for kernel debugging:

■ KdStub Captures exceptions and breakpoints, retrieves kernel information, and performs kernel operations.

■ OsAxS Retrieves information about the state of the operating system, such as information about memory allocations, active processes and threads, proxies, and loaded dynamic-link libraries (DLLs).

NOTE

Application debugging in Windows Embedded CE

By using the Kernel Debugger, you can control the entire run-time image as well as individual applications. However, KdStub is a kernel component that receives first-chance and second-chance exceptions, as explained in Chapter 3, "Performing System Programming." If you stop the Kernel Debugger during a session without stopping the target-side KdStub module first and an exception occurs, the run-time image stops responding until you reconnect the debugger, because the Kernel Debugger must handle the exception so that the target device can continue to run.

Debug Message Service

In Platform Builder, when you attach to a KITL-enabled and KdStub-enabled target device, you can examine debug information in the Output window of Microsoft Visual Studio 2005, which Platform Builder obtains from the target device by using the DbgMsg service in the CoreCon infrastructure. Debug messages provide detailed information about the running processes, signal potentially critical issues, such as invalid input, and give hints about the location of a defect in the code that you can then study further by setting a breakpoint and stepping through the code in Kernel Debugger. One of the kernel debugger stub's features is support for dynamic management of debug messages, so you can configure the debugging verbosity without source code modifications. Among other things, you can exclude Timestamps, Process IDs, or Thread IDs, if you display the Debug Message Options window that you can reach through the Target menu in Visual Studio. You can also send the debug output to a file for analysis in a separate tool. On the target device, all debug messages are sent directly to the default output stream handled through the NKDbgPrintf function.

NOTE

Debug messages with and without KITL

When both Kernel Debugger and KITL are enabled, the debug messages are displayed in the Output window of Visual Studio. If KITL is not available, the debug information is transferred from the target device to the development computer over a serial port configured and used by the OEM adaptation layer (OAL).

Macros for Debug Messages

To generate debug information, Windows Embedded CE provides several debugging macros that generally fall into two categories, debug macros and retail macros. Debug macros output information only if the code is compiled in the debug build configuration (environment variable WINCEDEBUG=debug), while retail macros generate information in both debug and retail build configurations (WINCEDEBUG=retail) unless you build the run-time image in ship configuration (WINCESHIP=1). In ship configuration, all debugging macros are disabled.

Table 4-1 summarizes the debugging macros that you can insert in your code to generate debug information.

Table 4-1 Windows Embedded CE macros to output debugging messages

Macro Description
DEBUGMSG Conditionally prints a printf-style debug message to the default output stream (that is, the Output window in Visual Studio or a specified file) if the run-time image is compiled in debug build configuration.
RETAILMSG Conditionally prints a printf-style debug message to the default output stream (that is, the Output window in Visual Studio or a specified file) if the run-time image is compiled in debug or release build configuration, yet not in ship build configuration.
ERRORMSG Conditionally prints additional printf-style debug information to the default output stream (that is, the Output window in Visual Studio or a specified file) if the run-time image is compiled in debug or release build configuration, yet not in ship build con­figuration. This error information includes the name of the source code file and the line number, which can help to quickly locate the line of code that generated the message.
ASSERTMSG Conditionally prints a printf-style debug message to the default output stream (that is, the Output window in Visual Studio or a specified file) and then breaks into the debugger, if the run-time image is compiled in debug configuration. In fact, ASSERTMSG calls DEBUGMSG followed by DBGCHK.
DEBUGLED Conditionally passes a WORD value to the WriteDebugLED function, if the run-time image is compiled in debug build configuration. This macro is useful on devices that provide only light-emitting diodes (LEDs) to indicate the system status and requires an implementation of the OEMWriteDebugLED function in the OAL.
RETAILLED Conditionally passes a WORD value to the WriteDebugLED function, if the run-time image is compiled in debug or release build configuration. This macro is useful on devices that provide only LEDs to indicate the system status and requires an implementation of the OEMWriteDebugLED function in the OAL.

Debug Zones

Debug messages are particularly useful tools to analyze multi-threaded processes, especially thread synchronization and other timing issues that are difficult to detect by stepping through the code. However, the number of debug messages generated on a target device can be overwhelming if you heavily use debugging macros in your code. To control the amount of information generated, debugging macros enable you to specify a conditional expression. For example, the following code outputs an error message if the dwCurrentIteration value is greater than the maximum possible value.

ERRORMSG(dwCurrentIteration > dwMaxIteration,

 (TEXT("Iteration error: the counter reached %u, when max allowed is %u\r\n"),

 dwCurrentIteration, dwMaxIteration));

In the example above, ERRORMSG outputs debugging information whenever dwCurrentIteration is greater than dwMaxIteration. You can also control debugging messages by using debug zones in the conditional statement. This is particularly useful if you want to use the DEBUGMSG macro to examine code execution in a module (that is, an executable file or a DLL) at varying levels without changing and recompiling the source code each time. First, you must enable debug zones in your executable file or DLL, and register a global DBGPARAM variable with the Debug Message service to specify which zones are active. You can then specify the current default zone programmatically or through registry settings on the development workstation or the target device. It is also possible to control debug zones dynamically for a module in Platform Builder via CE Debug Zones on the Target menu or in the Target Control window.

TIP

Bypassing debug zones

You can bypass debug zones in drivers and applications if you pass a Boolean variable to the DEBUGMSG and RETAILMSG macros that you can set to TRUE or FALSE when you rebuild the run-time image.

Zones Registration

To use debug zones, you must define a global DBGPARAM variable with three fields that specify the module name, the names of the debug zones you want to register, and a field for the current zone mask, as summarized in Table 4-2.

Table 4-2 DBGPARAM elements

Field Description Example
lpszName Defines the name of the module with a maximum length of 32 characters. TEXT("ModuleName")
rglpszZones Defines an array of 16 names for the debug zones. Each name can be up to 32 characters long. Platform Builder displays this information to the user when selecting the active zones in the module. { TEXT("Init"), TEXT("Deinit"), TEXT("On"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"), TEXT("Failure"), TEXT("Warning"), TEXT("Error") }
ulZoneMask The current zone mask used in the DEBUGZONE macro to determine the currently selected debug zone. MASK_INIT | MASK_ON | MASK_ERROR

NOTE

Debug zones

Windows Embedded CE supports a total of 16 named debug zones, yet not all have to be defined if the module doesn’t require them. Each module uses a separate set of zone names that should clearly reflect the purpose of each implemented zone.

The Dbgapi.h header file defines the DBGPARAM structure and debugging macros. Because these macros use a predefined DBGPARAM variable named dpCurSettings, it is important that you use the same name in your source code as well, as illustrated in the following code snippet.

#include <DBGAPI.H>

// A macro to increase the readability of zone mask definitions

#define DEBUGMASK(n) (0x00000001<<n)

// Definition of zone masks supported in this module

#define MASK_INIT    DEBUGMASK(0)

#define MASK_DEINIT  DEBUGMASK(1)

#define MASK_ON      DEBUGMASK(2)

#define MASK_FAILURE DEBUGMASK(13)

#define MASK_WARNING DEBUGMASK(14)

#define MASK_ERROR   DEBUGMASK(15)

// Definition dpCurSettings variable with the initial debug zone state

// set to Failure, Warning, and Error.

DBGPARAM dpCurSettings = {

 TEXT("ModuleName"), // Specify the actual module name for clarity!

 {

  TEXT("Init"), TEXT("Deinit"), TEXT("On"), TEXT("Undefined"),

  TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"),

  TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"),

  TEXT("Undefined"), TEXT("Undefined"), TEXT("Undefined"),

  TEXT("Failure"), TEXT("Warning"), TEXT("Error")

 },

 MASK_INIT | MASK_ON | MASK_ERROR

};

// Main entry point into DLL

BOOL APIENTRY DllMain( HANDLE hModule,

 DWORD ul_reason_for_call, LPVOID lpReserved) {

 if (ul_reason_for_call == DLL_PROCESS_ATTACH) {

  // Register with the Debug Message service each time

  // the DLL is loaded into the address space of a process.

  DEBUGREGISTER((HMODULE)hModule);

 }

 return TRUE;

}

Zone Definitions

The sample code above registers six debug zones for the module that you can now use in conjunction with conditional statements in debugging macros. The following line of code shows one possible way to do this:

DEBUGMSG(dpCurSettings.ulZoneMask & (0x00000001<<(15)),

 (TEXT("Error Information\r\n")));

If the debug zone is currently set to MASK_ERROR, the conditional expression evaluates to TRUE and DEBUGMSG sends the information to the debug output stream. However, to improve the readability of your code, you should use the DEBUGZONE macro defined in Dbgapi.h, as illustrated in the following code snippet, to define flags for your zones. Among other things, this approach simplifies the combination of debug zones through logical AND and OR operations.

#include <DBGAPI.H>

// Definition of zone flags: TRUE or FALSE according to selected debug zone.

#define ZONE_INIT    DEBUGZONE(0)

#define ZONE_DEINIT  DEBUGZONE(1)

#define ZONE_ON      DEBUGZONE(2)

#define ZONE_FAILURE DEBUGZONE(13)

#define ZONE_WARNING DEBUGZONE(14)

#define ZONE_ERROR   DEBUGZONE(15)

DEBUGMSG(ZONE_FAILURE, (TEXT("Failure debug zone enabled.\r\n")));

DEBUGMSG(ZONE_FAILURE && ZONE_ WARNING,

 (TEXT("Failure and Warning debug zones enabled.\r\n")));

DEBUGMSG(ZONE_FAILURE || ZONE_ ERROR,

 (TEXT("Failure or Error debug zone enabled.\r\n")));

Enabling and Disabling Debug Zones

The DBGPARAM field ulZoneMask is the key to setting the current debug zone for a module. You can accomplish this programmatically in the module by changing the ulZoneMask value of the global dpCurSettings variable directly. Another option is to change the ulZoneMask value in the debugger at a breakpoint within the Watch window. You can also control the debug zone through another application by calling the SetDbgZone function. Another option available at run time is to use the Debug Zones dialog box, illustrated in Figure 4-2, which you can display in Visual Studio with Platform Builder via the CE Debug Zones command on the Target menu.

Figure 4-2 Setting debug zones in Platform Builder

The Name list shows the modules running on the target device that support debug zones. If the selected module is registered with the Debug Message service, you can find the list of 16 zones displayed under Debug Zones. The names correspond to the selected module's dpCurSettings definition. You can select or deselect zones to enable or disable them. By default, the zones defined in the dpCurSettings variable are enabled and checked in the Debug Zones list. For modules not registered with the Debug Message service, the Debug Zone list is deactivated and unavailable.

Overriding Debug Zones at Startup

Windows Embedded CE enables the zones you specify in the dpCurSettings variable when you start the application or load the DLL into a process. At this point, it is not yet possible to change the debug zone unless you set a breakpoint and change the ulZoneMask value in the Watch window. However, CE supports a more convenient method through registry settings. To facilitate loading a module with different active debug zones, you can create a REG_DWORD value with a name that corresponds to the module name specified in the lpszName field of the dpCurSettings variable and set it to the combined values of the debug zones you want to activate. You can configure this value on the development workstation or the target device. It is generally preferable to configure this value on the development workstation because changing target device registry entries requires you to rebuild the run-time image, whereas a modification of the registry entries on the development workstation only requires you to restart the affected modules.

Table 4-3 illustrates the configuration for a sample module called ModuleName. Make sure you replace this placeholder name with the actual name of your executable file or DLL.

Table 4-3 Startup registry parameter examples

Location Development Workstation Target Device
Registry Key HKEY_CURRENT_USER\Pegasus\Zones HKEY_LOCAL_MACHINE\DebugZones
Entry Name ModuleName ModuleName
Type REG_DWORD REG_DWORD
Value 0x00000001-0x7FFFFFFF 0x00000001-0x7FFFFFFF
Comments The Debug Message system uses the target-side value for a module only if the development workstation is unavailable or if the development-side registry does not contain a value for the module.

NOTE

Enabling all debug zones

Windows Embedded CE uses the lower 16 bits of the REG_DWORD value to define named debug zones for application debugging purposes. The remaining bits are available to define unnamed debug zones, with the exception of the highest bit, which is reserved for the kernel. Therefore, you should not set a module's debug zone value to 0xFFFFFFFF. The maximum value is 0x7FFFFFFF, which enables all named and unnamed debug zones.

MORE INFO

Pegasus registry key

The name Pegasus refers to the code name of the first Windows CE version that Microsoft released for handheld personal computers and other consumer electronics in 1996.

Best Practices

When working with debug messages, keep in mind that heavy use of debug messages slows down code execution. Perhaps even more important, the system serializes the debug output operations, which can provide an unintentional thread synchronization mechanism. For example, multiple threads running unsynchronized in release builds might cause issues not noticeable in debug builds.

When working with debug messages and debug zones, consider the following best practices:

■ Use Conditional statements Use debug macros with conditional statements based on debug zones. Do not use DEBUGMSG(TRUE). Also avoid using retail macros without conditional statements, such as RETAILMSG(TRUE), although some model device driver (MDD)/platform dependent driver (PDD) drivers must use this technique.

■ Exclude debugging code from release builds If you only use debug zones in debug builds, include the global variable dpCurSettings and zone mask definitions in #ifdef DEBUG #endif guards and restrict the use of debug zones to debug macros (such as DEBUGMSG).

■ Use retail macros in release builds If you also want to use debug zones in release builds, include the global variable dpCurSettings and zone mask definitions in #ifndef SHIP_BUILD #endif guards and replace the call to DEBUGREGISTER with a call to RETAILREGISTERZONES.

■ Clearly identify the module name If possible, set the dpCurSettings.lpszName value to the module's file name.

■ Limit verbosity by default Set the default zones for your drivers to ZONE_ERROR and ZONE_WARNING only. When bringing up a new platform, enable ZONE_INIT.

■ Restrict the error debug zone to unrecoverable issues Use ZONE_ERROR only when your module or significant functionality fails due to incorrect configuration or other issues. Use ZONE_WARNING for recoverable issues.

■ Eliminate all errors and warnings if possible Your module should be able to load without any ZONE_ERROR or ZONE_WARNING messages.

Target Control Commands

The Target Control service provides access to a command shell for the debugger to transfer files to the target device and debug applications. This target control shell, displayed in Figure 4-3, is accessible from within Visual Studio with Platform Builder via the Target Control option on the Target menu. However, it is important to keep in mind that the target control shell is only available if the Platform Builder instance is attached to a device through KITL.

Figure 4-3 The target control shell

Among other things, the target control shell enables you to perform the following debugging actions:

■ Break into the Kernel Debugger (break command).

■ Send a memory dump to the debug output (dd command) or to a file (df command).

■ Analyze memory usage for the kernel (mi kernel command) or the entire system (mi full command).

■ List processes (gi proc command), threads (gi thrd command), and thread priorities (tp command), as well as the modules loaded on the system (gi mod command).

■ Launch processes (s command) and end processes (kp command).

■ Dump the processes heap (hp command).

■ Enable or disable the system profiler (prof command).

NOTE

Target control commands

For a complete list of target control commands, see the section "Target Control Debugging Commands" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN® Web site at http://msdn2.microsoft.com/en-us/library/aa936032.aspx.

Debugger Extension Commands (CEDebugX)

In addition to the regular debugger commands, the Target Control service provides the debugger with a debugger commands extension (CEDebugX) to increase the efficiency of kernel and application debugging. This extension provides additional features to detect memory leaks and deadlocks and diagnose the overall health of the system. The additional commands are accessible through the target control shell and start with an exclamation point (!).

To use CEDebugX, you need to break into the Kernel Debugger by using the break command in the target control shell or the Break All command on the Target menu in Visual Studio. Among other things, you can then enter a !diagnose all command to identify the potential reason for a failure, such as heap corruption, deadlocks, or memory starvation. On a healthy system, the CEDebugX should detect no any issues, as illustrated in Figure 4-4.

Figure 4-4 Diagnosing a run-time image with CEDebugX

The !diagnose all command runs the following diagnostics:

■ Heap Diagnoses all the heap objects of all processes on the system to identify potential content corruption.

■ Exception Diagnoses an exception that occurs on the system and is able to provide details on the exception, such as process and thread ID, and PC address at the exception time.

■ Memory Diagnoses the system memory to identify potential memory corruptions and low memory conditions.

■ Deadlock Diagnoses the thread states and system objects (see Chapter 3 for more details on thread synchronization). It can provide a list of system objects and thread IDs that generated the deadlock.

■ Starvation Diagnoses threads and system objects to identify potential thread starvation. Starvation occurs when a thread is never scheduled on the system by the scheduler because the scheduler is busy with higher-priority threads.

Advanced Debugger Tools

The target control shell and CEDebugX commands enable you to perform a thorough analysis of a running system or a CE dump file (if you select the CE Dump File Reader as the debugger to perform postmortem debugging), yet you are not restricted to the command-line interface. Platform Builder includes several graphical tools with a dedicated purpose to increase your debugging efficiency. You can access these advanced debugger tools in Visual Studio via the Debug menu when you open the Windows submenu.

The Platform Builder IDE includes the following advanced debugger tools:

■ Breakpoints Lists the breakpoints enabled on the system and provides access to the breakpoint properties.

■ Watch Provides read and write access to local and global variables.

■ Autos Provides access to variables similar to the Watch window, except that the debugger creates this list of variables dynamically, while the Watch window lists all manually added variables whether they are accessible or not. The Autos window is useful if you want to check the parameter values passed to a function.

■ Call Stack Accessible only when the system is in a break state (code execution has halted on a breakpoint). This window provides a list of all processes enabled on the system and a list of hosted threads.

■ Threads Provides a list of the threads running in the processes on the system. This information is dynamically retrieved and can be updated at any time.

■ Modules Lists the modules loaded and unloaded on the system and provides the memory address where those modules are loaded. This feature is useful for identifying whether a driver DLL is actually loaded or not.

■ Processes Similar to the Threads window, this window provides a list of the processes running on the system. Among other things, you can terminate processes if required.

■ Memory Provides direct access to device memory. You can use memory addresses or variable names to locate the desired memory content.

■ Disassembly Reveals the assembly code of the current code line executed on the system.

■ Registers Provides access to the CPU register values when running a specific line of code.

■ Advanced Memory Can be used to search the device memory, move portions of memory to different sections, and fill memory ranges by using content patterns.

■ List Nearest Symbols Determines a specific memory address for the nearest symbols available in the binaries. It also provides the complete path to the file containing the symbol. This tool is useful to locate the name of a function that generated an exception.

CAUTION

Memory corruption

The Memory and Advanced Memory tools can modify memory content. Using these tools incorrectly can cause system failures and damage the operating system on the target device.

Application Verifier Tool

Another useful tool to identify potential application compatibility and stability issues and necessary source code-level fixes is the Application Verifier tool, included in the CETK. This tool can attach to an application or a DLL to diagnose problems that are otherwise difficult to track on standalone devices. The Application Verifier tool does not require a device connection to a development workstation and can be launched at system startup to check and validate drivers and system applications. You can also start this tool from the CETK user interface or manually on the target device. If you want to use the Application Verifier tool outside of the CETK, you should use the Getappverif_cetk.bat file to copy all the required files into the release directory.

NOTE

Application Verifier tool documentation

For detailed information about the Application Verifier tool, including how to use shim extension DLLs to run custom test code or change the behavior of functions during application testing, see the section "Application Verifier Tool" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa934321.aspx.

CeLog Event Tracking and Processing

Windows Embedded CE includes an extensible event-tracking system that you can include in a run-time image to diagnose performance problems. The CeLog event-tracking system logs a set of predefined kernel and coredll events related to mutexes, events, memory allocation, and other kernel objects. The extensible architecture of the CeLog event-tracking system also enables you to implement custom filters to track user-defined events. For platforms connected to a development workstation through KITL, the CeLog event-tracking system can selectively log events based on zones specified in the ZoneCE registry entry, as summarized in Table 4-4.

Table 4-4 CeLog registry parameters for event logging zones

Location HKEY_LOCAL_MACHINE\System\CELog
Registry Entry ZoneCE
Entry Type REG_DWORD
Value <Zone IDs>
Description By default, all zones are logged. For a list of all possible zone ID values, see the section "CELog Zones" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa909194.aspx.

By using the CeLog event-tracking system, you can collect data, which CeLog stores in a buffer in RAM on the target device. Performance tools, such as Remote Kernel Tracker and Readlog, can then process the collected data. It is also possible to flush the data periodically to a file by using the CELogFlush tool.

NOTE

CELog and ship builds

You should not include the CeLog event-tracking system in final builds to avoid performance and memory penalties due to CeLog activities, and to reduce the attack surface through which a malicious user could try to compromise the system.

Remote Kernel Tracker

The Remote Kernel Tracker tool enables you to monitor system activities on a target device based on processes and threads. This tool can display information from the target device in real time through KITL, yet it is also possible to use Remote Kernel Tracker offline based on CeLog data files. You can find more information about the Remote Kernel Tracker tool in Chapter 3, "Performing System Programming."

Figure 4-5 shows Kernel Tracker on a target device collecting information about thread activities.

Figure 4-5 Thread information in Kernel Tracker

CeLogFlush Tool

To create CeLog data files, use the CeLogFlush tool to save the CeLog event data buffered in RAM into a .clg file. This file can be located in the RAM file system, persistent storage, or the release file system on a development workstation. To minimize data loss due to buffer overruns, you can specify a larger RAM buffer and increase the frequency at which CeLog flushes the buffer. You can optimize the performance if you keep the file open to avoid repeated file open and close operations and store the file in the RAM file system instead of a slower persistent storage medium.

NOTE

CELogFlush configuration

For detailed information about the CeLogFlush tool, including how to configure this tool through registry settings, see the section "CeLogFlush Registry Settings" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa935267.aspx.

Readlog Tool

In addition to the graphical Remote Kernel Tracker application, you can process CELog data files by using the Readlog tool, located in the %_WINCEROOT%\Public\Common\Oak\Bin\i386 folder. Readlog is a command-line tool to process and display information not exposed in Remote Kernel Tracker, such as debug messages and boot events. It is often useful to analyze system activities in Remote Kernel Tracker first and then focus on an identified process or thread with the Readlog tool. The raw data that the CeLogFlush tool writes into the .clg file is ordered by zones to facilitate locating and extracting specific information. You can also filter the data and extend filtering capabilities based on extension DLLs to process custom data captured through the custom events collector.

One of the most useful Readlog scenarios is to replace thread start addresses (the functions passed to the CreateThread call) in CeLog data files with the names of the actual thread functions to facilitate system analysis in Remote Kernel Tracker. To accomplish this task, you must start Readlog with the -fixthreads parameter (readlog -fixthreads). Readlog looks up the symbol .map files in the release directory to identify the thread functions based on the start addresses and generates new logs with the corresponding references.

Figure 4-6 shows CeLog data in Remote Kernel Tracker, captured through the CeLog event-tracking system, flushed to a .clg file with the CeLogFlush tool, and prepared for a more user-friendly display of the information by using the Readlog application with the -fixthreads parameter.

Figure 4-6 A CeLog data file prepared with readlog -fixthreads and opened in Remote Kernel Tracker

NOTE

Improving reference naming matching

The CeLog event-tracking system can take advantage of the kernel profiler to look up thread function names based on start addresses when capturing CreateThread events, if you explicitly enable the kernel profiler and add profiling symbols to the run-time image by rebuilding the image with the IMGPROFILER environment variable set. However, CeLog can only look up the profiling symbols built into the run-time image. Symbols of applications developed based on a Software Development Kit (SDK) are generally unavailable to the CeLog event-tracking system.

Lesson Summary

Debugging an operating system and applications requires familiarity with both the CE system and the debugging tools included in Platform Builder and CETK. The most important debugging tools are the system debugger, debug message feature, and CE target control shell. The system debugger enables you to set breakpoints and step through kernel and application code, while the debug message feature provides the option to analyze system components and applications without interrupting code execution. A variety of debug and retail macros are available to output debugging information from target devices with or without a display component. Because systems and applications can potentially generate a large number of debug messages, you should use debug zones to control the output of debugging information. The key advantage of debug zones is that you can change the debug information verbosity dynamically without having to rebuild the run-time image. The target control shell, on the other hand, enables you to send commands to the target device, such as a break command followed by a !diagnose all command to break into the debugger and perform a CEDebugX check on the overall system health, including memory leaks, exceptions, and deadlocks.

Apart from these core debugging tools, you can use typical CE configuration and troubleshooting tools, such as the Application Verifier tool, to identify potential application compatibility and stability issues, and Remote Kernel Tracker to analyze processes, threads, and system performance. Remote Kernel Tracker relies on the CeLog event-tracking system, which typically maintains logged data in memory on the target device; you can also flush this data to a file by using the CeLogFlush tool. If symbol files are available for the modules that you want to analyze, you can use the Readlog tool to replace the thread start addresses with the actual function names and generate new CeLog data files for more convenient offline analysis in Remote Kernel Tracker.

Lesson 2: Configuring the Run-Time Image to Enable Debugging

The debugging features of Windows Embedded CE rely on development workstation components and the target device, and require specific settings and hardware support. Without a connection between the development workstation and the target device, debug information and other requests cannot be exchanged. If this communication link breaks — for example, because you stop the debugger on the development workstation without first unloading the target-side debugging stub — the run-time image might stop responding to user input while waiting for the debugger to resume code execution after an exception occurred.

After this lesson, you will be able to:

■ Enable the Kernel Debugger for a run-time image.

■ Identify the KITL requirements.

■ Use the Kernel Debugger in a debugging context.

Estimated lesson time: 20 minutes.

Enabling the Kernel Debugger

As discussed in Lesson 1, the development environment for Windows Embedded CE 6.0 includes a Kernel Debugger that enables developers to step through and interact with code running on a CE target device. This debugger requires you to set kernel options and a communication layer between the target device and the development computer.

OS Design Settings

To enable an OS design for debugging, you must unset the environment variables IMGNODEBUGGER and IMGNOKITL so that Platform Builder includes the KdStub library and enables the KITL communication layer in the Board Support Package (BSP) when building the run-time image. Platform Builder provides a convenient method to accomplish this task. In Visual Studio, right-click the OS design project and select Properties to display the OS Design property pages dialog box, switch to the Build Options pane, and then select the Enable Kernel Debugger and Enable KITL check boxes. Chapter 1, "Customizing the Operating System Design," discusses the OS Design property pages dialog box in more detail.

Selecting a Debugger

Having enabled KdStub and KITL for a run-time image, you can select a debugger to analyze the system on the target device in the communication parameters for your target device. To configure these parameters, display the Target Device Connectivity Options dialog box in Visual Studio by opening the Target menu and selecting Connectivity Options, as explained in Chapter 2, "Building and Deploying a Run­Time Image."

By default, no debugger is selected in the connectivity options. You have the following debugger choices:

■ KdStub This is the software debugger for the kernel and applications to debug system components, drivers, and applications running on a target device. KdStub requires KITL to communicate with Platform Builder.

■ CE Dump File Reader Platform Builder provides you with an option to capture dump files, which you can then open by using the CE dump-file reader. Dump files enable you to study the state of a system at a particular point in time and are useful as references.

■ Sample Device Emulator eXDI 2 Driver KdStub cannot debug routines that the system runs prior to loading the kernel, nor can it debug interrupt service routines (ISRs), because this debugging library relies on software breakpoints. For hardware-assisted debugging, Platform Builder includes a sample eXDI driver that you can use in conjunction with a joint test action group (JTAG) probe. The JTAG probe enables you to set hardware breakpoints handled by the processor.

NOTE

Hardware-assisted debugging

For detailed information about hardware-assisted debugging, see the section "Hardware-assisted Debugging" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa935824.aspx.

KITL

As illustrated in Figure 4-1 at the beginning of this chapter, KITL is an essential communication layer between the development computer and the target device and must be enabled for Kernel Debugger support. As the name implies, KITL is completely hardware independent and works over network connections, serial cables, Universal Serial Bus (USB), or any other supported communication mechanism, such as Direct Memory Access (DMA). The only requirement is that both sides (development computer and target device) support and use the same interface. The most common and fastest KITL interface for the device emulator is DMA, as illustrated in Figure 4-7. For target devices with a supported Ethernet chip, it is typically best to use the network interface.

Figure 4-7 Configuring the KITL communication interface

KITL supports the following two methods of operation:

■ Active mode By default, Platform Builder configures KITL to connect to the development computer during the start process. This setting is most useful for kernel and application debugging during the software-development cycle.

■ Passive mode By clearing the check box Enable KITL on Device Boot, you can configure KITL for passive mode, meaning Windows Embedded CE initializes the KITL interface, but KITL does not establish a connection during the startup process. If an exception occurs, KITL makes an attempt to establish a connection to the development computer so that you can perform JIT debugging. Passive mode is most useful when working with mobile devices that do not have a physical connection to the development computer at startup.

NOTE

KITL modes and boot arguments

The Enable KITL on Device Boot setting is a boot argument (BootArgs) that Platform Builder configures for the boot loader. For more information about boot loaders and their advantages during the BSP development process, see the section "Boot Loaders" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa917791.aspx.

Debugging a Target Device

It is important to keep in mind that development-side and target-side debugger components run independently of each other. For example, it is possible to run the Kernel Debugger in Visual Studio 2005 with Platform Builder without having an active target device. If you open the Debug menu and click Start or press the F5 key, the Kernel Debugger starts and informs you in the Output window that it is waiting for a connection to the target device. On the other hand, if you start a debugging- enabled run-time image without an active KITL connection to a debugger and an exception occurs, the run-time image appears to hang because the system halts, waiting for control requests from the debugger, as mentioned earlier in this chapter. For this reason, the debugger typically starts automatically when you attach to a debugging-enabled target device. Instead of pressing F5 to start a debugging session, use Attach Device on the Target menu.

Enabling and Managing Breakpoints

The debugging features of Platform Builder provide most of the functionality also found in other debuggers for Windows desktop applications. You can set breakpoints, step through the code line-by-line, and use the Watch window to view and change variable values and object properties, as illustrated in Figure 4-8. Keep in mind, however, that the ability to use breakpoints depends on the existence of the KdStub library in the run-time image.

Figure 4-8 Debugging a Hello World application

To set a breakpoint, use the Toggle Breakpoint option on the Debug menu in Visual Studio. Alternatively, you can press F9 to set a breakpoint at the current line or click the left margin area of the code line. According to your selection, Platform Builder indicates the breakpoint with a red dot or a red circle, depending on whether the debugger can instantiate the breakpoint or not. The red circle indicates an un-instantiated breakpoint. Un-instantiated breakpoints occur if the Visual Studio instance is not linked to the target code, the breakpoint is set but has not yet been loaded, the debugger is not enabled, or if the debugger is running but code execution has not yet halted. If you set a breakpoint while the debugger is running, then the device must break into the debugger first before the debugger can instantiate the breakpoint.

You have the following options to manage breakpoints in Visual Studio with Platform Builder:

■ Source code, Call Stack, and Disassembly windows You can set, remove, enable, or disable a breakpoint by either pressing F9 and selecting Toggle

Breakpoint from the Debug menu or selecting Insert/Remove Breakpoint from the context menu.

■ New Breakpoint dialog box You can display this dialog box via the submenus available under New Breakpoint on the Debug menu. The New Breakpoint dialog box enables you to set breakpoints by location and conditions. The debugger stops at a conditional breakpoint only if the specified condition evaluates to TRUE, such as when a loop counter or other variable has a specific value.

■ Breakpoints window You can display the Breakpoints window by clicking Breakpoints under the Windows submenu on the Debug menu, or by pressing Alt+F9. The Breakpoints window lists all set breakpoints and enables you to configure breakpoint properties. For example, instead of using the New Breakpoint dialog box, which requires you to specify location information manually, you can set the desired breakpoint directly in the source code and then display the properties of this breakpoint in the Breakpoints window to define conditional parameters.

TIP

Too many breakpoints

Use breakpoints sparingly. Setting too many breakpoints and constantly selecting Resume impacts debugging efficiency and makes it hard to focus on one aspect of the system at a time. Consider disabling and re-enabling breakpoints as necessary.

Breakpoint Restrictions

When configuring the properties of a breakpoint in the New Breakpoint dialog box or the Breakpoints window, you may notice a Hardware button, which you can use to configure the breakpoint as a hardware breakpoint or software breakpoint. You cannot use software breakpoints in OAL code or interrupt handlers, because the breakpoint must not completely halt the execution of the system. Among other system processes, the KITL connection must remain active, because it is the only way to communicate with the debugger on the development workstation. KITL interfaces with the OAL and uses the kernel's interrupt-based communication mechanisms. If you set a breakpoint in an interrupt handler function, then the system will not be able to communicate any longer when the breakpoint is reached because interrupt handling is a single-threaded and non-interruptible function.

If you must debug interrupt handlers, you can use debug messages or hardware breakpoints. However, hardware breakpoints require an eXDI-compliant debugger (such as JTAG Probe) to register the interrupt in the processor's debug register. Typically, only one hardware debugger can be enabled on a processor at a time, although JTAG can manage multiple debuggers. You cannot use the KdStub library for hardware-assisted debugging.

To configure a hardware breakpoint, follow these steps:

1. Open the Breakpoint window by opening the Debug menu and then clicking Breakpoint.

2. Select the breakpoint in the breakpoint list and then right-click it.

3. Click Breakpoint Properties to display the Breakpoint Properties dialog box and then click the Hardware button.

4. Select the Hardware radio button and then click OK twice to close all dialog boxes.

Figure 4-9 Setting a hardware breakpoint

Lesson Summary

Enabling the debugger is a straightforward configuration process in the Platform Builder IDE if you include KITL and debugger libraries in the run-time image. You can then display the Target Device Connectivity Options dialog box and select an appropriate transport and debugger. Typically, the transport is DMA or Ethernet, but you can also use a USB or serial cable to connect the development workstation to the target device.

Platform Builder provides most of the debugging features also found in other debuggers for Windows desktop applications. You can set breakpoints, step through the code line-by-line, and use the Watch window to view and change variable values and object properties. Platform Builder also supports conditional breakpoints to halt code execution according to specified criteria. The debugger of choice for software debugging is KdStub, although you can also use an eXDI driver with Platform Builder for hardware-assisted debugging based on a JTAG probe or other hardware debugger. Hardware-assisted debugging enables you to analyze system routines that run prior to loading the kernel, OAL components, and interrupt handler functions where you cannot use software breakpoints.

Lesson 3: Testing a System by using the CETK

Automated software testing is a key to improving product quality while lowering development and support costs. This is particularly important if you create a custom BSP for a target device, added new device drivers, and implemented custom OAL code. Before releasing a new series of the system to production, it is vital to perform functional testing, unit testing, stress testing, and other types of testing to validate each part of the system and ensure that the target device operates reliably under normal conditions. It is generally much more expensive to fix defects after a new product reaches the market than to create testing tools and scripts that simulate users operating the target device and fix any defects while the system is still under development. System testing should not be an afterthought. To perform system testing efficiently throughout the software development cycle, you can use the CETK.

After this lesson, you will be able to:

■ Describe typical usage scenarios for CETK test tools.

■ Create user-defined CETK tests.

■ Run CETK tests on a target device.

Estimated lesson time: 30 minutes.

Windows Embedded CE Test Kit Overview

The CETK is a separate test application included with Platform Builder for Windows Embedded CE to validate the stability of applications and device drivers based on a series of automated tests organized in a CE test catalog. The CETK includes numerous default tests for several driver categories of peripheral devices and you can also create custom tests for your specific needs.

NOTE

CETK tests

For a complete list of default tests included in the CETK, see the section "CETK Tests" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa917791.aspx.

CETK Architecture

As illustrated in Figure 4-10, the CETK application is a client/server solution with components running on the development computer and on the target device. The development computer runs the workstation server application (CETest.exe) while the target device runs the client-side application (Clientside.exe), test engine (Tux.exe), and test results logger (Kato.exe). Among other things, this architecture enables you to run concurrent tests on multiple different devices from the same development workstation. Workstation server and client-side applications can communicate through KITL, ActiveSync® or a Windows Sockets (Winsock) connection.

Figure 4-10 The CETK client/server architecture

The CETK application includes the following components:

■ Development workstation server CETest.exe provides the graphical user interface (GUI) to run and manage CETK tests. This application also enables you to configure server settings and connection parameters, as well as connect to a target device. Having established a device connection, the workstation server can automatically download and start the client-side application, submits test requests, and compile test results based on captured logs in real-time for display.

■ Client-side application Clientside.exe interfaces with the workstation server application to control the test engine and return test results to the server application. If Clientside.exe is unavailable on the target device, the workstation server cannot establish a communication stream to the target device.

■ Test engine CETK tests are implemented in DLLs that Tux.exe loads and runs on the target device. Typically, you start the test engine remotely through workstation server and client-side application, yet it is also possible to start Tux.exe locally, in stand-alone fashion with no workstation server requirement.

■ Test results logger Kato.exe tracks the results of the CETK tests in log files. Tux DLLs can use this logger to provide additional information about whether a test succeeded or failed and have their output routed to multiple user-defined output devices. Because all CETK tests use the same logger and format, it is possible to use a default file parser or implement a custom log file parser for automatic result processing according to specific requirements.

NOTE

CETK for managed code

A managed version of the CETK is available to validate native and managed code. For details about the managed version, see the section "Tux.Net Test Harness" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa934705.aspx.

Using the CETK

You can run CETK tests in a variety of ways according to the connectivity options supported on the target device. You can use KITL, Microsoft ActiveSync, or a TCP/IP connection to connect to the target device, download the target-side CETK components, run the desired tests, and view the results in the graphical user interface on the development workstation. On the other hand, if your target device does not support these connectivity options, you must run the tests locally with appropriate command-line options.

Using the CETK Workstation Server Application

To work with the workstation server application, click Windows Embedded CE 6.0 Test Kit in the Windows Embedded CE 6.0 program group on your development computer, open the Connection menu and select the Start Client command. You can then configure the transport by clicking the Settings button. If the target device is switched on and connected to your development workstation, click Connect, select the desired target device, and then click OK to establish the communication channel and deploy the required binaries. The CETK application is now ready to run tests on the target device.

As illustrated in Figure 4-11, the CETK application automatically detects the device drivers available on the target and provides a convenient method to run the tests. One way is to click the device name under Start/Stop Test on the Tests menu, which causes CETK to test all detected components. Another way is to right-click the Test Catalog node and select the Start Tests command. You can also expand the individual containers, right-click an individual device test, and click Quick Start to test only a single component. The workstation server application also provides access to Application Verifier, CPU Monitor, Resource Consume, and Windows Embedded CE Stress tool when you right-click the device node and open the Tools submenu.

Figure 4-11 The graphical user interface of the CETK application

Create a Test Suite

Apart from running all tests at once or quick tests individually, you can create test suites that include a custom series of tests that you want to perform repeatedly throughout the software development cycle. To create a new test suite, use the Test Suite Editor, available in the workstation server application on the Tests menu. The Test Suite Editor is a graphical tool to select the tests that belong to a suite conveniently. You can export test suite definitions in the form of Test Kit Suite (.tks) files and import these files on additional development computers to ensure that all workstation server applications perform the same set of tests. These .tks files can also provide the basis for test definition archives.

Customizing Default Tests

The graphical user interface also enables you to customize the command lines that the workstation server application sends to the test engine (Tux.exe) to perform the tests. To modify the parameters of a test, right-click the test in the Test Catalog and select the Edit Command Line option. For example, the Storage Device Block Driver Benchmark Test analyzes the performance of a storage device by reading and writing data to every sector on the device. This implies that all existing data on the storage device will be destroyed. To protect you from accidental data loss, the Storage Device Block Driver Benchmark Test is skipped by default. To run the Storage Device Block Driver Benchmark Test successfully, you must edit the command line and explicitly add a special parameter called -zorch.

The supported command-line parameters depend on each individual CETK test implementation. Tests might support or require a variety of configuration parameters, such as an index number to identify the device driver to test, or additional information that must be provided to run the test.

NOTE

Command-line parameters for CETK tests

For a complete list of default CETK tests with links to additional information, such as command-line parameters, see the section "CETK Tests" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/ms893193.aspx.

Running Clientside.exe Manually

If you have included the Windows Embedded CE Test Kit catalog item in your runtime image, downloaded the CETK components with the workstation server application, or exported the components from your development workstation to the target device by using the File Viewer remote tool, you can start Clientside.exe on the target device and establish a connection to a workstation server manually. If your target device does not provide the Run dialog box for this purpose, open the Target menu in the Platform Builder IDE, select Run Programs, select Clientside.exe, and then select Run.

Clientside.exe supports the following command-line parameters that you can specify to connect to a specific workstation server application, detect installed drivers, and run tests automatically:

Clientside.exe [/i=<Server IP Address> | /n=<Server Name>] [/p=<Server Port Number>] [/a] [/s] [/d] [/x]

It is important to note that you can define these parameters also in a Wcetk.txt file or in the HKEY_LOCAL_MACHINE/Software/Microsoft/CETT registry key on the target device so that you can start Clientside.exe without command-line parameters.

In this case, Clientside.exe searches for Wcetk.txt in the root directory, then in the Windows directory on the target device, and then in the release directory on the development workstation. If Wcetk.txt does not exist in any of these locations, it checks the CETT registry key. Table 4-5 summarizes the Clientside.exe parameters.

Table 4-5 Clientside.exe start parameters

Command Line Wcetk.txt CETT Registry Key Description
/n SERVERNAME ServerName (REG_SZ) Specifies the host server name. Cannot be used together with /i and requires Domain Name System (DNS) for name resolution.
/i SERVERIP ServerIP (REG_SZ) Specifies the host IP address. Cannot be used together with /n.
/p PORTNUMBER PortNumber (REG_DWORD) Specifies the server port number that can be configured from the workstation server interface.
/a AUTORUN Autorun (REG_SZ) When set to one (1), the device automatically starts the test after the connection is established.
/s DEFAULTSUITE DefaultSuite (REG_SZ) Specifies the name of the default test suite to run.
/x AUTOEXIT Autoexit (REG_SZ) When set to one (1), the application automatically exits when the tests are completed.
/d DRIVERDETECT DriverDetect (REG_SZ) When set to zero (0), the detection of devices drivers is disabled.

Running CETK Tests in Standalone Mode

Clientside.exe connects to CETest.exe on the developer workstation, yet it is also possible to run CETK tests without a connection, which is particularly useful for devices that provide no connectivity possibilities. If you include the Windows Embedded CE Test Kit catalog item in the run-time image, you can start the test engine (Tux.exe) directly, which implicitly starts the Kato logging engine (Kato.exe) to track the test results in log files. For example, to perform mouse tests (mousetest.dll) and track the results in a file called test_results.log, you can use the following command line:

Tux.exe -o -d mousetest -f test_results.log

NOTE

Tux command-line parameters

For a complete list of Tux.exe command-line parameters, see the section "Tux Command-Line Parameters" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa934656.aspx.

Creating a Custom CETK Test Solution

The CETK includes a large number of tests, yet the default tests cannot cover all testing requirements, especially if you added your own custom device drivers to a BSP. To provide you with an option to implement user-defined tests for your custom drivers, the CETK relies on the Tux framework. Platform Builder includes a WCE TUX DLL template to create a skeleton Tux module with a few mouse clicks. When implementing the logic to exercise your driver, you might find it useful to check out the source code of existing test implementations. The CETK includes source code, which you can install as part of the Windows Embedded CE Shared Source in the Setup wizard for Windows Embedded CE. The default location is %_WINCEROOT%\Private\Test.

Creating a Custom Tux Module

To create a custom test library that is compliant with the Tux framework, start the Windows Embedded CE Subproject Wizard by adding a subproject to the OS design of your run-time image and select the WCE TUX DLL template. This causes the Tux wizard to create a skeleton that you can customize according to your driver requirements.

You must edit the following files in the subproject to customize the skeleton Tux module:

■ Header file Ft.h Defines the TUX Function Table (TFT), including a function table header and function table entries. The function table entries associate test IDs with the functions that contain the test logic.

■ Source code file Test.cpp Contains the test functions. The skeleton Tux module includes a single TestProc function that you can use as reference to add custom tests to the Tux DLL. You can replace the sample code to load and exercise your custom driver, log activities through Kato, and return an appropriate status code back to the Tux test engine when the tests are completed.

Defining a Custom Test in the CETK Test Application

The skeleton Tux module is fully functional, so you can compile the solution and build the run-time image even without code modifications. To run the new test function on a target device, you must configure a user-defined test in the CETK workstation server application. For this purpose, CETK includes a User-Defined Test Wizard, which you can start by clicking the User Defined command on the Tests menu. Figure 4-12 shows the User-Defined Test Wizard with configuration parameters to run a skeleton Tux module.

Figure 4-12 Configuring a custom test in the User-Defined Test Wizard

Debugging a Custom Test

Because Tux tests rely on code and logic implemented in Tux DLLs, it might be necessary to debug the test code. One issue worth mentioning is that you can set breakpoints in your test routines, but when code execution halts on those breakpoints, you lose the connection between the client-side application (Clientside.exe) and the workstation server application (CETest.exe). Consider using debug messages instead of breakpoints. If you must use breakpoints for thorough debugging, run Tux.exe directly on the target device in standalone mode, as mentioned earlier in this lesson. You can display the required command line in the workstation server application when you right-click the test and select Edit Command Line.

Analyzing CETK Test Results

CETK tests should use Kato to log test results, as demonstrated in the skeleton Tux module:

g_pKato->Log(LOG_COMMENT, TEXT("This test is not yet implemented."));

The workstation server application retrieves these logs automatically through Clientside.exe and stores them on the development workstation. You can also access these log files through other tools. For example, if you are using CETK in stand-alone fashion, you can import the log files to the development workstation by using the File Viewer remote tool.

The CETK includes a general CETK parser (Cetkpar.exe) located in the C:\Program Files\Microsoft Platform Builder\6.00\Cepb\Wcetk folder for convenient viewing of imported log files, as shown in Figure 4-13. Typically, you start this parser by right- clicking a completed test in the workstation server application and selecting View Results, yet you can also start Cetkpar.exe directly. Some tests, particularly performance tests based on PerfLog.dll, can also be parsed into comma-separated values (CSV) format and opened in a spreadsheet to summarize the performance data. The CETK includes a PerfToCsv parser tool for this purpose, and you can develop custom parsers for special analysis scenarios. Kato log files use a plain text format.

Figure 4-13 Analyzing CETK test results

Lesson Summary

The Windows Embedded CE Test Kit is an extensible tool that enables you to test drivers and applications on a target device in connected mode and in standalone mode. Running the CETK tools in standalone mode is useful if the target device does not support connectivity over KITL, ActiveSync, or TCP/IP. Most typically, developers use the CETK to test device drivers added to the BSP of a target device.

The CETK relies on the Tux test engine, which provides a common framework for all test DLLs. The Tux DLLs contain the actual testing logic and run on the target device to load and exercise the driver. Tux DLLs also interface with Kato to track test results in log files, which you can access directly in the CETK test application or process in separate tools, such as custom parsers and spreadsheets.

Lesson 4: Testing the Boot Loader

The general task of a boot loader is to load the kernel into memory and then call the OS startup routine after powering up the device. On Windows Embedded CE specifically, the boot loader is part of the BSP and in charge of initializing the core hardware platform, downloading the run-time image, and starting the kernel. Even if you do not plan to ship a boot loader in your final product and directly bootstrap the run-time image, you might find a boot loader helpful during the development cycle. Among other things, a boot loader can help to simplify run-time image deployment complexities. Downloading the run-time image over Ethernet connections, serial cable, DMA, or USB connections from a development computer is a convenience feature that can help to save development time. Based on the source code included with Platform Builder for Windows Embedded CE 6.0, you can also develop a custom boot loader to support new hardware or features. For example, you can use a boot loader to copy the run-time image from RAM into flash memory and eliminate the need for a separate flash memory programmer or Institute of Electrical and Electronic Engineers (IEEE) 1149.1-compliant test access port and boundary-scanning technology. However, debugging and testing a boot loader is a complex undertaking because you are working with code that runs before the kernel loads.

After this lesson, you will be able to:

■ Describe the CE boot loader architecture.

■ List common debugging techniques for boot loaders.

Estimated lesson time: 15 minutes.

CE Boot Loader Architecture

The underlying idea of a boot loader is to bootstrap a small program with pre-boot routines in linear, nonvolatile, CPU-accessible memory. Having placed the initial boot loader image on the target device at the memory address where the CPU begins to retrieve code through a built-in monitor program provided by the board manufacturer or a JTAG probe, the boot loader runs whenever you power up or reset the system. Typical boot loader tasks performed at this stage include initializing the Central Processing Unit (CPU), the memory controller, system clock, Universal Asynchronous Receiver/Transmitters (UARTs), Ethernet controllers, and possibly other hardware devices, downloading the run-time image and copying it into RAM according to the binary image builder (BIB) layout, and jumping to the StartUp function. The last record of the run-time image contains this function's start address. The StartUp function then continues the boot process by calling the kernel initialization routines.

Although the various boot loader implementations differ in their complexity and the tasks they perform, there are common characteristics that Windows Embedded CE covers through static libraries to facilitate boot loader development, as illustrated in Figure 4-14. The resulting boot loader architecture influences how you can debug boot loader code. Chapter 5, "Customizing a Board Support Package," discusses boot loader development in more detail.

Figure 4-14 Windows Embedded CE boot loader architecture

The Windows Embedded CE boot loader architecture is based on the following code portions and libraries:

■ BLCOMMON Implements the basic boot loader framework for copying the boot loader from flash memory to RAM for faster execution, decoding image file contents, verifying checksums, and keeping track of load progress. BLCOMMON calls well-defined OEM functions throughout the process to handle hardware-specific customizations.

■ OEM code This is the code that OEMs must implement for their hardware platforms to support the BLCOMMON library.

■ Eboot Provides Dynamic Host Configuration Protocol (DHCP), Trivial File Transfer Protocol (TFTP), and User Datagram Protocol (UDP) services to download run-time images over Ethernet connections.

■ Bootpart Provides storage partitioning routines so that the boot loader can create a binary ROM image file system (BinFS) partition and a second partition with another file system on the same storage device. Bootpart can also create a boot partition to store boot parameters.

■ Network drivers Encapsulate the basic initialization and access primitives for a variety of common network controller devices. The interface for the libraries is generic so that both the boot loader and the OS can use the interface. The boot loader uses the interface for downloading run-time images and the OS uses the interface to implement a KITL connection to Platform Builder.

Debugging Techniques for Boot Loaders

The boot loader design typically consists of at least two distinctive parts. The first part is written in assembly language and initializes the system before jumping to a second part written in C. If you are using a BLCOMMON-based architecture as illustrated in Figure 4-14, you might not have to debug assembly code. If your device is equipped with a UART, you can use the RETAILMSG macro in C code to send data over a serial output interface to the user for display.

Depending on whether you must debug assembly or C code, the following different debugging techniques are available:

■ Assembly code Common debugging techniques for the initial startup code rely on LEDs, such as a debugging board with seven-segment LEDs and UARTs for a serial communication interface, because it is relatively straightforward to access General Purpose Input/Output (GPIO) registers and modify the state of an input/output line.

■ C Code Debugging is much easier at the C-code level because you can access advanced communication interfaces and debugging macros.

■ Assembly and C code If a hardware debugger (JTAG probe) is available, you can use Platform Builder in conjunction with an eXDI driver to debug the boot loader.

EXAM TIP

To pass the certification exam, make sure you know the different techniques to debug the boot loader, kernel, device drivers, and applications.

Lesson Summary

Debugging the boot loader is a complex task that requires a good understanding of the hardware platform. If a hardware debugger is available, you can use Platform Builder in conjunction with an eXDI driver for hardware-assisted debugging. Otherwise, consider using an LED board for debugging assembly code and C-style macros to output debug messages over a serial communication interface in C code.

Lab 4: System Debugging and Testing based on KITL, Debug Zones, and CETK Tools

In this lab, you debug a console application added as a subproject to an OS design based on the Device Emulator BSP. To enable debugging, you include KdStub and KITL in the run-time image and configure corresponding target-device connectivity options. You then modify the source code of the console application to implement support for debug zones, specify initially active debug zones in the Pegasus registry key, and attach to the target device with the Kernel Debugger to examine the debug messages in the Output window of Visual Studio. Subsequently, you use the CETK to test the mouse driver included in the run-time image. To create the initial OS design in Visual Studio, follow the procedures outlined in Lab 1, "Creating, Configuring, and Building an OS Design."

NOTE

Detailed step-by-step instructions

To help you successfully master the procedures presented in this Lab, see the document "Detailed Step-by-Step Instructions for Lab 4" in the companion material for this book.

► Enable KITL and Use Debug Zones

1. Open the OS design project created in Lab 1 in Visual Studio, right-click the OSDesign name and select Properties to edit the OS design properties, select Configuration Properties and then Build Options, and then select the Enable KITL check box for the run-time image.

2. In the OS Design property pages dialog box, also enable the Kernel Debugger feature, apply the changes, and then close the dialog box.

3. Verify that you are currently working in debug build configuration to build an image that contains the KITL and Kernel Debugger components activated in the previous steps.

4. Build the OS design by selecting Rebuild Current BSP and Subprojects under Advanced Build Commands on the Build menu (perform a Clean Sysgen if you encounter errors during subsequent steps).

5. Open the Target menu and click Connectivity Options to display the Target Device Connectivity Options dialog box. Configure the following settings, as shown in Table 4-6 and then click OK.

Table 4-6 Device connectivity settings

Configuration Parameter Setting
Download Device Emulator (DMA)
Transport Device Emulator (DMA)
Debugger KdStub

6. Add a subproject to the OS design and select the WCE Console Application template. Name the project TestDbgZones and select the option A Typical Hello World Application in the CE Subproject Wizard.

7. Add a new header file called DbgZone.h to the subproject and define the following zones:

#include <DBGAPI.H>

#define DEBUGMASK(n) (0x00000001<<n)

#define MASK_INIT    DEBUGMASK(0)

#define MASK_DEINIT  DEBUGMASK(1)

#define MASK_ON      DEBUGMASK(2)

#define MASK_ZONE3   DEBUGMASK(3)

#define MASK_ZONE4   DEBUGMASK(4)

#define MASK_ZONE5   DEBUGMASK(5)

#define MASK_ZONE6   DEBUGMASK(6)

#define MASK_ZONE7   DEBUGMASK(7)

#define MASK_ZONE8   DEBUGMASK(8)

#define MASK_ZONE9   DEBUGMASK(9)

#define MASK_ZONE10  DEBUGMASK(10)

#define MASK_ZONE11  DEBUGMASK(11)

#define MASK_ZONE12  DEBUGMASK(12)

#define MASK_FAILURE DEBUGMASK(13)

#define MASK_WARNING DEBUGMASK(14)

#define MASK_ERROR   DEBUGMASK(15)

#define ZONE_INIT    DEBUGZONE(0)

#define ZONE_DEINIT  DEBUGZONE(1)

#define ZONE_ON      DEBUGZONE(2)

#define ZONE_3       DEBUGZONE(3)

#define ZONE_4       DEBUGZONE(4)

#define ZONE_5       DEBUGZONE(5)

#define ZONE_6       DEBUGZONE(6)

#define ZONE_7       DEBUGZONE(7)

#define ZONE_8       DEBUGZONE(8)

#define ZONE_9       DEBUGZONE(9)

#define ZONE_10      DEBUGZONE(10)

#define ZONE_11      DEBUGZONE(11)

#define ZONE_12      DEBUGZONE(12)

#define ZONE_FAILURE DEBUGZONE(13)

#define ZONE_WARNING DEBUGZONE(14)

#define ZONE_ERROR   DEBUGZONE(15)

8. Add an include statement for the DbgZone.h header file to the TestDbgZones.c file:

#include "DbgZone.h"

9. Define the dpCurSettings variable for the debug zones above the _tmain function, as follows:

DBGPARAM dpCurSettings = {

 TEXT("TestDbgZone"), {

  TEXT("Init"), TEXT("Deinit"), TEXT("On"), TEXT("n/a"),

  TEXT("n/a"), TEXT("n/a"), TEXT("n/a"), TEXT("n/a"),

  TEXT("n/a"), TEXT("n/a"), TEXT("n/a"), TEXT("n/a"),

  TEXT("n/a"), TEXT("Failure"), TEXT("Warning"), TEXT("Error")

 },

 MASK_INIT | MASK_ON | MASK_ERROR

};

10. Register the debug zones of the module in the first line of the _tmain function:

DEBUGREGISTER(NULL);

11. Use the RETAILMSG and DEBUGMSG macros to display debug messages and associate them with debug zones, as follows:

DEBUGMSG(ZONE_INIT,

 (TEXT("Message : ZONE_INIT")));

RETAILMSG(ZONE_FAILURE || ZONE_WARNING,

 (TEXT("Message : ZONE_FAILURE || ZONE_WARNING")));

DEBUGMSG(ZONE_DEINIT && ZONE_ON,

 (TEXT("Message : ZONE_DEINIT && ZONE_ON")));

12. Build the application, attach to the target device, and then start the application by using the Target Control window.

13. Note that only the first debug message is displayed in the debug Output window:

4294890680 PID:3c50002 TID:3c60002 Message : ZONE_INIT

14. Open the registry editor (Regedit.exe) on your development computer to activate the remaining debug zones, by default.

15. Open the HKEY_CURRENT_USER\Pegasus\Zones key and create a REG_DWORD value called TestDbgZone (according to the name of the module defined in the dpCurSettings variable).

16. Set the value to 0xFFFF to enable all 16 named zones, which correspond to the lower 16 bits in this 32 bit DWORD value (see Figure 4-15).

17. In Visual Studio, start the application again, and notice the following output:

4294911331 PID:2270006 TID:2280006 Message : ZONE_INIT

4294911336 PID:2270006 TID:2280006 Message : ZONE_FAILURE || ZONE_WARNING

4294911336 PID:2270006 TID:2280006 Message : ZONE_DEINIT && ZONE_ON

18. Change the TestDbgZone value in the registry to enable and disable different debug zones and verify the results in the Output window of Visual Studio.

Figure 4-15 HKEY_CURRENT_USER\Pegasus\Zones: "TestDbgZone"=dword:FFFF

NOTE

Enabling and disabling debug zones in Platform Builder

You cannot control the debug zones for the TestDbgZone module in Platform Builder because the application process exits before you can open and modify the active zone for this module. You can only manage debug zones for loaded modules in Platform Builder, such as for graphical applications and DLLs.

► Perform Mouse Driver Tests by Using the CETK

1. Open the Windows CE Test Kit application from the Start menu on your development computer (open the Windows Embedded CE 6.0 menu and click Windows Embedded CE Test Kit).

2. In the Windows Embedded CE Test Kit window, open the Connection menu and click Start Client to establish a connection to the target device.

3. Click Connect and select the device in the Connection Manager window.

4. Verify that the workstation server application connects successfully to the device, deploys the required CETK binaries, detects available device drivers, and displays a list of all components in a hierarchical tree, as shown in Figure 4-16.

5. Right-click the Windows CE Test Catalog node and click Deselect All Tests.

6. Open each node in the list and select the Mouse Test check box.

7. Open the Test menu and then clock on Start/Stop Test to perform a mouse test.

8. On the target device perform the required mouse actions.

9. Complete the test and then can access the test report by right-clicking the test entry and selecting View Results.

10. Examine the results in the CETK parser and notice successful, skipped, and failed test procedures.

Figure 4-16 Device categories in the Windows Embedded CE Test Kit window

Chapter Review

Platform Builder for Windows Embedded CE ships with a comprehensive set of debugging and testing tools to diagnose and eliminate root causes of errors, and validate the system in its final configuration prior to its release to production. The debugging tools integrate with Visual Studio and communicate over KITL connections with the target device. Alternatively, you can create a memory dump and use the CE Dump File Reader to debug the system in offline mode, which is particularly useful for postmortem debugging. The debugging environment is also extensible by means of eXDI drivers to perform hardware-assisted debugging beyond the capabilities of the standard Kernel Debugger.

The Kernel Debugger is a hybrid debugger for kernel components and applications. Debugging starts automatically if you attach to a target device with KdStub and KITL enabled. You can use the Target Control window to start applications for debugging and perform advanced system tests based on CEDebugX commands. However, it is important to keep in mind that you cannot set breakpoints in interrupt handlers or OAL modules because at these levels, the kernel operates in single-thread mode and stops communicating with the development workstation if code execution halts. To debug interrupt handlers, use a hardware debugger or debug messages. The debug messages feature supports debug zones to control the information output without having the rebuild the run-time image. You can also use debug messages to debug the C-code portion of a boot loader, yet for the assembly code portion you must use a hardware debugger or an LED panel.

KITL is also a requirement if you want to centralize system testing based on the CETK Test application, although it is also possible to run CETK tests in standalone mode. If you are developing a custom BSP for a target device, you can use the CETK to perform automated or semi-automated component tests based on custom Tux DLLs. Platform Builder includes a WCE TUX DLL template to create a skeleton Tux module that you can extend to meet your specific testing needs. You can integrate the custom Tux DLL in the CETK test application and run tests individually or as part of a larger test suite. Because all CETK tests use the same logging engine and log file format, you can use the same parser tool to analyze the results of default and user-defined tests.

Key Terms

Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book.

■ Debug Zones

■ KITL

■ Hardware debugger

■ dpCurSettings

■ DebugX

■ Target Control

■ Tux

■ Kato

Suggested Practices

To help you successfully master the exam objectives presented in this chapter, complete the following tasks.

Detect Memory Leaks

Add a subproject to the OS design for a console application that generates memory leaks by allocating memory blocks and never freeing them. Using the tools discussed in this chapter, isolate the issue and fix it.

Custom CETK Test

Add a subproject to the OS design for a WCE TUX DLL. Build the Tux DLL and register it in the Windows Embedded CE Test Kit application. Run a CETK test and verify the test results. Set breakpoints in your Tux DLL and debug the code by running a CETK test in standalone mode.

Chapter 5

Customizing a Board Support Package

Application developers do not often need to create a Board Support Package (BSP). However, Original Equipment Manufacturers (OEMs) face this requirement when porting Microsoft® Windows® Embedded CE 6.0 R2 to a new hardware platform. To help OEMs accomplish this task efficiently, Windows Embedded CE features a production-quality OEM adaptation layer (PQOAL) architecture that promotes code reuse based on a collection of OAL libraries organized by processor model and OAL function. Microsoft encourages OEM developers to clone and customize an existing BSP to meet their specific requirements and take full advantage of tested and proven production features for power management, performance optimizations, and input/ output controls (IOCTL). This chapter covers the PQOAL architecture, explains how to clone BSPs, and lists the functions that OEM developers must implement in order to adapt Windows Embedded CE to new hardware architectures and models. It is advantageous to understand the various aspects of customizing a BSP even if you do not intend to develop your own. This chapter will provide an overview of the aspects of BSP customization, ranging from modifications of the startup process and implementing kernel initialization routines, to adding device drivers, power management capabilities, and support for performance optimization.

Exam objectives in this chapter:

■ Understanding the BSP architecture of Windows Embedded CE

■ Modifying and adapting BSPs and boot loaders for specific target devices

■ Understanding memory management and layout

■ Enabling power management in a BSP

Before You Begin

To complete the lessons in this chapter, you must have the following:

■ At least some basic knowledge about Windows Embedded CE software development.

■ A thorough understanding of hardware architectures for embedded devices.

■ Basic knowledge about power management and how to implement it in drivers and applications.

■ A development computer with Microsoft Visual Studio® 2005 Service Pack 1 and Platform Builder for Windows Embedded CE 6.0 R2 installed.

Lesson 1: Adapting and Configuring a Board Support Package

The BSP development process for a new hardware platform typically begins after performing functional tests of the hardware by using a ROM monitor, by cloning an appropriate reference BSP, followed by implementing a boot loader and the core OAL functions to support the kernel. The goal is to create a bootable system with the least possible amount of custom code. You can then add device drivers to the BSP to support integrated and peripheral hardware and expand the system by implementing power management and other advanced operating system (OS) features according to the capabilities of the target device.

After this lesson, you will be able to:

■ Identify and locate the content of a PQOAL-based Board Support Package.

■ Identify hardware-specific and common-code libraries.

■ Understand how to clone a BSP.

■ Adapt a boot loader, OAL, and device drivers.

Estimated lesson time: 40 minutes.

Board Support Package Overview

A BSP contains all the source code for the boot loader, OAL, and device drivers for a given platform. In addition to these components, the BSP also contains build and system configuration files, as illustrated in Figure 5-1. The configuration files are not included in the actual run-time image, yet they are part of the BSP package to specify source code files, memory layout, registry settings, and other aspects to compile and build the run-time image, as explained in Chapter 2, "Building and Deploying a Run-Time Image."

Figure 5-1 Components of a BSP in relationship to the remaining elements of Windows Embedded CE 6.0

According to Figure 5-1, BSP development includes the following main components:

■ Boot loader Runs when powering up or resetting the device. The boot loader is responsible for initializing the hardware platform and passing execution to the operating system.

■ OEM adaptation layer (OAL) Represents the core of the BSP and is the interface between the kernel and the hardware. Because it is linked directly to the kernel, it becomes part of the kernel in a CE run-time image. Some of the core kernel components are directly dependent on the OAL for hardware initialization, such as the interrupt handling and timer handling for the thread scheduler.

■ Device drivers Manage the functionality of a particular peripheral and provide an interface between the device hardware and the operating system. Windows Embedded CE supports a variety of driver architectures based on the interfaces they expose, as explained in Chapter 6, "Developing Device Drivers."

■ Configuration files Provide the necessary information to control the build process and plays a key role in the design of a platform's operating system. Typical configuration files included in BSPs are Sources files, Dirs files, Config.bib, Platform.bib, Platform.reg, Platform.db, Platform.dat, and catalog files (*.pbcxml).

Adapting a Board Support Package

It is generally a good idea to jump start the BSP development p