The Desktop Side of Mobile Computing

 

Larry Roof

March 2004

Applies to:
   Microsoft® .NET Compact Framework
   Windows Mobile™-based Pocket PCs
   Windows Mobile-based Smartphones
   Microsoft ActiveSync®

Summary: Learn about data storage options working with the Remote API or RAPI and how it can be incorporated into applications developed with the .NET Compact Framework. (21 printed pages)

Contents

Introduction
The Remote API Library
Classes Provided through the Communication Namespace
Working with OpenNETCF's Communication Library
The RAPI Class
Working with the RAPI Class
Connecting to the Device
Working with Files
Launching an Application
Retrieving System Information
Jump Start Your .NET Compact Framework Development
Back on the Road

Introduction

In my last article, I presented the position that mobile computing is all about options. In that article I focused on data storage option, specifically Pocket Access databases, and how they could be incorporated into applications developed with the .NET Compact Framework.

In this article, I'm going to continue with my theme about options, but with a twist. I'm going to introduce you to the desktop side of mobile computing, a perspective that is frequently ignored by novice mobile developers. Many developers get so consumed by the mobile aspects of their applications that they overlook how a desktop or server application can be part of their mobile solutions. Obviously, most of a mobile solution is based on the device, but in almost all cases eventually you will need to transfer data to and from the device. That includes data gathered by the device, data delivered from a server for reference purposes, configuration data and even application updates. In this article I'm going to walk you through a simple desktop application that leverages the Remote API to:

  • Copy files to and from a device.
  • Move files on a device.
  • Delete files from a device.
  • Launch an application on a device.
  • Retrieve system information from a device.

Starting with this article and going forward the examples that are part of the "Two for the Road" column will be shown in both Microsoft Visual C#® .NET and Visual Basic® .NET.

The Remote API Library

This month's sample application demonstrates the power of the Remote API, or RAPI. This API is one of the key components of ActiveSync. RAPI is a library comprising a set of functions that can be used to manage a device from a desktop application, including the device's directories and files, its registry and system information.

To simplify working with the Remote API, we'll use the OpenNETCF organization's Communication library, an open-source resource that you can obtain from the OpenNETCF website. This library takes all of the heartache out of dealing with RAPI, providing you with a set of methods which expose all of the features RAPI has to offer.

Classes Provided through the Communication Namespace

The OpenNETCF.Desktop.Communication namespace offers the following classes listed in Table 1.

Table 1. Classes offered in the OpenNETCF.Desktop.Communication namespace

Class Description
ActiveSync Provides access to the functions underlying Microsoft ActiveSync.
AutoStartApps Provides information on and the ability to configure applications that are run when an ActiveSync session commences.
FileInformation Describes the file information structures returned from the FindFirstFile and FindNextFile methods.
FileList A collection of FileInformation classes. Frequently used by a desktop application to query and manage the file structure of a connected device.
RAPI Provides a convenient wrapper to the base RAPI functions.
RAPIException Defines the exceptions thrown by the RAPI class.
Registry Provides access to the base RegistryKey classes, which in turn can be used to access and modify value and subkeys in a connected device's registry.
RegistryKey Defines a key-level node in a connected device's registry.

As the table above shows, the OpenNETCF.Desktop.Communication namespace offers a wealth of desktop functionality, far more than could be covered in detail in a single article. For this purpose of this article, I'm going to focus on the RAPI class, and particularly three key parts of this class' capabilities:

  • Management of the device's directories and files.
  • Launching an application on the device.
  • Retrieving system information from the device.

My reason for selecting these specific items is that I've found this trio particularly useful in the development of mobile solutions.

Working with OpenNETCF's Communication Library

The OpenNETCF Communication library is delivered through the file OpenNETCF.Desktop.Communication.DLL. You will need to include a reference to this library within your desktop application. To add the reference, perform the following steps:

  1. In the Solution Explorer window, right-click the References folder. A pop-up menu displays.
  2. From the menu, select Add Reference.
  3. The Add Reference dialog box displays. Select the OpenNETCF.Desktop.Communication component.

Click the OK button to add the selected component to your project.

The RAPI Class

Table 2 provides a list of the RAPI class methods that I've found to be most useful in developing mobile applications.

Table 2. Useful RAPI class methods for developing mobile applications

