Deployment and Installation

After a Compact Framework application has been packaged into a .cab file, it must be deployed and installed on the device. There are several techniques for doing so, including using ActiveSync, a Web site, a storage card, a file share, and even creating an auto-updating application.

Using ActiveSync

Perhaps the most obvious option for deploying a Compact Framework application is to allow the application to be installed when the device is cradled using the ActiveSync software typically used to synchronize files and e-mail, as discussed in Chapter 6.

To do so, the application must be registered with the ActiveSync Application Manager installed on the desktop machine. This process is relatively simple; it requires creating a custom setup executable that invokes the Application Manager on the workstation with ActiveSync installed and passing it an .ini file that contains information about the application to install. For example, to install a simple application called MyApp.exe on the device, the bare-bones .ini file would look as follows[7] :

[7] The .ini file can also include entries to specify the icon that ActiveSync uses when displaying information about the application.


[CEAppManager]
Version      = 1.0
Component    = MyApp

[MyApp]
Description  = My Application
Uninstall    = MyApp
CabFiles     = MyApp_PPC.arm.cab

You'll notice that each application to be installed is configured in its own section within the .ini file. In this case, MyApp includes a short description used by the Application Manager when displaying the application in its UI, a pointer to the registry key name used by the Application Manager[8] to uninstall the application if need be, and a comma-delimited list of .cab files to deploy to the device and to install. In this case only the ARM .cab is shown, but others could be added and installed by the Application Manager as necessary.

[8] This key defaults to HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall.

The custom setup application then needs to invoke the Application Manager and pass it the path to the .ini file. To do so, a developer could write a simple desktop VB .NET application like the one shown in Listing 10-1.

Listing 10-1 A Custom Setup Application. This application invokes the Application Manager executable discovered from the registry and passes it the path to the .ini file.
Imports System.Diagnostics
Imports System.Reflection
Imports System.IO
Imports Microsoft.Win32

