Introduction
The objective of this article is to describe a solution that uses threading and ActiveSync to automate the process of installing multiple CAB files to a Windows Mobile or Pocket PC device.
The included example setup project is a standard Windows MSI installation file which when run will install two CAB files to a Windows Mobile or Pocket PC device.
The Custom Action included in the project is responsible for the installation of the CAB files to the device, and it installs each of the CAB files, one at a time, consecutively using ActiveSync. Each installation is run in a separate thread, and the main process waits until each installation has completed before proceeding with the next installation.
For the purposes of this example, I have included two CAB files, but the Custom Action can easily be extended to install as many CAB files as you wish. The included CAB files will install the .NET Compact Framework 3.5 and SQL Mobile 2.0.
Description
The Custom Action works by overriding and extending the default functionality of the Installer
class. The overridden Commit
method of the installer class first calls the base Commit
method and then proceeds to implement the custom functionality.
Broadly, the custom functionality works as below:
- All the CAB files are copied by the installer to the initial installation target directory. The MSI does this automatically.
- The Custom Action then initializes an
InstallParams
object which takes an AutoResetEvent
object (used to signal when the thread has terminated) and a string which identifies the initialization (.ini) file of the CAB to be installed.
- The method
RunInstallerTask
and the InstallParams
object are used to fire up a new thread through a call to ThreadPool.QueueUserWorkItem
.
- The new thread fires up a process, i.e., executes a program, in this case ActiveSync. If ActiveSync is installed, the default installation directory of ActiveSync is retrieved from the Registry key: HKEY_LOCAL_MACHINE\Software\microsoft\windows\currentversion\app paths\ceappmgr.exe.
- While ActiveSync executes and installs the target CAB, the new (executing) thread waits, intermittently checking the
Process.HasExited
property (of the Process
class) every 1000 milliseconds. When the process has exited, it signals the AutoResetEvent
through a call to the Set()
method of the AutoResetEvent
object. This signals the calling thread, which had been in an efficient wait state (through a call to the AutoResetEvents
WaitOne
method), to continue executing.
- The process from step II. is repeated for any subsequent CAB file that will be installed.
Implementation
InstallParams class
The InstallParams
class is fairly easy to understand. As mentioned before, it simply contains an AutoResetEvent
object and a string containing the CAB file installation initialization file (.ini). An InstallParams
object is passed as the second parameter to ThreadPool.QueueUserWorkItem
.
This class is implemented as a nested public class, as part of the CABFileInstaller
class.
CABFileInstaller class
This class is derived from the Installer
class (declared as partial
, and therefore a derived extension of the standard library base Installer
class). The only overridden method of the Installer
class is the Commit method, which calls the base Commit
method before implementing any custom functionality.
The class has a private string member (string appPath
) which stores the path to the ActiveSync executable.
This derived class also has an additional private method called RunInstallerTask
which starts ActiveSync and installs the CAB file based on the information in the .ini file (described below). This method depends on information passed to the setup process as part of its context in order to determine the path to the target directory.
The line strIniFilePath = "\"" + Context.Parameters["targetdir"]...
in the RunInstallerTask
method determines the target installation directory from the current context. In order to implement this, the CustomActionData
property of the Custom Action must be set to /targetdir="[TARGETDIR]\", as described in the walkthrough below.
The initialization file (.ini)
For each CAB file that is to be installed, an .ini file in the given format must be created and installed to the MSI target directory. The MSI does the transfer to the installation folder automatically, but you must create the ini file for each CAB and add it to the files included in the ApplicationFolder of the setup project.
The details of the contents of the .ini file and its format are available on MSDN at this URL: http://msdn.microsoft.com/en-us/library/bb158614.aspx.
For a simple example of an .ini file, the one used to install the .NET Compact Framework CAB file (NETCFv35.ini, in the included example) looks like this:
[CEAppManager]
Version = 1.0
Component = NETCFv35.ppc.armv4
[NETCFv35.ppc.armv4]
Description = RMS Stock Count Client
CabFiles = NETCFv35.ppc.armv4.cab
The first two lines never change, i.e., [CEAppManager]
, and the version must always be 1.0.
The Component
value must match the beginning of the next section, and the last (CabFiles
) value should match the exact name of the CAB file(s) being installed. One may include more than one CAB file on the last line separated by commas to support different architectures. In this case, the .NET Compact Framework CAB file included is for ARM type processors.
Project setup - Walkthrough
I used Visual Studio 2008, but this should work for VS2005 as well.
To create the custom action
- On the File menu, click New Project.
- In the New Project dialog box, select Visual C# Projects in the Project Types pane, and then click Class Library in the Templates pane. In the Name box, type CABInstallCustomAction.
The project is added to Solution Explorer.
- On the Project menu, click Add Class, and then in the Add New Item dialog box, select Installer Class. Type in the name as CABFileInstaller.cs. Click Add.
- Switch to code view by clicking Click here to switch to code view on the design surface (or by right-clicking the design surface and clicking View Code).
- In the Code Editor, add the following code to CABFileInstaller.cs, after the class declaration (also see the same file accompanying this article in the example solution):
To declare the InstallParams
class:
public class InstallParams
{
private AutoResetEvent reset;
private string strIniFile;
public InstallParams(AutoResetEvent ev, string ini_file)
{
if (ev == null)
throw new Exception("Null reset event");
reset = ev;
strIniFile = ini_file;
}
public AutoResetEvent Reset
{
get
{
return reset;
}
}
public string IniFile
{
get
{
return strIniFile;
}
}
}
To declare the private string app path:
private string appPath = null;
To create the RunInstallTask
method:
private void RunInstallerTask(Object iparam)
{
int time = 0;
InstallParams iparams = iparam as InstallParams;
AutoResetEvent are = (AutoResetEvent)iparams.Reset;
string strIniFilePath = "\"" + Context.Parameters["targetdir"] +
iparams.IniFile + "\"";
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = appPath;
process.StartInfo.Arguments = strIniFilePath;
process.Start();
while (!process.HasExited)
{
time = 1000;
Thread.Sleep(time);
}
are.Set();
}
To create the overridden Commit
method:
public override void Commit(System.Collections.IDictionary savedState)
{
base.Commit(savedState);
AutoResetEvent ev;
InstallParams ip;
Microsoft.Win32.RegistryKey key = null;
key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
"Software\\microsoft\\windows\\currentversion\\app paths\\ceappmgr.exe");
if (key != null)
{
appPath = key.GetValue(null).ToString();
if (appPath != null)
{
ev = new AutoResetEvent(false);
ip = new InstallParams(ev, "NETCFv35.ini");
ThreadPool.QueueUserWorkItem(new WaitCallback(RunInstallerTask), ip);
ev.WaitOne();
ev = new AutoResetEvent(false);
ip = new InstallParams(ev, "sql.ini");
ThreadPool.QueueUserWorkItem(new WaitCallback(RunInstallerTask), ip);
ev.WaitOne();
}
}
else
{
throw new Exception("ActiveSync Not Installed");
}
}
- In Solution Explorer, right-click Class1.cs and then click Delete (because it is unnecessary).
To add a deployment project
- On the File menu, point to Add, and then click New Project.
- In the Add New Project dialog box's Project Type pane, open the Other Project Types node, and then select Setup and Deployment Projects. In the Templates pane, click Setup Project. In the Name box, type MultipleCABFilesSetup.
The project is added to Solution Explorer and the File System Editor is displayed. Copy the CAB files and the corresponding .ini files to the project folder.
- In the File System Editor, select Application Folder in the left pane. On the Action menu, point to Add, and then click File. Add all the CAB files and corresponding .ini files to the application folder in this way.
- In the File System Editor, select Application Folder in the left pane. On the Action menu, point to Add and then click Project Output.
- In the Add Project Output Group dialog box, CABInstallCustomAction will be displayed in the Project list. Select Primary Output.
Primary Output from CABINstallCustomAction (Active) appears in the Application Folder.
To add the custom action
- Select the MultipleCABFilesSetup project in Solution Explorer. On the View menu, point to Editor, and then click Custom Actions.
The Custom Actions Editor is displayed.
- In the Custom Actions Editor, select the Install node. On the Action menu, click Add Custom Action.
- In the Select Item in Project dialog box, double-click the Application Folder. Select Primary output from CABInstallCustomAction.
Primary output from CABInstallCustomCation appears under the Install node in the Custom Actions Editor.
In the properties window, add the following line to the CustomActionData property: /targetdir="[TARGETDIR]\". This data is passed to the Custom Action in order to locate the ini and CAB files in the target installation directory. See the custom action code.
- In the Properties window, make sure that the InstallerClass property is set to True (this is the default).
- In the Custom Actions Editor, select the Commit node. On the Action menu, click Add Custom Action.
- In the Select Item in Project dialog box, double-click the Application Folder. Select Primary output from CABInstallCustomAction.
Primary output from CABInstallCustomCation appears under the Commit node in the Custom Actions Editor.
In the properties window, add the following line to the CustomActionData property: /targetdir="[TARGETDIR]\". This data is passed to the Custom Action in order to locate the ini and CAB files in the target installation directory. See the custom action code.
- In the Properties window, make sure that the InstallerClass property is set to True (this is the default).
- On the Build menu, click Build Custom Action Installer.
To install on your development computer
- Make sure the target device is connected and a partnership has been setup using ActiveSync. Then, select the MultipleCABFilesSetup project in Solution Explorer. On the Project menu, click Install.
This will run the installer and install MultipleCABFilesSetup on your development computer. At the end of installation, you should see ActiveSync start and prompt you to install the .NET Compact Framework followed by the SQL Client .
You must have install permissions on the computer in order to run the installer.
To deploy to another computer
- In Windows Explorer, navigate to your project directory and find the installer. The default path will be \Documents and Settings\<yourloginname>\My Documents\Visual Studio\Projects\MultipleCABFilesSetup\ MultipleCABFilesSetup \<project configuration>\ MultipleCABFilesSetup.msi. The default project configuration is Debug.
- Copy Custom Action Installer.msi, Setup.exe, and all other files and subdirectories in the directory to another computer.
To install on a computer that is not on a network, copy the files to traditional media such as CD-ROM.
- On the target computer, make sure ActiveSync is installed, the target device is connected, and a partnership has been setup using ActiveSync, and then double-click Setup.exe to run the installer.
At the end of installation, ActiveSync should start and should prompt you to install the two CAB files included in the setup project.
You must have install permissions on the computer in order to run the installer.
To uninstall the application
- In Control Panel, double-click Add or Remove Programs.
- In the Add or Remove Programs dialog box, select MultipleCABFileSetup and click Remove.
To uninstall from your development computer, with the MultipleCABFileSetup project open and selected in Solution Explorer, from the Project menu, click Uninstall.