Application Modularity Maximizes Portability from Desktops to Devices

 

Kirk Radeck, Windows Embedded MVP
Senior Software Engineering Consultant

May 2003

Download the Modular.exe

Applies to:
    Microsoft® Windows® CE .NET
    Microsoft Windows XP Embedded
    Microsoft Windows Powered Pocket PC
    Microsoft Visual Studio® .NET
    Microsoft Visual C++®
    Microsoft Visual C#®

Abstract

This white paper demonstrates how to maximize software portability by writing modular code and includes a set of downloadable applications and platform-agnostic libraries that will run on multiple operating system platforms: Windows CE .NET, Pocket PC (2000 and 2002) and any Microsoft desktop operating system supporting .NET 1.1 or the Microsoft .NET Compact Framework version 1. A total of 11 projects have been built and tested for this sample project collection, written in C# using Visual Studio .NET 2003. (25 printed pages)

Contents

Introduction
.NET Compact Framework
Document Motivation
Original Goals
Software and Hardware Recommended
Current Games
Conclusion
Additional Resources

Introduction

So I ordered the Bicycle Board Games CD off the Microsoft site a while back. Although I can play chess on my laptop, it would be really cool if I could load it on my Microsoft® Windows® Powered Pocket PC phone as well so that I could play it anywhere, anytime, anyplace. After all, that seems to be everyone's marketing pitch nowadays. But it doesn't apply to this game. Anywhere, anytime, anyplace—as long as I can carry a five-pound laptop.

Well, necessity may actually be the mother of invention. The preceding necessity—playing games anywhere, anytime, anyplace—has potentially created its corresponding invention: Smart Device Programmability (SDP). SDP was added to the Microsoft Visual Studio® .NET 2003 (final beta), and will be available in the release version Spring of 2003. It finally allows developers to create applications for Pocket PC 2000 or 2002 and Windows CE .NET-based devices using managed code written in Visual C#® .NET or Microsoft Visual Basic® .NET, with hopefully more supported languages to follow.

.NET Compact Framework

Microsoft was required to create the Microsoft .NET Compact Framework for embedded devices, which is a subset of the Microsoft .NET Framework for the desktop. This was necessary because the Framework is quite large, and embedded devices have limited memory available. The Compact Framework installs on a device in the \Program Files\.NET Compact Framework directory automatically for library and run-time support the first time a managed-code application is downloaded using Visual Studio .NET 2003. It may also be downloaded and installed off the Microsoft site for consumer use, but this has not been tested for the purposes of this document.

This is all well and good, but solving one problem sometimes creates another problem. If a developer wishes to build applications that will run on both the .NET Compact Framework and the .NET Framework, he must be very careful, because any libraries used must usually exist on both in order for the application to run correctly on both. This document describes methods for creating portable applications using Visual Studio .NET 2003 and Visual C# .NET, and supplies device-specific downloadable game applications with shared platform-agnostic libraries where the majority of both source and compiled code is loadable on Pocket PC, Windows CE .NET, and desktop computers as a proof of concept.

Document Motivation

I will give a chronological list of Microsoft-related papers that I have written so far that will help describe the motivation for writing this document. This will not only allow shameless self-promotion of my past work, but it will also show that there is at least some "method to the madness" for my present work.

More than a year ago, Microsoft asked me to write a paper that would help developers write C++ application code that could not only be used on the Pocket PC at that time, but could also be used for the then-upcoming Windows CE 4.0. Even though I wasn't sure how to approach the problem initially, I was willing to take on the project, mostly because I had free time on my hands. After "playing around" with projects and code for a while, I wrote the MSDN® article—Building Native C++ Applications that Will Run on Microsoft Pocket PC and Windows CE .NET Platforms—which offered a general coding strategy for any Microsoft operating system using C++. At that time, I heard that Microsoft was going to release something called the Compact Framework. I also heard that developers were going to be able to use C# for development on these new .NET-based platforms. I had written a few simple Windows Forms and Web Applications using C# for the desktop, and I really liked C#. Even though it's generally considered bad style to quote oneself, here's the "bold" prediction that I made.

When the Compact Framework is released for Windows CE in the second half of 2002, both Visual Studio .NET and C# both are good candidates for future product development. I believe that companies that do not at least evaluate using C# for new products (and maybe even product ports) will be making a mistake, because C# has such a rich library already and it is highly portable by the nature of the language syntax.

It really wasn't such a "bold" prediction because it was obvious, particularly after "playing around" with C++ and realizing that writing portable code is not completely trivial, even across Microsoft platforms. It seemed at the time that using C# would most likely be easier than using C++ for building portable applications, even without having the luxury of seeing the future tools and the eventual cross-platform application programming interface (API) support using Visual Studio .NET 2003.

I wanted to add support to my first paper's assertion that C# is a good alternative to C++ as a modern-day programming language. Although I realize that programming languages are equivalent, I personally believe that C# is superior to Java from an application developer's perspective when it comes to ease, but I wanted to convey that opinion as objectively as possible in C# and Java: Comparing Programming Languages.

I was asked at the last minute if I could write a 3,000-word article — Simple, Standard and Smart Device Programmability — for the April 2003 Wireless Communications and Computing Solutions magazine (formerly known as Intel Solutions Journal). When I asked what the topic should be, all I heard was "applications development for smart devices." At the time, I had installed and started to experiment with Visual Studio .NET 2003 for ideas on this paper, so I decided to preemptively write an abbreviated version of this document for that Intel paper. In the Intel paper, I built a very simple portable temperature conversion Form that runs on the desktop and Windows CE-based and Pocket PC-based devices, but promised to build a more complex application for an upcoming MSDN article. The Intel article should be available online by the time this document posts.