Method Description
Connect Establishes a synchronous connection to a device.
CopyFileFromDevice Copies a file from the device to a PC.
CopyFileOnDevice Copies a file from one location on a device to a new location on the device.
CopyFileToDevice Copies a file from a PC to a device.
CreateDeviceDirectory Creates a directory on a device.
CreateProcess Launches an application on a device.
DeleteDeviceFile Deletes a file from a device.
DeviceFileExists Checks whether a file exists on a device.
Disconnect Terminates a connection to a device.
EnumFiles Provides an ArrayList of FileInformation classes that match the criteria provided by the FileName parameter.
GetDeviceCapabilities Retrieves device-specific information about a device.
GetDeviceFileAttributes Retrieves the attributes for a specific device file.
GetDeviceFileSize Retrieves the size in bytes of a file on a device.
GetDeviceFileTime Retrieves the date and time of a file on a device.
GetDeviceMemoryStatus Retrieves information on a device's memory usage.
GetDeviceSystemFolderPath Retrieves the path to the system folder on the device.
GetDeviceSystemInfo Retrieves system information details from a device.
GetDeviceSystemPowerStatus Retrieves the power status from a device.
GetDeviceVersion Retrieves the operating system version from a device.
MoveDeviceFile Moves or renames an existing device file to a new location.
RemoveDeviceDirectory Deletes a directory from a device.
SetDeviceFileAttributes Sets the attributes of a file on a device.
SetDeviceFileTime Sets the date and time of a file on a device.

Working with the RAPI Class

To simplify working with the RAPI class, I've added a using statement to the C# version of the sample and an Imports statement to the VB.NET version as shown below:

