VSM Cover Story
Update CF Apps Automatically
Simplify the administration and updating of Compact Framework applications by writing a Web service that facilitates checking for new files for a given app, as well as downloading and updating that app if a newer version exists.
- By John Gavilan
Technology Toolbox: C#, XML, Microsoft Compact Framework
Recent advancements in mobile devices and software development have opened up new possibilities that can increase productivity for mobile workers significantly.
Applications as trivial as e-mail and address books are already commonplace for Pocket PCs and Smart Phones. More advanced applications—including work order fulfillment reporting, photo capture, employee time tracking, expense reporting, and even GPS enabled apps—are now possible and developed easily using the .NET Compact Framework.
However, most of these apps face a crucial limitation that could hinder more widespread deployment of these types of enterprise applications: the ability to download updates to remote devices automatically whenever new versions of the application become available.
The most common way to update custom Pocket PC applications is to connect your device to a PC physically, uninstall the application using Active Sync, and then reinstall the application using the supplied MSI that loads the updated application to your Pocket PC or Smart Phone. Another way to perform an update is to navigate to the Pocket PC's file system using Active Sync, and then copying the updated binaries from your desktop to the Pocket PC's folders. The downside: This approach isn't practical across an enterprise or even a moderately-sized deployment base. Asking non-computer savvy users to do this would mean spending lots of time on helpdesk lines walking users through this potentially problematic process—and with few guarantees users would do exactly as they were told. After all, you are the expert on the installation process, not the users.
Fortunately, there is a better way. You can build your CF-based applications so they handle the process of updating themselves automatically. I'll walk you through how to do this, explaining how to create a Web service that your mobile app can connect to that checks whether new updates exist. I'll also explain how to develop your mobile app so that it checks this Web service for updates, downloads the latest version, and then executes the main application immediately after it downloads the updates (see Figure 1). I'll also point out potential 'gotchas' along the way, including possible glitches related to establishing a TCP/IP network connection.
The solution I describe in this article requires three major pieces: the Updater Web Service, the Compact Framework Updater application, and the main application itself. The Updater Web Service serves as the repository for the main application's latest binary files. You install the Compact Framework Updater application on each Pocket PC that you deploy your application to. The main application—the CF-based application that provides the specific functionality your users need—is installed by the updater application automatically. Note that your main application must be aware of the updater application before it can initiate any updates.
You can prompt an update in a couple of different ways. First, you can invoke the check for updates every time the main application starts, that will throw up a dialog if a new version of the application exists. Or, you can prompt the user to check for updates manually. Either approach will suffice in most circumstances, but it's more likely that users will have the correct version of a given app if you perform the check every time they launch your application.
I won't just describe the concepts required for implementing such a solution, I'll also provide the code you need to implement such a solution in your own applications. The CF-based main app that this article relies on is a relatively simple "Hello World" app, but the concepts are the same, regardless of a given Compact Framework app's complexity. The real work happens in the Web service and in the polling, downloading, and installation code you place in the main app—code that is easily reusable. Note that the code online includes the source code for both the Web service and the updater application, so you can modify the code to fit your specific situation and needs.
Create the Updater Web Service
The Updater Web Service is a .NET 2.0 Framework Web Service application that resides on a public Web server. This Web server exposes two public methods: GetFileInventory and GetBinaries.
When the GetFileInventory method is invoked, the Web service reads an internal XML configuration file. This file contains information on the main mobile application's file structure. The XML file includes an element for each of the binaries required by the main mobile app. These elements contain actual file paths pointing to the network location of the binaries, a special action attribute, and the location of each of the binary files you want to install on the mobile device:
The GetBinaries method takes an XML file that contains a list of files that you need to return. The Web service evaluates the list, and then opens each file on the list, transforming each file into a byte array, and, finally, converting this byte array into a Base 64 string. Note that you use Base 64 strings to safely transmit the binary data within an XML string. This encoding method uses only printable ASCII characters. The Web service then prepares a package in the form of an XML document to return. The base 64 strings are inserted as attribute values of the XML nodes in the XML package. After transforming and packaging all the files in the list, the Web service returns the XML package to the consumer as a string. (see Listing 1).
There are a couple of possible alternatives to this approach. The first is to gather all the files, compress all the files into a single ZIP file, and then return the zip file to the consumer as a byte array. The second alternative approach is to download each binary from the Web service as byte arrays, making one Web service call per binary that you need to download. The downside to this method is that you cannot ensure that you have performed all the necessary updates in one transaction.
The CF Updater application is a Compact Framework Windows application that has a Web reference to the Updater Web service. You need to deploy this application to the Pocket PC manually.
When the deployed app invokes the Update Web service, the Web service calls the GetFileInventory method, which returns a string representing the updater's main application's latest inventory list in a simple XML format.
When the Web service returns the new XML file to the application, it checks for the existence of a corresponding local XML file. If there isn't one, the updater knows to download all of the files on the list. The local corresponding file is created when all the updates are complete.
If the Web service finds a local XML file, the Web service compares its values with the values in the XML file that the server returned. The values to be compared include the binary locations, timestamps, and the special command. Any discrepancy discovered prompts the updater to bring down the server's versions to the local Pocket PC file system.
Note that you could alternatively check for the local file's existence, as well as timestamps using System.IO.File.Exists and System.IO.File.GetLastWriteTime to compare for discrepancies. However, maintaining this information in another XML file gives you the advantage of being able to compare timestamps as they actually are in the server. This enables you to proceed without having to worry about different time zone setups on individual Pocket PCs.
Determine Necessary Downloads
The combined information from the Web service-provided XML file and the local XML file gives the CF Updater enough information to determine whether it needs to download new binaries from the server. The updater then compiles a list of binaries that it needs to download and makes a second call to the Web service. This second call is a call to the GetBinaries Web method, which commands the Web service to return all the binaries, encoded as Base 64 strings, in a single XML file.
When the Pocket PC device receives the XML (with the Base 64 loaded strings), it converts the base 64 strings back to their original binary formats, and then places them in the proper location of the Pocket PC's file system, as specified by the original Web service XML configuration file. Once all the binaries have been copied successfully, the local XML file updates with the timestamps from the Web service. Throughout the download and conversion process, copies of the binaries you need to update are made and preserved throughout the update duration. Whenever an unexpected error occurs, such as a locked file that cannot be overwritten or a communications failure, you roll the files back to their original values.
After this whole cycle is complete, the mobile application's main binary executes using a PInvoke call to CreateProcess, with your main application's EXE file as a parameter. The updater application then terminates itself, just in time for the main application to load (see Figure 2).
The sample code includes a simple Hello World application. In a real world scenario, this would be your enterprise application, and it can be composed of multiple assemblies, configuration files, graphics, and everything else that an enterprise application needs to function. Keep in mind that immediately after your application invokes the updater, it needs to terminate itself to allow the updater to overwrite any files that need to be updated. When the updater has finished updating it should then re-invoke the main application.
Chances are that your environment isn't properly set up for debugging a Pocket PC application that communicates with a local Web service unless you have been creating Pocket PC apps in the recent past. Visual Studio does an excellent job with debugging Windows applications, Web applications, and virtually every other application type that it supports. However, debugging a Web service that is invoked by the Pocket PC Emulator can be difficult.
I'll try to help you avoid some of these problems by walking you through the debugging setup process. One important thing to note is that the Pocket PC Emulator is an actual Virtual PC Image of the Pocket PC operating System. Your developer machine is actually a Host to this virtual "Pocket PC." In order for this virtual Pocket PC to participate in a TCPIP network, and invoke the Web service living in your local machine, it must have a valid IP address, and it must be an actual participant in your network.
Participate in a TCP/IP Network
To participate in a TCP/IP network, you need to bind the Virtual Pocket PC's NE2000 network card to one of the network cards on your developer machine. Select Tools from the Visual Studio Main Menu Strip, and then select Device Tools | Devices | Pocket PC 2003 SE Emulator (see Figure 3).
Click on Properties, then click on Options and select the "Network" tab. Next, click on Emulator Options and Select the "Network" tab. Check the "Enable NE2000 PCMCIA Network Adapter" and select a connected network card.
I have experienced some issues trying to connect to the host local Web server when binding to a wireless network card for some reason, but selecting a non-wireless card has always worked flawlessly for me.
You are now ready to fire up the emulator. Click on the small Pocket PC to bring up the emulator. Once the emulator loads, go to the Configure Network Adapters Screen. Choose Start | Settings, and select the "Connections" tab. Next, click on "Network Cards." Select "The Internet" under "My network card connects to:"
The next step is to test your connectivity. Open Pocket Internet Explorer and enter the URL for your Web service. In my sample code I used http://10.2.22.178/ppcupdaterwebservice/ppcupdater.asmx. You will need to replace the IP Address with the current IP Address of your developer machine. Make sure that the Web service is accessible from your host machine on a regular browser before trying it on the emulator.
You are ready to debug once you establish a connection to the Updater Web Service on your host machine from your Pocket PC Emulator. Note that you need to create a virtual directory on your local IIS Web server that points to the Updater Web Service files. The emulator has difficulty connecting to the .NET 2.0 "mini Web server" that runs automatically whenever you debug a Web application or a Web service.
There are a handful of steps you can take if you still experience difficulties obtaining a connection to the updater Web service from the emulator. First, make sure that your host developer machine can browse to your updater Web service. Also make sure that all local firewalls are turned off on the network card that you are binding the emulator to. Finally, try performing a hard reset on the emulator device and starting over.
Click on the Debug button in Visual Studio to initiate debugging. This deploys the Pocket PC updater application files to the emulator's local file system automatically, and then executes it in debug mode.
When the Updater form finishes loading, click on the "Check for Updates" button, and trace through the code in Visual Studio to follow how the process works (see Figure 4).
This solution contains all the necessary components you need to deploy an Enterprise Compact Framework application without worrying about future updates. I am making the source code available for you to use and incorporate into your own applications. Microsoft has already provided this functionality out of the box for WinForms applications with ClickOnce, but has fallen short on providing the same capability for Compact Framework applications.
PDAs are becoming increasingly powerful, and it's no longer uncommon to expect these devices to have intermittent (or even permanent) connections to the Internet. Devices such as the Motorola Q, the Palm 700w, and the HP 6315 (to name a few) are perfect platforms to host the next generation of mobile applications. The techniques described in this article should enable you to take better advantage of such devices.