I decided to write this white paper mostly because of the C++ paper's recommendation to look into C# later; the C# paper's argument that it is a good object-oriented programming language; and the promise to build a more complex application in the Intel article, all listed previously. It seems to finish a series of articles. There was also one other motivation. Microsoft's latest TechInsight deals with the topics of modularity and small footprint. I imagined that there would be several articles about building Windows CE operating system images using Platform Builder to meet this campaign requirement, but there would probably be few application papers that would do so. Therefore, I chose to write this article to both complete the series and to give application developers a "voice."

Original Goals

The motivation for this paper drove the original goals.

  1. Determine the feasibility of writing portable applications using C# and Visual Studio .NET 2003.
  2. Build a near "real-world" proof of concept application to test feasibility, and to give some proof to the reader that it is at least possible in some scenarios.
  3. Try to determine what is "easy" and what is "difficult," and build an issues list for the reader.
  4. Create a final recommendation based upon the previous goals and what I learned.

Desktop Computer (meeting minimum hardware requirements for all software below)

  1. Microsoft Windows XP Professional with SP1 and all Windows Updates installed
  2. Microsoft Visual Studio .NET 2003 (final beta, or release if available is better)
  3. Microsoft ActiveSync® 3.6
  4. Pocket PC
  5. Pocket PC 2000 or 2002
  6. Windows CE-based device
  7. Windows CE 4.1 or later

Although the Pocket PC and Windows CE-based devices are optional because Visual Studio .NET 2003 has built-in emulators for both, it is always best to finally use actual embedded hardware to test real application behavior. I personally used a Siemens SIMpad SL4 running Windows CE 4.1, and an HP Jornada Pocket PC running Pocket PC 2000, and the Visual Studio .NET 2003 (final beta) Pocket PC emulator.

Creating Projects Using Visual Studio .NET 2003

Although carefully constructed Framework libraries may load and run on both the desktop and Windows CE-based devices, it is advisable to create Smart Device libraries when building dynamic-link libraries (DLLs) for application import instead. Why? The Compact Framework is a subset of the desktop version and Smart Device projects compile against this Compact Framework using Visual Studio .NET. Since it is our goal to create portable applications, more compile-time help will be available in this manner because if an API is available for a Smart Device it is usually available for the desktop as well, but not vice versa. One exception would be classes in the System.Data.SqlServerCE namespace, which are available for the Compact Framework but not the Framework. A few other exceptions exist as well to this rule. Because there are exceptions, it is therefore necessary to use both compiler and online help, unfortunately. For example, when building a Smart Device library, the compiler should give you an error if you try to use the System.Collections.ArrayList's this indexer, as this method is not supported for the Compact Framework. But to determine if this indexer can be used before writing code and compiling:

  1. Open the Visual Studio .NET 2003 Documentation.
  2. Click the Index tab and then type ArrayList class.
  3. Double-click the all members link.
  4. With the ArrayList members panel, scroll to Item Public Properties cell.
  5. Note that the Supported by the .NET Compact Framework text is not available in the corresponding property cell. This means that this call cannot be used for portable code across platforms since it only is available on the desktop.

To determine if the ArrayList class is available on all necessary platforms

  1. Open the Visual Studio .NET 2003 Documentation.
  2. Click the Index tab.
  3. Type ArrayList class.
  4. Double-click about ArrayList class.
  5. With the ArrayList Class panel, scroll to Requirements\Platforms list at bottom.
  6. Verify that every platform where the application should run is in list.

Naturally, if a class or method is not available on every platform necessary, you usually cannot use it in any platform-agnostic library that is created. There is at least one "trick" for cases where the use of a class or method is non-critical to application behavior, however, discussed in a following ToolTips section.

When developers used Visual Studio 6 and built C++ native code, there were generally two options for building libraries: static libraries, which were packaged in .lib files and linked into executables at build time; and dynamic libraries, which were packaged in .dll files and linked to programs at run time. There were advantages and disadvantages to both approaches. The static libraries were more "natural" because a developer could import the library and its headers and use classes in the library as if they were local to the project. For example, if class A existed in the static library then the importer could create a new class B which subclassed A and make standard C++ calls on these classes as if they both were local to the project. But using this approach created some problems as well. Because a static library is copied into an executable at compile time, if some bug is found in the library, the library had to be modified and recompiled and all executable files using this library had to be recompiled as well. Programs were larger because each had the static copy. There were many other issues beyond the scope of this paper.

Developers could avoid the above problems by creating Active Template Library (ATL)/Component Object Model (COM) DLLs. There was only one copy, and only one version needed to reside on disk and in memory no matter how many programs used it. This reduced program size, which used less disk space and memory and allowed for updating one DLL and fixing all executables that imported it, but it also created at least one problem for application developers. ATL is somewhat difficult to use as a client and even more difficult to use as a server because a developer really needs to learn a whole new language and set of rules to write correct ATL code. As a comparison, I learned the basics of programming in C# in a few days to a week; I spent months learning how to create and use ATL DLLs and am far from a "master." Because of these difficulties, it was not uncommon for developers to "bite the bullet" and just create static libraries and live with their inefficiencies because they were easier to use.

Beginning with Visual Studio .NET version one, all libraries are now dynamic DLLs, but .NET managed code has really improved the usability of these DLLs by giving them the advantages of both static and dynamic libraries while removing all the disadvantages as well. Classes can be used as if they are local to the project using standard syntax, which is awesome. A class can be created in C# (or any other managed-code language), compiled into a DLL, imported into a managed C++ project (or any other managed-code project supported now or in the future) and be subclassed as if it were written in the importing project's language. Once again, this is awesome. Programs can share DLLs, which means that overall application footprint is smaller. Data can be more easily shared across applications. In my opinion, Microsoft finally got it right this time around. This is awesome. And the word "awesome" is not being used lightly here.

