Christopher Tacke
OpenNETCF Consulting
August 18, 2005
So you’ve got the next killer embedded app written. The next step is to get it into the hands and onto the devices of users. With a desktop application it’s pretty simple – you create a deployment package project that generates a Microsoft Installer (MSI) package, and distribute it. Unfortunately it’s not quite so simple for a device. In this article we’ll look at what a Windows CE device needs to install an application, how to leverage Microsoft ActiveSync to make distributing easier, and how to use Visual Studio 2005 to make it all seamless.
Deploying an application to a device has a lot more variables that deploying a simple desktop application yet the process needs to be just as simple for the end-user. To make this happen it’s important to understand the overall process and then all of the variables that come into play. Once you understand that, you can put together any deployment, no matter how complex the underlying system gets.
There are essentially three major “systems” involved with a device deployment. The first is the desktop PC, the second is Microsoft ActiveSync and the third is the device CAB file extractor application called wceload.exe. Add to this the potential mix of different processors as well as a mix of managed and unmanaged code and you’ve got the deployment landscape. We’ll walk through each of these starting on the device itself.
To the end user, a device deployment is no different than a desktop application deployment. They download or receive an MSI file and run it. The Microsoft Installer steps through a few Wizard screens and then the application is installed.
From a developer’s point of view, however, the MSI is the final step in creating the deployment package. A CE device only knows how to handle a CAB file for installation, and the CAB file needs to get copied to the device with the option of allowing the user to deploy to more than one device or uninstall later.
To achieve this the general steps are:
1. Create CAB files for each potential class of target device (more on this later)
2. Register the CAB file(s) with ActiveSync so it can deploy from the desktop
3. Pack all of this information, along with anything else the app needs into a single distributable MSI file
We’ll take a look at each of these steps using a real-world example of deploying the OpenNETCF WiFiDiscovery application.
The first step in creating the deployment package is to generate the CAB files that actually get installed on the target device. If you’ve done much device development, or even installed many third-party applications on your device, you’re probably familiar with CAB files. Essentially a CAB is a compressed file that contains the application’s files, along with any necessary registry entries and some string resources for the on-device installer.
Since CE devices can differ in their OS version as well as processor architecture, most application install packages will contain several CAB files – one for each permutation of processor and OS version supported. For example, the Compact Framework 2.0 itself has seven CAB files.
At this point you might be tempted to ask “But managed applications use I, which is processor independent. Why can’t there just be one CAB file?” This is a good and very valid question. Visual Studio 2003 would always create several CAB files when you chose to have it generate the them. The reason is that an installer CAB may contain a file called setup.dll that contains special instructions for the installer to run when the app is installed or uninstalled. This DLL must be written in unmanaged code, and because of that, there must be different installs for the processor types and OS versions.
Fortunately, if your application has no special needs (like a GAC install) then you don’t need a setup.dll, and a single CAB file will work. That’s what we’ll take a look at for our sample.
So the first step is to add a Smart Device Cab Project to your application Solution. For our example we’ll name the project DeploymentCAB, as seen in Figure 1.

Figure 1 – Adding a new Device CAB project to your solution
Next we need to add files to the CAB. This can be done in a couple of ways: you can select an individual file you want to deploy, or you can select another project in the solution and have its output added to the CAB file. While the first method would work for every file, being able to add a project output is useful because it makes it easier to keep the CAB contents current with project changes, and it automatically adds all detected references. Select the DeploymentCAB project in the Project Explorer then Add -> Project Output. You’ll be presented with the dialog in Figure 2.

Figure 2 – Add the output of your application to the CAB
In our sample case, we’re referencing the OpenNETCF Smart Device Framework libraries, and they get automatically pulled in, which you can see in Figure 3.

Figure 3 – Project output and dependencies shown in the Project Explorer
We named our project “DeploymentCAB” because when looking at the solution it’s self-descriptive. However Studio uses the project name as the default name for the generated CAB file and deploying a CAB file to uses called “DeploymentCAB.cab” isn’t terribly friendly. We can alter the Project Properties to alter the actual output file name as seen in Figure 4.