Public Class SetupApp

    Private Const INI_FILE As String = "\setup.ini"

    Public Shared Sub Main()
        Dim ceApp As String = GetCeAppMgr()
        Dim args As String = GetIniArgs()

        ' Start the Application Manager process
        Dim p As Process = Process.Start(ceApp, args)
    End Sub

    Public Shared Function GetCeAppMgr() As String
        ' Get the key from the registry
        Dim ceApp As String = KeyExists()
        If ceApp = String.Empty Then
            MessageBox.Show( _
         "The Application Manager is not installed on this machine.", _

             "Setup", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Application.Exit()
        Else
            Return ceApp
        End If

    End Function

    Public Shared Function GetIniArgs() As String
        Return """" & Application.StartupPath & INI_FILE & """"
    End Function

    Private Shared Function KeyExists() As String
        Dim key As RegistryKey = Registry.LocalMachine.OpenSubKey( _
     "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\CEAPPMGR.EXE")

        If key Is Nothing Then
            Return String.Empty
        Else
            Return key.GetValue("", String.Empty)
        End If
    End Function
End Class

You'll notice in Listing 10-1 that the program's execution begins in Sub Main and first calls GetCeAppMgr to retrieve the path to the Application Manager. This path is stored in the registry, and so, if it does not exist, the application throws up a message box and quits. Once the path is found, the Setup.ini file (hard-coded using a constant in this case) is formatted with double quotes and returned through the GetIniArgs method. Finally, both the path to the Application Manager and its argument are passed to the shared Start method of the Process class to start the Application Manager.

The custom setup application can then be packaged (along with the .ini and .cab files) using the Windows Installer in VS .NET 2003 and deployed on the machine that the device connects to. When the user double-clicks the setup application, the application will be deployed to the device.

Although this deployment and installation option is intuitive and provides a familiar mechanism to the user, it does require that the custom setup application be installed on the workstation used for synchronizing with the device.

Using a Web Site

The second option for deploying and installing a Compact Framework application is to use a Web site. Using this option, an organization could create a Internet or intranet site that contains links to the various .cab files created by SDP. This option has the benefit of not requiring the device to be cradled in order to install software.

When a user navigates to the Web page using Pocket Internet Explorer, he or she can tap on the required .cab file. The resulting dialog allows the file to be downloaded and opened. Opening the file after download is equivalent to executing the .cab file on the device and will cause the application to be installed.

NOTE

Regardless of how the .cab file reaches the device, the installation software on the device automatically deletes the .cab file once the installation is complete. To prevent this from happening, the .cab file can be marked as read-only.


To protect the .cab files, especially on a site accessible over the Internet, it is recommend that the virtual directory in which the download page resides be protected by standard HTTP encryption and authentication schemes. For example, the site could use basic authentication to authenticate the user and protect the virtual directory using SSL in order to encrypt both the credentials and the data. Alternatively, a VPN could be used as discussed in Chapter 9.

For scenarios where mobile applications need to be deployed dynamically and are location dependent (an application should be installed on the device when the device enters the WLAN available on a manufacturing floor, for example), third-party provisioning servers can be used. For example, the Appear Provisioning Server (APS) from Appear Network allows automatic delivery, single-click download, and automatic discard of location-specific applications targeted for health care, hospitality, transportation, and workforce coordination.

Using a Storage Card

graphics/key point_icon.gif

In many cases, the application needs to be deployed along with a SQLCE database or other software. As a result it can be both time-consuming and bulky to deploy such an application over the Internet or through a cradled connection.

For those reasons, the application can alternatively be deployed on a memory storage card, such as a Compact Flash card; however, rather than requiring the user to execute the .cab file once the storage card is inserted into the device, Pocket PC devices include an Autorun feature.

With this feature, when a storage card is inserted into the device, the Pocket PC looks for an executable called Autorun.exe in a folder mapping to the processor type of the device. For example, if the processor type is ARM, it will then look for the file \Storage Card\ARM\Autorun.exe on the storage card. When found, the executable is copied to the \Windows folder and executed with the install parameter. Likewise, when the card is removed from the device, the same executable is launched with the uninstall parameter.

As a result, the Autorun executable needs to perform the following steps:

  1. Determine which mode to run in. Because the application is passed either the install or uninstall command-line parameter, the Autorun application must first determine which of the two modes to run in. The rest of the steps discussed here assume the install mode.[9]

    [9] In uninstall mode, the application will perhaps not be uninstalled, but the Autorun executable could check to see if the application is currently executing and if so display a warning message or shut down gracefully.

  2. Verify that the application is not already installed on the device. This can be done by searching for the application's installation directory.

  3. Find the storage card name. Because storage card names can differ on localized devices (they won't always be called "storage card") and devices can support more than one storage card, techniques such as using the Windows CE FindFirstFile API function can be used.[10]

    [10] See the article "Pocket PC Programming Tips" by John Kennedy at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnroad/html/road03132002.asp

  4. Find the processor-specific .cab file. This can be done by first detecting the processor on the device and then mapping that to the appropriate directory on the storage card. The Windows CE GetSystemInfo returns the processor architecture in its SYSTEM_INFO structure.

  5. Execute the .cab file. This can be done using a Windows CE API, such as ShellExecuteEx.

Because the Compact Framework might not have been previously installed on the device, the Autorun.exe application is typically written using eMbedded Visual C. Further discussion is beyond the scope of this book.

Using a File Share

As with deploying through a Web site, the .cab files can be placed on a network (LAN) share and accessed from the File Explorer on the device, either wirelessly using 802.11 or when cradled using ActiveSync 3.5 and higher.

And, as with deployment through the Web, the share can be protected, and users will be forced to enter valid credentials to gain access to the files. Once again, this option frees the user from having to cradle the device in order to install software.

Setting Up Autodeployment

graphics/key point_icon.gif

Although they differ slightly from the previous deployment options covered in this chapter, Compact Framework applications, like those written with the desktop Framework, can be written to be auto-updating. By auto-updating we mean that the application checks file shares or Web sites either periodically or with each invocation for newer versions of its assemblies and dynamically downloads them. This technique is especially effective for applications that use private assemblies because the application can be partitioned into various functionalities, each housed in a separate assembly and, therefore, updated independently.

The obvious benefit to creating an auto-updating application is that it takes the burden of keeping the application up to date off the user.

To understand how such an application would work, consider the simple Loader class shown in Listing 10-2.

Listing 10-2 An Auto-updating Loader. This class can be used from a Compact Framework application to check automatically for new assemblies on a server and to download them.
Public Class Loader

    Protected _assemblies As New ArrayList
    Protected _net As String
    Protected _localPath As String

    Public Event Downloading(ByVal f As String)

    Public Sub New(ByVal networkPath As String, _
      ByVal assemblies As ArrayList)

        ' Grab the defaults
        _net = networkPath
        _localPath = (Path.GetDirectoryName( _
         [Assembly].GetExecutingAssembly.GetName.CodeBase) & _
         "\").Substring(6)

        ' Load the FileInfo objects
        Dim f As String
        For Each f In assemblies
            If File.Exists(_localPath & f) Then
                Dim fi As New FileInfo(f)
                _assemblies.Add(fi)
            End If
        Next

    End Sub

    Public Sub CheckForNewAssemblies()

        Try
            Dim fi As FileInfo
            For Each fi In _assemblies
                Dim serverTime As DateTime = _
                 Me.GetServerTime(_net & fi.Name)
                If serverTime > fi.LastWriteTime Then
                    ' Get the file
                    RaiseEvent Downloading(fi.Name)
                    Me.GetServerFile( _
                     _net & fi.Name, _localPath & fi.Name)
                End If
            Next
        Catch e As IOException
            ' Exception occurred
            Throw New Exception("Could not retrieve updates")
        End Try

    End Sub

    Protected Overridable Function GetServerTime( _
      ByVal f As String) As DateTime
        ' This method could be rewritten or overridden
        ' to check using HttpWebRequest
        Return File.GetLastWriteTime(f)
    End Function

    Protected Overridable Sub GetServerFile( _
      ByVal serverFile As String, ByVal localFile As String)
        ' This method could be rewritten or overridden
        ' to download using HttpWebRequest
        File.Copy(serverFile, localFile, True)
    End Sub
End Class

As Listing 10-2 shows, the Loader class exposes a public constructor that accepts the network path of files on the server, along with an ArrayList that holds the names of private assemblies to update if new versions are available on the server. The constructor creates an internal ArrayList that holds a series of System.IO.FileInfo objects to represent each of the private assemblies to inspect.

When the CheckForNewAssemblies method is called, it enumerates the assembly list and determines each assembly's date and timestamp on the server, using the GetServerTime method. If the server has a newer file, the file is downloaded using the GetServerFile method, and it replaces the existing file.

You'll notice that both the GetServerTime and GetServerFile methods are marked as protected and overridable (virtual in C#). This allows a class derived from Loader to override these methods and to use a different means of determining the date and timestamp of the server file and of downloading the file. This could be used to create an LoaderHttp class that uses the HttpWebRequest class to download new assemblies placed on an intranet.

A bootstrap application or a method in an executable could then use the loader as follows:


Dim net As String = "\\myserver\updates\"
Dim s As String = "mydll.dll"
Dim a As New ArrayList
a.Add(s)

Dim l As New Loader(net, a)
l.CheckForNewAssemblies()