Without having used both models, a developer can have no idea how much easier the .NET paradigm is to use over COM or any other language-neutral scheme devised to this point. I strongly urge you to at least "play" with .NET libraries. After you do, you should realize the true power of Visual Studio .NET. And you may discard any other tools that you have and wish they would just go away forever.

Creating a Platform-Agnostic Library

To create a Smart Device library DLL for use cross-platform

  1. Open Visual Studio .NET 2003.

  2. Click File, click New, and then click Project.

  3. With the New Project dialog box, select correct "Project Types:" item.

    Currently supports:

    1. Visual Basic Projects
    2. Microsoft Visual C# Projects
    3. (I chose C# for all sample code)
  4. Select the correct Templates icon.

  5. For the Smart Device application, click the Browse button.

  6. With the Project Location dialog box, select the parent directory for where the project will reside and click Open.

  7. Back in the New Project dialog box, type <project name> in the Name text field and click OK.

  8. With "Smart Device Application Wizard - <project name>" dialog box:

    1. At the "What platform do you want to target?" prompt, click Windows CE.
    2. At the "What project type do you want to create?" prompt, click Class Library.
  9. Click OK.

    Create platform-agnostic classes by calling only .NET classes and methods, which are available on all necessary platforms, described previously, and build.

"New Project" dialog box. Note the new "Smart Device Application" icon which is new to Visual Studio .NET 2003.

Figure 1.

"Smart Device Application Wizard - <project name>" dialog box. This dialog box is new to Visual Studio .NET 2003. It opens following the selection of a Smart Device Application and allows a developer to select an embedded platform and project type. Selecting the "Windows CE" platform will give the developer the most options. If "Pocket PC" is chosen, then only the Pocket PC emulator and device will later be available for download and testing after a build. Choosing "Windows CE" will later allow emulators and devices for both Windows CE and Pocket PC in case the application type is an executable.

Figure 2.

Creating Platform-Specific Applications

It is necessary to create an application (.exe) project for each platform you intend to support, as a CE Windows Form application will run on the desktop but not generally vice-versa (I did determine that a simple console application built for the desktop will run on a Compact Framework device, however, but I do not know all of the limitations). There is another reason as well. In order to use the Windows CE and Pocket PC emulators, a Smart Device project must be created otherwise the emulator will not be available to you. The desktop application with a Windows user interface will be created by selecting the "Windows Application" icon in the "New Project" Dialog's "Templates:" ScrollPanel. Creating Pocket PC and Windows CE .NET-based applications are similar to the platform-agnostic library above, except select either "Pocket PC" or "Windows CE" respectively, and "Windows Application" for both, in the "Smart Device Application Wizard" dialog box.

Each application should be very thin, however. It is best to use this project strictly as a program entry point that imports platform-agnostic libraries, which do most of the "dirty work" to minimize coding and maintenance. In the sample application included and discussed in the following text, the projects' Forms simply subclass a Form, which was created in a platform-agnostic control library. And these platform-specific Forms only supply a small amount of platform-specific override code necessary.

Master Project

It is recommended to create one master project, where all applications and libraries created by the developer are added to this master. This will ease debugging and allow for viewing multiple related projects simultaneously in one application instance of the Visual Studio .NET Solution Explorer tree. It is also a very good idea to create all related projects in one file system parent root node, as I found that Visual Studio .NET does an excellent job of reestablishing absolute reference paths in case all projects are moved to a different drive together. (When a project is created and a reference is added, a reference is saved as an absolute path to the location of the imported DLL file. Let's say that all of your projects were created as subdirectories of the E:\Modular directory, and then you decide to move all projects to D:\Modular. When the IDE opens the master project the first time after moving, it will reset all references from E:\Modular. . . to D:\Modular automatically. This is very nice feature in Visual Studio .NET. Care must be taken to "move" and not just "copy" directories, however. While untested, I believe that a "copy" may cause the copied projects to refer to the original location as those projects would still then exist.)

To create a master project

  1. Click File and then click New Project.
  2. With the New Project dialog box, select correct "Project Types:" item.
  3. Currently available Visual Basic Projects or Visual C# Projects.
  4. In the Templates panel, select the Empty Project icon.
  5. Set project location as noted previously and click OK.
  6. The master project is created.

To add existing project(s) to this master project as subproject(s)

  1. In the master project, click File, click Add Project, and then click Existing Project.

    1. With the Add Existing Project dialog box, open the preexisting project file to add on the local drive.
    2. The C# Project example - <subproj> .csdproj
    3. Click the Open button.
    4. The sub project is now added to master project.

    Adding references to projects will be necessary to use any library.

  2. In Master Project's Solution Explorer panel, click the project to add the reference to.

  3. Right-click the project's "References" tree node and click Add reference.

    1. In the Add Reference dialog box, click the Projects tab.
    2. Double-click the project to add and click OK.

    Visual Studio .NET allows you to change the project build dependencies.

  4. In the Master Project's Solution Explorer panel, right-click the project to modify and click Project Dependencies.

  5. In the Project Dependencies dialog box, click the Dependencies tab.

  6. Select all projects that must be built before the selected project is built, clear all projects that this project is not dependent on, and then click OK.

    The following steps will allow you to build a project's dependencies and finally itself in correct order (including the master project, which would build all projects added to the master in case every project except itself was added as a dependency):

  7. Right-click the Solution Explorer's project to build.

  8. Select Build (or Rebuild for a complete project and dependency rebuild).

Sample Application

The preceding project instructions were used to create a game application, where games could be added to the application without recompiling the application itself. Why? There were several reasons for these requirements.

  1. I believe that games are a good candidate for platform-agnostic applications. Most users that have Pocket PCs also have desktop computers, and games are popular applications for both.
  2. My first C++ paper included a sample TicTacToe game. I wanted to use a more object-oriented approach in this C# version than the previous C++ version, and C# allows me to do this more easily (while standard C++ allows for subclassing and dynamic binding, and is considered to at least be a "hybrid" language, it is easier using C# because this new language supports garbage collection while standard C++ does not). If you download the source files from that paper versus this paper, then compare the C++ files in the former's TicTacToeEngine against the C# files in the latter's TicTacToeBoard, you should note that the C# code is indeed more object oriented. I will admit that I had to optimize some code in the C# version's TTTBoardBase.FindBestMoveHelper method, because garbage-collected code compiled into MSIL is slower than native-compiled C++. Another reason is that many more virtual calls are made when running the C# version.
  3. I need to support the "Modular and Small Footprint" theme. Games are packaged in DLLs, which supports the Modular theme. I would imagine that a user, particularly on a Windows CE-based or Pocket PC-based device, would only install games that he would actually play to save memory. If a monolithic executable is created that includes all games and logic in its binary, then the user would later have to install that exe with all the included games even if he only wants to play one. Creating a pluggable game framework which requires games to be packaged in DLLs allows a more versatile installation program, allowing the user to only install those games that he expects to play and reducing the overall application and library footprint This sample project set may be close to the "ultimate" in modularity and small footprint, because not only is most of the compiled code included in multiple DLLs that may be used by other projects and libraries on multiple devices, but the majority of the source code is used and reused across platforms as well. 1

Current Games

There are two games included in the code samples, one functional and one not. Both were created as Smart Device libraries creating one or more DLLs, which plug into the main game application for any supported device.

  1. TicTacToe. While this application may not truly be ready for "primetime," it is a functioning game that allows the human to play against the computer. It is internationalized, and localized for English, German, and French. In my C# paper, I recommended that most applications should be internationalized today, and stated that Visual Studio .NET has good built-in i18n support classes. Therefore, I feel that it is only fair to provide proof for those statements by using some of the classes here. This game also demonstrates the use of ToolTips, which display on the desktop version but do not in Windows CE or Pocket PC (ToolTips are not supported in the Compact Framework). This helps support a potential white paper counter-argument that might say, "Yeah, but I want to build applications that are feature-rich on the desktop. Your method will make me always sacrifice the desktop application for those running on a handheld."

Desktop TicTacToe Game (with German selected)

Figure 3.

TicTacToe Game (running in the Pocket PC emulator.) The resource DLLs were not copied over for this screen shot, but if there were, a simple "Tools" MenuItem will appear and allow the user to select French, German, or English.

Figure 4.

  1. Chess. This is just a sample to show how a game is created, built and added to the game application without rebuilding the application exe. It is not added as a dependency to any of the applications, but simply creates a Chess.dll file which, when copied to the root executable directory, can be loaded and "played." The images/\Chess directory and contents should be copied over as well (they are there currently for the desktop only), which just provides a simple graphic for this game.

Desktop Chess Game (just loads an image for now and allows the user to exit)

Figure 5.

Adding a New Game

I don't expect anyone to create a new game for this framework as it is only a demo, but I should be able to give instructions on how to accomplish this anyway.

  1. Create a platform-agnostic DLL game library as discussed previously with a unique name from other available games.

    1. Let's call it "NewGameProject".
    2. (If this were a "real world" application, the unique name would probably not be required as it would be potentially too limiting. It is only required here so that one directory only needs to be searched for available games.)
  2. Add the following references to your library:

    1. GameInterfaces
    2. GameHelpers
    3. GameGraphicsHelpers
  3. Create a new game class in your new library.

  4. Let's call it "NewGame".

  5. NewGame must implement one default empty Constructor.

  6. NewGame must explicitly implement the GameInterfaces.IGame interface.

    1. For ease, have NewGame subclass the GameGraphicsHelpers.Panels.GraphicsPanelBase class and implement this base' required abstract methods.
    2. It should be noted that any Control minus a System.Windows.Forms.Form should be able to implement the IGame interface, but only the Panel has currently been tested.
  7. NewGame must implement an exit method:

    1. Must call "Remove" on main game application.
    2. Allows user to create and play other games.
  8. In your game, implement a static method with the following signature which returns a displayable name for your game.

    1. public static string GameDisplayName {get;} For example, TicTacToe just returns "TicTacToe."

      An interesting C# note: It is not possible to enforce a static method on an interface. When the actual application loads, it searches through all DLLs in the executable directory for Types that implement the IGame interface. For each Type it finds, it uses Reflection to make this GameDisplayName static call on that Type passing null as an object parameter to retrieve this name. A custom MenuItem is created and added to the controlling Panel's MainMenu for each game available, where this custom MenuItem knows how to dynamically create a new instance of the corresponding game by calling the required empty constructor. I did not want to create an actual game instance to get this name, and I did not want developers to have to add configuration information somewhere to load a game, as I prefer querying objects or Types at run time for information if possible. I felt that this was a reasonable compromise solution.

  9. Override any appropriate base class method.

    Look at the TicTacToeGame as an example.

  10. Implement any NewGame-specific code necessary to play.

  11. Compile your project and create the NewGame.dll at minimum and any other support files.

  12. Copy all necessary files to the game application directory.

  13. Restart the main game application and you should be able to play NewGame.

Sample Projects

There are 11 projects in this sample. Although the Compact Framework and Framework have excellent portable graphics support, it is still best to create library projects without graphics if possible. This allows other libraries or applications to import these libraries with greater flexibility. For example, say that you build a console application later that wants to use a library that includes graphics? And what if some library method opens a dialog box for the user? This would create problems. Isolating graphics only to those projects where graphics support is absolutely necessary allows the library reuse across all project types.

Project Name Information Game Dependencies
GameHelpers Provides support for loading files, resources, etc. None
TicTacToeBoard Provides logical game board and search. No graphics. Generally not thread-safe. (Microsoft recommends that most libraries should not be thread safe for many reasons, in particular performance issues.) None
GameInterfaces Defines interfaces for communication between the main game application and a game. Limited graphics; only added as return elements in the IGameGraphics interface. GameHelpers
GameGraphicsHelpers Provides some hand-coded custom Controls which provide default behavior for game and application creation GameHelpers

GameInterfaces

GameForm Provides a default Windows.Forms.Form class that platform-specific application's Form classes may subclass. Also provides a Panel controller for game creation. GameHelpers

GameInterfaces

GameGraphicsHelpers

Chess Stubbed game for demonstrating how a new game can be added without an application rebuild GameHelpers

GameInterfaces

GameGraphicsHelpers

TicTacToe Functional game for playing TicTacToe against the computer GameHelpers

TicTacToeBoard

GameInterfaces

GameGraphicsHelpers

GameCE, GamePPC, GameDesktop Platform-specific game applications that provide an entry point, and a small amount of custom behavior, if necessary GameHelpers

TicTacToeBoard

GameInterfaces

GameSolution Empty master project containing all above projects All projects above

GameSolution Project Loaded in the Visual Studio .NET 2003 (final beta) IDE. Note the new icon for Smart Device projects, displayed in the Solution Explorer panel. Most projects created are Smart Projects and are shared across platforms.

Figure 6.

Custom Class Support

There were some projects and classes created for generic Framework and Compact Framework support. These help isolate the developer from some platform-specific differences.

ToolTips

As stated earlier, the System.Windows.Forms.ToolTip class is not available on the Compact Framework. Although ToolTips are nice, they are not critical to an application. So, a GameHelpers.ClassProxy abstract class was created for support, which could be reused if necessary for other similar scenarios. When this abstract's constructor is called, it attempts to create an actual instance of the underlying desired class and hold it as a private variable. Two methods are currently supported: Invoke and InvokeNonSilent, where the former catches any Exception thrown if a call cannot be made on an actual object or null, while the latter requires the caller to catch any Exception.

A subclass should implement any method needed matching the signature of the actual class' corresponding method. It can then call into either of the base method's Invoke calls with necessary parameters, and if the actual class was created and the method can be called, it has expected behavior. If it cannot, it either fails silently or throws an Exception depending upon which call is made. Why should it follow the same signature? If the actual class eventually becomes available on all supported platforms then this subclass can simply be modified to subclass the actual .NET class later without breaking the client, is one reason. (Reflection is slow, in general, and provides little compile-time information to the developer. For these reasons, reflection should be used with care. Although it is very powerful and fun to use, it also carries extra risks that must be considered.)

A GameGraphicsHelper.ToolTips.GameToolTipBase abstract class was created which subclasses this ClassProxy class, and implements the SetToolTip method with the same signature as the actual .NET ToolTip method. When a GameToolTip class, which subclasses the GameToolTipBase, is instantiated, the ClassProxy class constructor will attempt to create an actual System.Windows.Forms.ToolTip instance, and will later display as usual on the desktop but fail silently on Windows CE or the Pocket PC.

All of this work for ToolTips was done to show one example for how to create rich applications for the desktop that also run on Windows CE or the Pocket PC without having to write platform-specific code.

GameInterfaces.IGameRefreshable interface

This interface was created for refreshing custom controls for internationalization in case the user changes to a different language at run time. Each IExtendableGame interface member must implement an IGameCulture interface, the latter requiring support for setting and getting the CultureInfo value or that interface (this was added because the System.Threading.Thread.CurrentCulture and CurrentUICulture methods are not supported in the Compact Framework). When the user selects a new language, an event is fired which propagates through all applicable game descendent controls implementing the IGameRefreshable interface. This event delegate includes the originating IExtendableGame interface where the event originated so that its CultureInfo value may be retrieved.

There was one other motivation for this scheme. It demonstrates that custom events can be created and fired that they work correctly on all .NET Frameworks.

GameHelpers.Paths abstract class

This class was created to handle directory and file access in a generic way, regardless of platform. Because Windows CE and Windows XP file systems are slightly different, this abstracts the problems for any client who calls the static methods it implements.

Downloading and Testing Sample Applications

If you want to play with the application, try the following steps. (There may be easier ways but only these were tried during testing).

To download the sample application

  1. Create a new directory called <drive>:\Modular

  2. Download the files and extract to this new directory.

  3. Open Visual Studio .NET 2003 (tested on final beta but not release).

  4. Run "File\Open\Project."

  5. With the Open Project dialog box, open <drive>:\Modular\GameSolution\GameSolution.csproj"

    When projects are loaded, 11 projects should display in the Solution Explorer panel).

    You may want to minimize each project node so that all projects are easily visible. This is one "feature" in the IDE that I don't like: It automatically opens all tree nodes when it opens projects. I believe that it would be better if the tree displayed the project nodes closed as default upon opening a project with multiple projects added.

  6. Right-click GameSolution project, and then click Rebuild.

    All projects should build with one message "Program . . .GameSolution.exe does not have an entry point defined," which is OK.

  7. To run the desktop version, right-click GameDesktop\Debug\Start New Instance.

  8. In the game, run File and New. Notice that TicTacToe is available.

  9. Close the game and application,

    1. Copy <drive>:\Modular\Chess\bin\Debug\Chess.dll to <drive>:\Modular\GameDesktop\bin\Debug
    2. Right-click GameDesktop\Debug\Start New Instance.
    3. In the game, run File and New. Notice that TicTacToe and Chess are available.
    4. If you select Chess, you can just see a dummy board.
    5. Only Exit is currently supported.
    6. If you select TicTacToe, you can play games against the computer.
    7. Run "File\Exit" to return to game panel for a different game.

To run the Pocket PC version in emulator (long-winded, but files must be copied currently by hand)

  1. Open Windows Explorer on the desktop.
  2. Create a network share on <drive>:\Modular\GameDesktop\bin\Debug.
  3. Return to IDE, and right-click GamePPC\Debug\Start New Instance.
  4. With the Deploy GamePPC dialog box, select Pocket PC 2002 Emulator (Default) and click the Deploy button.
  5. Emulator should open and download application and the Compact Framework
  6. This will take some time. Wait for the PPC Game to open.
  7. In the emulator, select "Start\Settings" and click the System tab.
  8. Click the About icon, and click the Device ID tab.
  9. Set Device name text field to something new and click OK.
  10. Close the Settings form and click Start and then Programs.
  11. Select the File Explorer icon and click the File Share icon on the Main Menu.
  12. In the Open path text field, type <name> of your computer on the network and click OK.
  13. The Network dialog box opens, "Connecting to Server."
  14. In the Network Log On form, enter the following:
    1. "User name:" text

    2. Enter your PC user name (same as windows log on)

    3. "Password:" text

    4. Enter your network PC password

    5. "Domain:" text

    6. Enter the domain of your PC (which I believe is optional).

    7. Click OK.

    8. Select PC file share file in emulator

    9. Select "Images" directory

    10. Select "Edit\Select All"

    11. Open the virtual keyboard and press CTRL+C.

    12. Select the Device icon on Main Menu and select "Program Files\GamePPC".

    13. Select "Edit\New Folder" and name New Folder "Images".

    14. Select the Images folder and select "Edit\Paste".

    15. Close the "File Explorer" Form and close the "Programs" Form.

    16. (Should be back in "PPC Game" application)

    17. Select "File\New\TicTacToe".

    18. The TicTacToe game should appear with graphics.

      Note   The Chess.dll file and all of the resources may be copied as well using similar methods)