Figure 4 – Set the name of the generated CAB file
Keep in mind that this only affects the output file name, and it must be done by selecting the project and then choosing Project Properties from the menu. It’s a bit counterintuitive that the Properties Window in the UI doesn’t set this. The settings in the Properties Window affect how the ActiveSync installer UI will present information like Product Name and Company.
A CAB file is actually generated by an application called “cabwiz.exe” on the PC, even in the Device Cab Project, Studio simply calls out to cabwiz.exe to generate the actual files. The information on what files and registry changes go into the CAB, along with text to display is contained in an INF file, which Studio will generate based on your project settings.
There is plenty of documentation online about the exact format of the INF file so we’ll not go into detail about it in this article, but it’s important to know that changes to the Properties Window (Figure 5) are reflected in the INF file, and we’ll see where that gets generated a little later.

Figure 5 – Modify the Properties window for setting install text
As I mentioned earlier, we can add registry entries into the CAB file as well. In the Project Explorer simply click on the ‘Registry Editor’ icon to get the editor UI to display then make the modifications. Figure 6 shows an example of adding a version number to the registry for our WiFiDiscovery application. Remember that entries you put into the Device Cab Project get applied to the target device, not the installing PC. If you need registry entries on the PC, those are done in one of the other projects which we’ll cover later.

Figure 6 – Add Your Install’s Device Registry Keys
Once you’ve added all the files and registry entries to your project and adjusted the Properties Window to reflect the information you want displayed by ActiveSync and the device CAB installer, go ahead and Build the project. It will generate an output like that seen in Figure 7: the CAB file, a LOG file (useful if the build fails) and the INF file (useful if the actual device install behaves differently than expected).

Figure 7 – Output Files from the CAB project
Once you’ve verified that the CAB file was generated you’re done with this phase of the deployment package. Next we’ll look at the pieces for the PC.
While just providing a CAB file for download is one way to get your application out, most users won’t know what to do with it, leading to support calls at best, or lost customers at the worst. The desktop installer package is where the “magic” that makes the installation friendly and professional looking to the end user.
For device applications there are two things that the installer needs to do. First, it must present the usual Wizard screens for the installation, but it also must deploy the application to a connected device without requiring additional steps for the user. In this section we’ll look at both of these steps.
Microsoft ActiveSync handles installing applications to a connected CE device using an application called “CeAppMgr.exe” which is installed with the ActiveSync application. CeAppMgr requires an INI file for an application to get registered with it for deployment, and unfortunately this INI file must be hand generated. Fortunately it’s not a complex file format.
Note: When you’re generating and testing your INI files, it can be extremely helpful to turn on debug output from CeAppMgr, which will provide simple MessageBox outputs that will report how it is parsing the INI file. To turn on debuggin set the following registry key on your PC:
[HKLM\Software\Microsoft\Windows CE Services\AppMgr]
“ReportErrors”=dword:1
Let’s take a look at the INI file used for registering WiFiDiscovery as an example. Not that line numbers were added for reference only – your INI file should not have them.
01 [CEAppManager]
02 Version = 1.0
03 Component = OpenNETCF WiFiDiscovery
04
05 [OpenNETCF WiFiDiscovery]
06 Description = Sample WiFi Network Discovery Application using the SDF
07 CabFiles = WiFiDiscovery.cab
Line 01 denotes that this is an INI file for CeAppMgr and is required.
Line 02 denotes the version of CeAppMgr that this file isupports. It is not the version of your application
Line 03 denotes the component name. The name used here must appear in square brackets later in the file (in this case it’s used on line 05)
Line 05 starts the component description section
Line 06 provides the text that will be displayed as the application description in the ActiveSync installer UI on the screen (we’ll see this later in Figure 17 when we test the deployment package).
Line 7 enumerates all of the CAB files used for this installation in a comma-separated list. This line must not have any spaces in it. As we learned earlier, CAB files internally hold the processor and OS version they support, so you simply need to list all of the CAB files. ActiveSync will device which to use when. These files can also be in subdirectories – if you choose to go that route, then the CAB file names use a relative path from the ActiveSync install folder (more on this later).
Again, since we’re trying to make our installation friendly and professional we want to follow some basic guidelines and standards. Applications registered with CeAppMgr.exe for installation usually lie in a subfolder of the ActiveSync installation folder (in fact logo certification requires that you put your app here).
Since an end user could have installed ActiveSync anywhere, and we don’t want them to have an option to install elsewhere we need our installation to determine where this path is and automatically put the necessary files where they should be. To achieve this we’ll use a feature of the Microsoft Installer that allows us to provide a class library that exports some custom actions to get automatically run at certain points during the installation process. In fact we’ll use this mechanism to launch CEAppMgr.exe with our INI file as well.
Simply add a new Windows Class Library project – we’ll name ours InstallerDLL – to the application solution (Figure 8).