[VC#.NET]
using OpenNETCF.Desktop.Communication;

[VB.NET]
Imports OpenNETCF.Desktop.Communication

In addition, I've declared a single module-level variable, myrapi, which is used to hold an instance of the RAPI class.

[VC#.NET]
// Declare an instance of the RAPI object.
RAPI myrapi;

[VB.NET]
' Declare an instance of the RAPI object.
Dim WithEvents myrapi As New rapi

Connecting to the Device

The first step that your desktop application needs to perform when working with the RAPI class methods is to establish a connection to the device.

Note The use of RAPI from a desktop application requires that there be an active ActiveSync connection between your PC and the device.

In the sample application included with this article, the connection to the device is established in the Form_Load event. To connect to a device, you use the Connect method of the RAPI class. As the following code shows, I immediately followed up with a check of the RAPI classes' DevicePresent property to verify that the connection was successful.

[VC#.NET]
try
        
//  Connect to the device.
{
  myrapi.Connect();
  while (! myrapi.DevicePresent)
  {
    MessageBox.Show("Please connect your device to your PC using ActiveSync and before clicking the OK button.", 
      "No Device Present");
    myrapi.Connect();
  }
}

catch (Exception ex)
{
  MessageBox.Show("The following error occurred while attempting to connect to"+ " your device - " + ex.Message, 
    "Connection Error");
  Application.Exit();
}

[VB.NET]
Try
' Connect to the device.
  myrapi.Connect()
  Do While Not myrapi.DevicePresent
    MessageBox.Show("Please connect your device to your PC using ActiveSync and " & _
      "before clicking the OK button.", "No Device Present")
    myrapi.Connect()
  Loop

Catch ex As Exception
  MessageBox.Show("The following error occurred while attempting to connect to" & _
    " your device - " & ex.Message, "Connection Error")
  Application.Exit()
End Try

With a connection established, we are ready to begin exploring the functionality offered through RAPI. We will start by examining how you can manage directories and files of a device from your desktop application.

Working with Files

RAPI provides a plethora of functionality for working with directories in files. I've chosen to demonstrate three file-related capabilities: copying files to and from a device, moving files on the device, and deleting files from a device. I will look at copying files first.

Copying Files To and From a Device

One of the easiest methods to move data to and from a device is to simply copy a text or XML file between a device and a PC; one option is to use the RAPI Demo program shown in Figure 1. Text and XML-based files can be used in mobile applications as a simple way to store application data or configuration data.

Figure 1. The Copy File tab of the RAPI demo program.

The OpenNETCF.Desktop.Communication namespace RAPI class provides two methods for copying files — CopyFileToDevice and CopyFileFromDevice. Both of these methods expect the source file as the first argument and the destination file as the second argument.

The click event procedure for the btnCopyPerform button demonstrates both of these methods. Which method is called, CopyFileToDevice or the CopyFileFromDevice, is dependent upon the direction the user selects from the combo box on the user interface.

[VC#.NET]
private void btnCopyPerform_Click(object sender, System.EventArgs e)
      {
      
// Perform the copy.
  try
  {
    if ((txtCopySource.Text == "") || (txtCopyDestination.Text == ""))
    {
      MessageBox.Show("You must provide both a source and destination file.",
        "Missing File Information");
    }
    else
    {
   switch (cmbCopyDirection.Text)
   {
        case "":
       MessageBox.Show("You must select a direction before initiating the copy.",
         "No Destination Selected");
        break;
     case "from desktop to device":
          myrapi.CopyFileToDevice(txtCopySource.Text, txtCopyDestination.Text);
       MessageBox.Show("Your file has been copied.", "Copy Success");
       break;
     case "from device to desktop":
          myrapi.CopyFileFromDevice(txtCopySource.Text, txtCopyDestination.Text);
       MessageBox.Show("Your file has been copied.", "Copy Success");
       break;
     }
      }  
    }
      
// Handle any errors that might occur.
  catch (Exception ex)
  {
    MessageBox.Show("The following error occurred copying the file -" + ex.Message,
      "Copy Error");
  }
}

[VB.NET]
Private Sub btnCopyPerform_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCopyPerform.Click

' Perform the copy.
  Try
    If (txtCopySource.Text = "") Or (txtCopyDestination.Text = "") Then
      MessageBox.Show("You must provide both a source and destination file.", _
        "Missing File Information")
      Exit Sub
    End If

    Select Case cmbCopyDirection.Text
      Case ""
        MessageBox.Show("You must select a direction before initiating the copy.", _
          "No Destination Selected")
        Exit Sub
      Case "from desktop to device"
        myrapi.CopyFileToDevice(txtCopySource.Text,  txtCopyDestination.Text)
      Case "from device to desktop"
        myrapi.CopyFileFromDevice(txtCopySource.Text,  txtCopyDestination.Text)
    End Select

    MessageBox.Show("Your file has been copied.", "Copy Success")

' Handle any errors that might occur.
  Catch ex As Exception
    MessageBox.Show("The following error occurred copying the file -" & ex.Message, _
      "Copy Error")
  End Try

End Sub

Moving Files on a Device

There may be situations where you need to move or rename a file on the device. For example, you may want to make a backup copy of a configuration file before copying a new version of the file to the device; one option is to use the RAPI Demo program shown in Figure 2.

Figure 2. The Move File tab of the RAPI demo program

The OpenNETCF.Desktop.Communication namespace RAPI class provides the MoveDeviceFile method for moving or renaming files. As with the CopyFile methods, this method expect the source file as the first argument and the destination file as the second argument.

The click event procedure for the btnMovePerform button includes demonstration for both of the MoveDeviceFile method.

[VC#.NET]
private void btnMovePerform_Click(object sender, System.EventArgs e)
{
//  Perform the move.
  try
  {
    if ((txtMoveSource.Text ==  "") || (txtMoveDestination.Text ==  ""))
    {
      MessageBox.Show("You must provide both a source and destination file.", 
        "Missing File Information");
    }
    else
    {
      myrapi.MoveDeviceFile(txtMoveSource.Text, txtMoveDestination.Text);
      MessageBox.Show("Your file has been copied.","Copy Success");
    }
  }

// Handle any errors that might occur.
  catch (Exception ex)
  {
    MessageBox.Show("The following error occurred moving the file " + 
      ex.Message,"Connection Error");
  }

}

[VB.NET]
Private Sub btnMovePerform_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMovePerform.Click

' Perform the move.
  Try
    If (txtMoveSource.Text = "") Or (txtMoveDestination.Text = "") Then
      MessageBox.Show("You must provide both a source and destination file.", _
        "Missing File Information")
      Exit Sub
    End If

    myrapi.MoveDeviceFile(txtMoveSource.Text, txtMoveDestination.Text)

    MessageBox.Show("Your file has been copied.", "Copy Success")

' Handle any errors that might occur.
  Catch ex As Exception
    MessageBox.Show("The following error occurred moving the file -" & ex.Message, _
      "Move Error")
  End Try

End Sub

Deleting Files from a Device

Often times you will find that you will use the RAPI copy file method in conjunction with the delete file method. For example, you may have your desktop application copy a file that the application used to store data from the device, and then upon successful performance of that copying task, turn right around and delete the file from the device so that the mobile application is ready to begin again fresh. One option is to use the RAPI Demo program shown in Figure 3.

Figure 3. The Delete File tab of the RAPI demo program

The OpenNETCF.Desktop.Communication namespace RAPI class provides the DeleteDeviceFile method for deleting a device file. The file you want to delete is the first argument for this method.

The click event procedure for the btnDeletePerform button includes demonstration of the DeleteDeviceFile method.

[VC#.NET]
private void btnDeletePerform_Click(object sender, System.EventArgs e)
{

// Perform the delete.
  try
  {
    if (txtDeleteFile.Text == "")
    {
      MessageBox.Show("You must provide a file to delete.",
        "No File Provided");
    }
    else
    {
      myrapi.DeleteDeviceFile(txtDeleteFile.Text);
      MessageBox.Show("Your file has been deleted.", "Delete Success");
    }
  }

// Handle any errors that might occur.
  catch (Exception ex)
  {
    MessageBox.Show("The following error occurred while deleting the file -" + 
      ex.Message, "Delete Error");
  }      
}

[VB.NET]
Private Sub btnDeletePerform_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDeletePerform.Click

' Perform the delete.
  Try
    If (txtDeleteFile.Text = "") Then
      MessageBox.Show("You must provide a file to delete.", _
        "No File Provided")
      Exit Sub
    End If

    myrapi.DeleteDeviceFile(txtDeleteFile.Text)

    MessageBox.Show("Your file has been deleted.", "Delete Success")

' Handle any errors that might occur.
  Catch ex As Exception
    MessageBox.Show("The following error occurred while deleting the file -" & ex.Message, _
      "Delete Error")
  End Try

End Sub

Now that we've looked at the common trio of file-related methods, copy, move and delete, next we'll look at how to launch an application on the device from a desktop application.

Launching an Application

There is a variety of reasons why you may want to incorporate launching an application on a device from a desktop application. You can use this technique to:

  • Install a new version of your application. Simply copy the CAB file to the device and then run the CAB installer on the device to perform the installation. This technique is useful in situations where you want to automate the delivery and installation of application updates.

    Note Another alternative to this approach would be to automate the desktop side of the installation process, leveraging the inherent functionality found in ActiveSync.

  • Restart your mobile application on the device after installing a new version of the application.

  • Start a device application that will process new data updates that were pushed down in a text or XML-based file.

One option is to use the RAPI demo program as shown in Figure 4.

Figure 4. The Launch Application tab of the RAPI demo program

The OpenNETCF.Desktop.Communication namespace RAPI class provides the CreateProcess method for launching a device file. The device application you wish to launch is the first argument for this method. You can pass a command line to be processed by the application as the method's second argument.

The click event procedure for the btnLaunchPerform button includes demonstration of the CreateProcess method.

[VC#.NET]
private void btnLaunchPerform_Click(object sender, System.EventArgs e)
{
            
// Perform the launch.
  try
  {
    if (txtLaunchFile.Text == "")
    {
      MessageBox.Show("You must provide a file to launch.",
        "No File Provided");
    }
    else
    {
      myrapi.CreateProcess(txtLaunchFile.Text, txtLaunchCommand.Text);
      MessageBox.Show("Your file has been launched.", "Launch Success");
    }
  }
    
// Handle any errors that might occur.
  catch (Exception ex)
  {
    MessageBox.Show("The following error occurred while launching the file -" +
      ex.Message, "Launch Error");
  }      
}

[VB.NET]
Private Sub btnLaunchPerform_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLaunchPerform.Click

' Perform the launch.
  Try
    If (txtLaunchFile.Text = "") Then
      MessageBox.Show("You must provide a file to launch.", _
        "No File Provided")
      Exit Sub
    End If

    myrapi.CreateProcess(txtLaunchFile.Text, txtLaunchCommand.Text)

    MessageBox.Show("Your file has been launched.", "Launch Success")

' Handle any errors that might occur.
  Catch ex As Exception
    MessageBox.Show("The following error occurred while launching the file -" & ex.Message, _
      "Launch Error")
  End Try

End Sub

We've got one last RAPI-related topic to examine, retrieving system information. As you will see in the following section, there are a handful of methods provided through the RAPI class that can be used to retrieve details on the connected device.

Retrieving System Information

Having the ability to retrieve specific system information of a connected device can enable your desktop application to deliver content or to alter functionality based upon:

  • The processor being used by the connected device, which is frequently applicable when pushing CAB files to the device that contain processor-specific files.

    Note This technique is most useful in environments where you are deploying applications to earlier versions of Pocket PC devices, as Windows Mobile devices are all based upon the ARM processor.

  • The version of the operating system running on the connected device, which as with the processor type is commonly used to deliver the appropriate file updates.

  • The power status of the connect device, which can be used to alert the user that their device is running low on power before it is taken into the field.

  • The memory status of the connected device, which can be used to determine whether data has been offloaded, if the user has loaded unauthorized applications or other memory-related functions, and whether you have enough room to install updates to your application.

One option is to use the RAPI Demo program, as shown in Figure 5.

Figure 5. The Device Information tab of the RAPI demo program

The four methods provided by the RAPI class for retrieving this information are GetDeviceSystemInfo (processor-type), GetDeviceVersion (operating system version), GetDeviceSystemPowerStatus (power status) and GetDeviceMemoryStatus (memory).

The click event procedure for the btnInfoRetrieve button includes demonstration of each of these methods.

[VC#.NET]
private void btnInfoRetrieve_Click(object sender, System.EventArgs e)
{
  string info;
  MEMORYSTATUS ms;
  SYSTEM_INFO si;
  SYSTEM_POWER_STATUS_EX sps;
  OSVERSIONINFO vi;

// Retrieve the system information.
  myrapi.GetDeviceSystemInfo(out si);

// Retrieve the device OS version number.
  myrapi.GetDeviceVersion(out vi);

// Retrieve the device power status.
  myrapi.GetDeviceSystemPowerStatus(out sps);

// Retrieve the device memory status.
  myrapi.GetDeviceMemoryStatus(out ms);

// Format the retrieved information.
  info = "The connected device has an ";
  switch (si.wProcessorArchitecture)
  {
  case ProcessorArchitecture.Intel:
    info += "Intel processor.\n";
    break;
  case ProcessorArchitecture.MIPS:
    info += "MIPS processor.\n";
    break;
  case ProcessorArchitecture.ARM:
    info += "ARM processor.\n";
    break;
  default:
    info = "unknown processor type.\n";
    break;
  }

  info += "OS version: " + vi.dwMajorVersion + "." + vi.dwMinorVersion + "." + 
    vi.dwBuildNumber + "\n";
  if (sps.ACLineStatus == 1)
  {
    info += "On AC power: YES\n";
  }
  else
  {
    info += "On AC power: NO \n";
  }
  info += "Battery level: " + sps.BatteryLifePercent + "%\n";
  info += "Total memory: " + String.Format("{0:###,###,###}",  ms.dwTotalPhys) +
    "\n";

// Display the results.
  lblInfo.Text = info;   
}

[VB.NET]
Private Sub btnInfoRetrieve_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnInfoRetrieve.Click
  Dim info As String
  Dim ms As New MEMORYSTATUS
  Dim si As New SYSTEM_INFO
  Dim sps As New SYSTEM_POWER_STATUS_EX
  Dim vi As New OSVERSIONINFO

' Retrieve the system information.
  myrapi.GetDeviceSystemInfo(si)

' Retrieve the device OS version number.
  myrapi.GetDeviceVersion(vi)

' Retrieve the device power status.
  myrapi.GetDeviceSystemPowerStatus(sps)

' Retrieve the device memory status.
  myrapi.GetDeviceMemoryStatus(ms)

' Format the retrieved information.
  info = "The connected device has an "
  Select Case si.wProcessorArchitecture
    Case ProcessorArchitecture.Intel
      info += "Intel processor." & vbCrLf
    Case ProcessorArchitecture.MIPS
      info += "MIPS processor." & vbCrLf
    Case ProcessorArchitecture.ARM
      info += "ARM processor." & vbCrLf
    Case Else
      info = "unknown processor type." & vbCrLf
  End Select

  info += "OS version: " & vi.dwMajorVersion & "." & vi.dwMinorVersion & "." & vi.dwBuildNumber & vbCrLf
  info += "On AC power: " & IIf(sps.ACLineStatus = 1, "YES", "NO") & vbCrLf
  info += "Battery level: " & sps.BatteryLifePercent & "%" & vbCrLf
  info += "Total memory: " & String.Format("{0:###,###,###}",  ms.dwTotalPhys) & vbCrLf

' Display the results.
  lblInfo.Text = info

End Sub

That concludes our brief introduction to the Remote API and how desktop applications can be integrated into your mobile solutions. I would suggest that you take the time to examine the sizable remaining functionality provided through the OpenNETCF.Desktop.Communication namespace. Remember, it's all about options and the OpenNETCF namespace provides a wide-variety of options for enhancing your handheld applications.

Jump Start Your .NET Compact Framework Development

Want to get up to speed with the .NET Compact Framework in a week? My training can get you there. I offer a five-day NETCF class that provides you with everything you need to get up and running quickly. I show you how to create robust mobile solutions using the .NET Compact Framework, SQL Server CE, and XML. A detailed outline is available at www.larryroof.com.

If you cannot get away for a face-to-face class check out my new book The Definitive Guide to the .NET Compact Framework from Apress. It provides a comprehensive overview of how to create mobile applications with the .NET Compact Framework.

Back on the Road

That's it for another month. Spring training is just around the corner, so I'll be packing up my surf board and Pocket PC and heading to sunny Florida. In my next article we'll be examining more options for the mobile developer.