To run Windows CE version in emulator

I have not figured out yet how to copy files to the emulator. The application will start, but I can't figure out how to copy the Image files over yet. Currently, I have tested in the Pocket PC emulator and a Pocket PC device, and an actual Windows CE .NET-based device only. I apologize for the lack of instructions here.

To run on handhelds (one solution that works)

  1. Connect device to computer with serial or USB cable.
  2. Establish an ActiveSync connection.
  3. Right-click either GamePPC or GameCE \Debug\Start New Instance.
  4. In the "Deploy <project name>" dialog box, select Pocket PC or Windows CE device and click the Deploy button.
  5. The Compact Framework should download and install on device if not installed already
  6. Corresponding game application and supporting DLLs should download to the correct \Program Files\<project name> directory on device
  7. Application should start automatically. Exit the application manually on the device.
  8. Open Windows Explorer on the desktop computer.
  9. Copy all <drive>:\Modular\GameDesktop\bin\Debug subdirectories to Mobile Device's \Program Files\<project name> directory. This copies the resource DLLs and Images to device
  10. Copy <drive>:\Modular\Chess\bin\Debug\Chess.dll to same directory as previous step. This is only necessary if you want to open the Chess game
  11. Restart the application manually on the device.

New "Deploy <project name>" dialog box in Visual Studio .NET for devices or emulation. This will open if a Smart Device application is created and debugged.