Figure 8 – Add a new Windows Class Library
A custom installer class is a special type, so we won’t be using the default wizard generated ‘Class1’ so go ahead and delete it. Of course if you need a complex installation process there’s nothing that prohibits you from adding generic classes for use, we just won’t be doing anything that complex in this sample.
Now add a new Installer Class to the InstallerDLL project (Figure 9).

Figure 9 – Add an Installer Class to the Project
The Installer Class supports several events related to installation and uninstallation. For our example we’ll use BeforeInstall to copy our files to the ActiveSync folder and register with CEAppMgr.exe, we’ll use AfterInstall to clean up the files from the install path (no need to keep needless duplicate files) and we’ll use BeforeUninstall to remove the files from the ActiveSync folder, since the automatic uninstaller doesn’t know about files that we’ve copied elsewhere.
First we have to register for each of the events we’re interested in handling in the Class constructor:
public CustomInstaller()
{
InitializeComponent();
this.BeforeInstall +=
new InstallEventHandler(CustomInstaller_BeforeInstall);
this.AfterInstall +=
new InstallEventHandler(CustomInstaller_AfterInstall);
this.BeforeUninstall +=
new InstallEventHandler(CustomInstaller_BeforeUninstall);
}
We define some constants for use throughut the code:
private const string CEAPPMGR_PATH =
@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\CEAPPMGR.EXE";
private const string ACTIVESYNC_INSTALL_PATH =
@"SOFTWARE\Microsoft\Windows CE Services";
private const string CEAPPMGR_EXE_FILE = @"CEAPPMGR.EXE";
private const string CEAPPMGR_INI_FILE = @"WiFiDiscovery.ini";
private const string APP_SUBDIR = @"\OpenNETCF WiFiDiscovery";
private string TEMP_PATH =
Environment.SystemDirectory + @"\TEMP\WiFiDiscovery";
Since we’ll need to know where ActiveSync is installed so we can both install and uninstall our application, we need a helper method to extract this information from the registry:
private string GetAppInstallDirectory()
{
// get the ActiveSync install directory
RegistryKey keyActiveSync =
Registry.LocalMachine.OpenSubKey(ACTIVESYNC_INSTALL_PATH);
if (keyActiveSync == null)
{
throw new Exception("ActiveSync is not installed.");
}
// build the target directory path under ActiveSync
string activeSyncPath =
(string)keyActiveSync.GetValue("InstalledDir");
string installPath = activeSyncPath + APP_SUBDIR;
keyActiveSync.Close();
return installPath;
}
Now let’s look at the EventHandlers themselves. First, before installation we need to determine what the path for our installation under the ActiveSync folder will be. We determine this by calling GetAppInstallDirectory above, then we copy all of our application files to that directory.
Once copied, we have to determine where CeAppMgr.exe is installed – again this is retrieved from the registry. We call CeAppMgr.exe with the fully qualified path of our INI file as a parameter and this will register our app and start the ActiveSync installation wizard.
void CustomInstaller_BeforeInstall(object sender, InstallEventArgs e)
{
// find where we're going to be installed
string installPath = GetAppInstallDirectory();
// create the target directory
Directory.CreateDirectory(installPath);
// copy our app files to the directory
foreach (string installFile in Directory.GetFiles(TEMP_PATH))
{
File.Copy(installFile, Path.Combine(installPath,
Path.GetFileName(installFile)), true);
}
// get the path to ceappmgr.exe
RegistryKey keyAppMgr =
Registry.LocalMachine.OpenSubKey(CEAPPMGR_PATH);
string appMgrPath = (string)keyAppMgr.GetValue(null);
keyAppMgr.Close();
// run CeAppMgr.exe to install the files to the device
System.Diagnostics.Process.Start(appMgrPath,
"\"" + Path.Combine(installPath, CEAPPMGR_INI_FILE) + "\"");
}
After we’ve installed our application in the ActiveSync folder, we want to remove the files from the temp folder we initially installed to.
void CustomInstaller_AfterInstall(object sender, InstallEventArgs e)
{
// delete the temp files
foreach (string tempFile in Directory.GetFiles(TEMP_PATH))
{
File.Delete(tempFile);
}
}
Since we’ve moved files during the installation process, the Microsoft Installer has no idea they exist, so we must manually remove them. Again we determine the directory where the files were copied, then we iterate through and delete each.
void CustomInstaller_BeforeUninstall(object sender, InstallEventArgs e)
{
// find where we are installed
string installPath = GetAppInstallDirectory();
// delete the files
foreach (string appFile in Directory.GetFiles(installPath))
{
File.Delete(appFile);
}
// delete the folder
Directory.Delete(installPath);
}
Once we’ve got all of our EventHandlers written all that’s left for the InstallerDLL project is to compile it to generate a class library which we’ll consume in the final step.
Now for the final step: creating the actual MSI installer file. Here we’ll focus on the device-specific stuff, leaving things like wizard UI modifications and the like as exercises for the reader.
First, add a new Setup Project to the existing Solution (figure 10). For our sample we’ll name it DeploymentMSI.

Figure 10 – Add an Installer Project to the Solution
Again, it’s good form to update the Properties Window for the Application Name, Company, SupportUrl and the like. These items get displayed on the PC during the Install Wizard progression.
Again Studio sets the default output file name to the project name, which is often not very user friendly, so use the Project Properties menu item to open the dialog shown in Figure 11 and set the output file name.

Figure 11 – Set the property for the output file name
Next we need to set up where on the PC we’re going to put the files from the installation. As you may recall, we have to eventually put the files in a subfolder of ActiveSync, and we won’t know that until the installer is run and then only through a custom action, so in this phase we’ll simply place the files in a temp directory we create in the Windows Sytem folder called WiFiDiscovery. Simply use the File System Explorer of the Installer Project to generate the new folder.
Once we have the folder we can add any file content to it. Again, to make things simple we can add the output of another project in our solution instead of an explicit file. Figure 12 shows the dialog for adding the InstallerCAB project output.

Figure 12 – Add the CAB Project Output to the Installer
Next we want to set up the MSI to use the custom actions we created in the InstallerDLL project. First open the 'Custom Actions Editor' using the menu or the icon at the top of the Project Explorer Window, then select ‘Add Custom Action.’
Select our newly created temp folder and then use the Add Output dialog (Figure 13) to add the output of the InstallerDLL project.

Figure 13 – Add the Custom Installer DLL to the Installer
Once you’ve added the output the dialog should look like Figure 14.

Figure 14 – Verify that the Custom Installer has been added
And the Custom Action Explorer should show the output of InstallerDLL being used for all actions (Figure 15).

Figure 15 – Custom Actions applied to installer
Finally we add the INI file that CeAppMgr.exe uses as an explicit file and our project is ready to go (Figure 16).

Figure 16 – All Files added to the Installer
Go back and make sure you’ve built all of the other Projects in the Solution then build the DeploymentMSI project to create the output MSI file.
Finally we can test the installer from end to end. Make sure you have your CE device connected via ActiveSync, then right-click on the DeploymentMSI project in the Project Explorer and select ‘Install’ from the context menu (you’ll also see ‘Uninstall’ as an option which is useful for testing package uninstall processes). If all goes well, the Installer Wizard will begin and when you run through it CeAppMgr.exe will launch its installation dialog (Figure 17).

Figure 17 – ActiveSync’s Application Manager
Follow the couple dialogs it presents and you’ll have the WiFiDiscovery application installed on your mobile device.
Deploying a mobile application is still not a simple point and click process, especially if you want to get logo certification for your application and make it easy for your end users. In this article we covered the basic steps necessary for a typical installation. By no means does it cover every possibility or contingency, but depending on just how complex your application’s deployment needs are, these steps should either be everything you need for your install, or provide a solid foundation that you can modify. The hope is that when it’s time to work on the deployment package for your next killer application, you’ll at least know the path to follow for a successful rollout.