Figure 7.

Issues

There were naturally problems since beta software was used to create the sample projects. Some of these problems were created by my lack of knowledge and experience, but some may be actual issues in the beta software, and if so, will hopefully be fixed in the release version.

  1. CE .NET System.Windows.Forms.MainMenu. When adding a MainMenu to a Form running on Windows CE .NET 4.1, the Form.ClientSize is not adjusted to subtract the size of this MainMenu implicitly. I had to create a virtual method with a default implementation in the GameForm.GameFormBase abstract class so that the GameCE.Form1 class could override and handle this issue explicitly. If this is ignored, and a panel is added to the Form and given the full ClientSize, then the Panel covers the MainMenu in Windows CE, disallowing the user from selecting any MenuItems, at least on the device that I tested. Both the Pocket PC and desktop versions of the Framework seem to handle this implicitly for the developer, however.
  2. System.Windows.Forms.TabControl and TabPage. I had problems with the System.Windows.Forms.TabControl and TabPage on Windows CE or Pocket PC but had no problems with the desktop application. At one point, when I attempted to add any Control to a TabPage, the call stopped responding and never returned. I believe that the problem was created by settings or dependencies in my embedded projects, but I ran close on my deadline and removed these Controls for simplicity. I should remark that the TabControl and TabPage do seem to work in simple projects for the Compact Framework, however. This was and still is a mystery to me.
  3. System.Windows.Forms.ToolBar and ToolBarButton classes. Originally, I required any IGame implementer to return a ToolBar, where the default behavior was to have ToolBarButtons displaying national flags for language selection instead of creating a Tools MenuItem. The ToolBar worked correctly on the desktop, but on Windows CE or Pocket PC only the first button image would display correctly for some reason. For a "quick fix" I removed the ToolBars from the user interfaces (UIs), and created support for changing languages on a custom MainMenu control. I would prefer the ToolBar solution because, while most users will recognize flags, not all will understand two-character language codes. One workaround that I considered was to hand-code my own ToolBar class, which could be a Panel with Buttons and custom event handling, but I didn't have the time. I must admit that I am not fond of the ImageList solution anyway, because it is not a good object-oriented approach. I believe that there should (perhaps) be some ToolBarButtomImage subclass. So, a ToolBar could be created, then ToolBarButtonImage instances could be created with a constructor accepting a reference to some Image, and then each of these buttons could be added to the ToolBar in sequential order for display. I believe that requiring the developer to keep track of indices on the ImageList is not really a good solution, although it does seem to work as designed, at least with the Framework.
  4. When a platform-specific application is rebuilt without any obvious changes in projects shared between the application and some game, and the application is reinstalled on Windows CE or the Pocket PC, the game DLL must be rebuilt as well and copied over. I did not see this same behavior on the desktop version. It may be "as designed" and is probably caused by versioning on the Compact Framework.
  5. When a platform-specific game application is built, and the game is rebuilt as well, the game's resource files (debug\de\TicTacToe.resources.dll for example) copy over to the desktop version's application subdirectories correctly. But they are not copied over to the Windows CE or Pocket PC application directories, and must be moved by hand for either the emulator or the actual device. When these files are copied, however, the Compact Framework application seems to work correctly and load the correct resource. The lack of auto copying resource files to Compact Framework projects may be a "bug" in the beta software; it may be "as designed." At any rate, I am making an enhancement request for the next version, if not yet done.
  6. Some Compact Framework System*.dll and .xml files are copied to the desktop application directory during a build. After experimentation, I have found that these can be deleted and the application still runs correctly. This doesn't seem to be a real problem except it uses a little more disk space for the desktop project.
  7. System.Windows.Forms.MessageBox issues. I couldn't figure out how to center a MessageBox over the desktop application's main Form while coding against the Compact Framework, although I'm sure that it is possible and it may even be simple. There is a very small subset of the Show overloaded methods available for the Compact Framework, so I used the version that worked (the MessageBox does not center over the desktop version unless the application just happens to be centered on the screen currently). Because the supported Show methods seem to always place the MessageBox in the middle of the screen, they are centered over the Windows CE and Pocket PC application Forms by default.2 In addition, even though the TicTacToe game is internationalized, I couldn't figure out how to set the MessageBox Buttons to localized strings (the labels and messages are localized, however).
  8. Compact Framework Class support. There are some APIs that would really be nice if they were added to the Compact Framework. For example, look at the supported methods in the System.Threading.Monitor class. It is my opinion that this class is fairly important yet has very few methods available for the Compact Framework. Other examples can be found as well. What does this mean? You may have to create support yourself for these "missing" methods in some scenarios.
  9. Speed. Even though it is expected for managed code to run slower than native, Forms can be slow on Windows CE and Pocket PC devices, possibly even slower than you might expect. If you are building an application where speed is absolutely critical (and I mean "absolutely critical"), it might be a good idea to use eMbedded Microsoft Visual C++® and some of the ideas in my earlier C++ paper. But I believe that a managed-code solution is the best medium-term to long-term approach if possible because that seems to be the direction that Microsoft is taking. Future support for .NET should be excellent for any device (you may quote me on this easy prediction).
  10. System.Windows.Forms.Control.Invoke. This call is very important, particularly on Windows CE or Pocket PC devices and emulators. When you update a control on a separate thread from the thread which owns the underlying Control handle, you must call the Invoke method by passing a delegate, as properly documented online and some newsgroup help. 3 (BeginInvoke, EndInvoke, and InvokeRequired are not supported on Windows CE, unfortunately.) Also, only the empty Invoke method may be called; the overloaded version which accepts a parameter list is not supported either in the Compact Framework. I noticed that on the desktop, if you update a Control from a separate thread without calling a thread-safe method, the application usually "works" (even though it's really not supposed to), but on Windows CE and the Pocket PC, eventually your UI will stop responding. Lesson one: Just because it "works" on the desktop doesn't mean that your code is correct. I learned this one the hard way. Lesson two: It may actually be good to develop code in this manner because some coding errors may be more easily exposed when running on embedded devices.

Helpful Hints

After "playing around" with building portable solutions, I would like to recommend a few hints if you attempt to use the same or similar methods for real software products.

  1. Back up work. After you get a stable environment by testing on all platforms successfully, back up all of your work. That way you can return to a working version if you have problems later.
  2. Test early and often. As stated previously, your application may run perfectly on the desktop but may fail unexpectedly on Windows CE or Pocket PC. After making any major change, I would recommend testing on all platforms and emulators if possible to find any issues as soon as possible. I agree that testing is very slow on either a device or even the emulator, but it is not as slow as having to find some nasty bug that was coded two days earlier.
  3. Build simple test applications first. If you are going to try something new that you are unfamiliar with on any platform, it is a good idea to build a simple test project for that platform, and verify that the behavior is expected. This will isolate the current minor issue to solve from potential larger application issues and make testing easier.
  4. This is only a demo. It does not discuss real-world deployment, and it does not discuss any real-world build process. When building a commercial software product I can imagine some automated build environment where at least two full versions of software are created: one desktop and one embedded (and maybe even better, one each for Windows CE and Pocket PC). Maybe one source file set is copied, built and tested against the Framework for the desktop version; maybe source files are copied, built and tested against the Compact Framework for the Windows CE and Pocket PC version(s). This would be a good way to verify that no API is explicitly called for any platform that is unavailable for that platform. The preceding ToolTip example is different, because it calls a noncritical method implicitly, which is expected to fail silently using Reflection at run time on a Windows CE-based device, but it should compile without any problems for any device, which was the intent of the developer, or me. Creating platform-specific projects later would help expose unsupported explicit calls. I believe that without some formal process like this not all discrepancies can be caught unless all code branches, boundary conditions, and so on are exercised by some QA department on all platforms. This may be impossible to do, particularly with large software products and large function domains.
  5. All or only some of the ideas in this paper could be used. Remember: The purpose of this paper is to demonstrate that it can be done—it doesn't necessarily imply that it always or even should be done, which is an important distinction. For example, even though Microsoft is developing SPOT devices, I would not encourage anyone to build portable applications that can run on those as well because the framework library support for those devices is so limiting. However, I can imagine scenarios where a company building applications for Compact Framework and Framework devices would build some libraries that can be used on any platform, then create some platform-specific libraries as well. One situation where this may apply: A commercial desktop application is needed with all of the "bells and whistles," particularly when it has a full-featured graphical user interface (GUI) with many controls that are not supported by the Compact Framework. Although there may be workarounds to missing support on the Compact Framework, it may be easier to just build separate libraries for different platforms in some situations depending upon company needs.

Conclusion

In the preceding Motivation section, I explained the reasons why I wrote this article, but I didn't really argue why you should read it. You may ask, "It's all well and good for you, but why should I read this article? What do I get out of it? And why should I follow your recommendation?"

The bad news. The software industry is very competitive today, and it most likely will only get more competitive tomorrow. In general, if company A can build product P, company B can also build P provided that both companies have sufficient capital and skill sets available. Working independently, both companies' source code will be somewhat similar after final inspection, and both companies' final product will look even more similar if they each meet the original software requirements. From an economic perspective, this pushes the price of software down and creates a more competitive environment, particularly when more companies are fighting to build the same or fewer products. As the price goes down and salaries stay the same or rise, there will be fewer developers available to work on any future project to guarantee that some profit is made. This may all sound like theory from the "dismal science," but I think that this theory is unfortunately becoming reality in the software industry today.

The good news. Competition can sometimes create opportunity. I have seen little software, if any, that can run on both my phone running Pocket PC 2002 and my desktop running Windows XP. It would be great if I could buy a CD or download software off the Internet that will install and run on any Microsoft device that I own. In my Intel article I argued that software users only want to learn software once, and then have it behave in a similar fashion on whatever device it runs. This makes using some of these ideas appealing. There is, however, also an economic argument. If I see two companies that build software, and company A's software only runs on Windows XP and company B's software runs on both the Pocket PC and Windows XP, I will buy company B's software as long as company B's version is comparable in quality. I will even be willing to pay a little more for company B's software, or even accept somewhat lesser software, if I know that I really need it on both devices. I am sure that I am not the only person whose purchasing behavior will follow this pattern.

Naturally, not every software application should run on a Pocket PC. For example, I can't imagine that I would want to use a version of Visual Studio .NET on a handheld device, at least not given current hardware limitations. The fact that the screen is too –small –and –I –would –be required to –carry –a –portable keyboard seems to be reason enough not to even attempt it. However, there are many software products that should run on any device&151;games come to mind immediately.

If you follow this reasoning and agree with it, then it should be your motivation for attempting to gain a competitive edge with your company. If you build portable software using .NET and C# (or even use C++ and eMbedded Visual Tools if necessary) in cases where applications should run on any device, your market share should increase, and if one of your developers builds applications that will be more portable, you are making him more efficient, which should help in those scenarios where fewer developers are available for projects. Although I have determined that it is not easy to build portable applications across Microsoft platforms using either C++ or C#, I do believe that companies will be doing so even more in the future, and those who do might gain a real competitive advantage. There are already companies that have used Visual Studio .NET and the .NET Compact Framework to build applications that will run on multiple Microsoft operating systems. Intrinsyc built a jail surveillance system posted as a case study on the Microsoft site. I would assume that they used some similar methods that are introduced in this paper to accomplish this.

If you at least consider building portable applications using C# and Visual Studio .NET 2003, you should be able to meet my "selfish" goals (applications that I can install and run on any of my devices) and your own business needs (gaining an edge in your more competitive business climate). It would be great if the Bicycle games were eventually rebuilt using .NET, so that chess can run on my desktop and my phone. I provided the unfinished Chess game as motivation. I would be willing to buy that software again if it were done.

Additional Resources

For more information about Windows CE .NET, see the Windows Embedded Web site.

For online documentation and context-sensitive Help included with Windows CE .NET, see the Windows CE .NET product documentation.

For more information about Microsoft Visual Studio® .NET, see the Visual Studio Web site.

Notes

1 It probably could and should be argued that just installing the Compact Framework on a device will automatically consume about two megabytes. Most managed applications tend to be smaller than their native counterparts, however. I can imagine that a break-even point may be reached if enough different managed-code applications are installed on a device. I can also imagine that, in the future, many Windows CE and Pocket PC devices probably will and should ship with the Compact Framework preinstalled, which would eliminate this argument.

2 I believe that I know how to solve it but haven't tried it yet. More than likely, there is method supported on the Framework and not on the Compact Framework that allows you to center a MessageBox over any control. If this is true, an abstract class static method wrapper accepting necessary parameters could be created in the GameGraphicsHelpers, for example, that opens a MessageBox for the caller. Inside this method, Reflection could be used to try to open the version supported on the desktop with the necessary logic to center it. If that call fails, then the .NET Compact Framework version would be called directly. If this approach actually works and was implemented, when a MessageBox opens while running on the desktop, the desktop version would be called. On Pocket PC and Windows CE .NET, the .NET Compact Framework would be called. There is another solution. A custom control could always be built, but I will leave that for another day.

3 I'd like to thank Barrett Selfridge from Cymedix for answering a few questions for me on this issue.