.NET Compact Framework File Deployment Tool

By Robbe D. Morris

Printer Friendly Version

Robbe Morris
Robbe & Melisa Morris
  Download Source Code
Ah, the joys of deploying files to mobile devices such as the iPAQ.  In my opinion, there are really two separate areas to discuss when it comes to deploying your .NET Compact Framework application.  The first being the installation of the .NET Compact Framework itself along with any additional permanent software such as database clients, 3rd party controls, browsers, etc.  The second deals with the deployment of your application and its support files which is what the deployment tool provided in this article deals with.
I put together the .NET Compact Framework File Deployment Tool to manage a specific work situation.  We have around 100 older iPAQs that don't have built-in wireless cards.  These devices are used for a specific business purpose so the only applications running on them will be ours.  We'll also be swapping out different versions of our software as well as installing and deleting different software applications on a month to month basis.  For reasons I can't discuss here, we don't want any extra software physically on the device besides its dedicated purpose for that month or quarter.
That said, the idea of no-touch deployment scenarios really didn't fit our business need in its entirety.  Neither did trying to utilize commercial software for deployments especially since they don't always handle installations of multiple devices from the same laptop as well as they should.  Some of these tools perform checks on the local laptop to see what has and has not been installed.  Thus, not all of the shortcuts get created properly or some of the files don't get updated.


The .NET Compact Framework File Deployment Tool is geared towards dealing with all of these concerns.  In reality, the tool itself is not all that special or original.  OpenNETCF.org has a communication library designed to enable developers to write applications such as this to work with files, registry, and other settings on mobile devices.  The deployment tool is merely a user friendly wrapper to this library that does the following:
 
1.Add a registry key entry that tells ActiveSync to always connect the mobile device as Guest Only and prevents that annoying dialog prompt from being shown.
2.Demonstrate how to wire up and react to events raised by the communication library.  You'll be able to perform certain tasks when a device is connected, in the act of connecting, or disconnected.
3.By reviewing the Program Files folder under the root of the deployment application on the PC, it populates hierarchy trees of all the files and folders that exist on the PC and those match files that may or may not exist on the device.  This way, you only see the relevant files for a specific application or applications.  To hide old applications, just remove/move the folder on the local PC out of the Program Files folder for the deployment tool and click Refresh.
4.Reviews the Start Menu and StartUp folders under the root of the deployment application on the local PC as well as the \Windows\Start Menu and \Windows\StartUp folders on the mobile device.  You can then easily deploy or delete shortcuts from the mobile device as needed.
5.Provides a variety of ways to copy or delete selected files, selected folders, and shortcuts on the mobile device.  In most cases, the user could set up the proper selections and then simply hit deploy after connecting each device.  This is a real timesaver.
6.Demonstrates how to implement right click context menus for the TreeView.
7.Copies killall.exe to Program Files\killall.exe prior to copying files.  When the mobile device Program Files tree is drawn in the tool, it creates a list of .exe filenames to be passed as a command line parameter to killall.exe.  The killall.exe application is simply this example from MSDN mocked up to accept command line parameters and shut itself down when finished Creating a .NET Compact Framework Process Manager .  It makes sure that none of the .exe files you might intend to copy are running at the time of deployment.  It also makes sure that their are no API locks on the device that could have been enabled using this methodology from one of the running applications:  .NET Compact Framework Lock Down Device .
 
With these things in mind, I've found the process of deploying application updates during the testing phase to be much less cumbersome than performing the same task manually.  All I have to do is manually create my shortcuts once and place them in the appropriate deployment folder.  From that point on, our testing group just checks out the application folder, deletes the existing folder on the mobile device, and clicks deploy.
You can download the source code via the link above or quickly peruse the relevant source code below.
 
C# Sample Code
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Diagnostics; 
using System.Threading;
using OpenNETCF.Desktop.Communication;

namespace Deployment
{
 
  public class Form1 : System.Windows.Forms.Form
  {

    private const string ProgramFiles = "Program Files";
    private const string StartMenu = "Start Menu";
    private const string StartUp = "StartUp";
    private const string ConnectedMsg = "Device connected";
    private const string DisconnectedMsg = "No device connected";
    private const string ConnectingMsg = "Connecting...";

    private string MobileExeFileList = "";
    private bool RefreshListsNeeded = false;
    private string AppPath="";
    private ContextMenu tvMobileProgramFilesMenu = new ContextMenu();

    private RAPI rapi = new RAPI();

  
    #region Constructor

    [STAThread]
    static void Main() 
    {
      Application.Run(new Form1());
    }

    public Form1()
    {
      InitializeComponent();
      rapi.ActiveSync.Active += new ActiveHandler(ActiveSync_Active);
      rapi.ActiveSync.Disconnect += new DisconnectHandler(ActiveSync_Disconnect);
      rapi.ActiveSync.Listen += new ListenHandler(ActiveSync_Listen);
      rapi.ActiveSync.Answer += new AnswerHandler(ActiveSync_Answer);
    }
 
    #region Form Load
    private void Form1_Load(object sender, System.EventArgs e)
    {

      Hourglass(true);

      try
      {

        this.SetAppPath();

        this.CreateAppFolders();

       // set a registry key so that all future ActiveSync
       // connections launch as Guest Only without that
       // annoying prompt.  The .ConnectAsGuestOnly property
       // will create the following key if it doesn't already
       // exist.  Simply use regedit to delete this key or
       // set the DWORD value = 0

      //  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows CE Services
      //  GuestOnly DWORD = 1

          rapi.ActiveSync.ConnectAsGuestOnly = true;  

          this.lblStatus.Text = DisconnectedMsg; 

          this.Connect();
 
          if (rapi.Connected)
          { 
            this.lblStatus.Text = ConnectedMsg; 
          }
 
          tvMobileProgramFilesMenu.MenuItems.Add("Delete",
                                        new EventHandler(tvMobileProgramFilesRightClickDelete));  
         
          Application.DoEvents(); 
           
          this.LoadLocalFileLists();
            
          this.LoadLocalProgramFiles();

          RefreshListsNeeded = true;

        }
        catch (Exception err) {  Hourglass(false); ShowError(err.Message); }
        finally { Hourglass(false); }
      }
      #endregion

        #region Form Closed
        private void Form1_Closed(object sender, System.EventArgs e)
        {
            try
            {
              rapi.Disconnect();
              rapi.Dispose(); 
            }
            catch { }
        }
        #endregion

        #region Exit
        private void cmdExit_Click(object sender, System.EventArgs e)
        {
          this.Close();
          Application.Exit(); 
        }
        #endregion

        #region Create App Folders
        private void CreateAppFolders()
        {

           if (!Directory.Exists(Path.Combine(AppPath,ProgramFiles)))
           {
               Directory.CreateDirectory(Path.Combine(AppPath,ProgramFiles));
           }
           if (!Directory.Exists(Path.Combine(AppPath,StartUp)))
           {
               Directory.CreateDirectory(Path.Combine(AppPath,StartUp));
           }
           if (!Directory.Exists(Path.Combine(AppPath,StartMenu)))
           {
               Directory.CreateDirectory(Path.Combine(AppPath,StartMenu));
           }
 
        }
        #endregion

        #region Set App Path
        private void SetAppPath()
        {
           try
           {
             AppPath = Path.GetFullPath(".");  
             AppPath = AppPath.Replace(@"\bin\Debug",""); 
             AppPath = AppPath.Replace(@"\bin\Release",""); 
             if (AppPath.EndsWith(@"\") == false) { AppPath += @"\"; }
           }
           catch (Exception) { throw; }
        }
        #endregion

        #region Show Error
        private void ShowError(string msg)
        {
           MessageBox.Show(msg);
        }
        #endregion

        #region Timer
        private void timer1_Tick(object sender, System.EventArgs e)
        {

           if (!RefreshListsNeeded) { return; }
           RefreshListsNeeded = false;
           this.LoadMobileProgramFiles();
           this.LoadMobileStartMenuList();
           this.LoadMobileStartUpList(); 
           return;
        }
        #endregion

        #region Load Local File Lists
        private void LoadLocalFileLists()
        {

           DirectoryInfo d;
           FileInfo[] fil;

           Hourglass(true);

           try
           {
 
             d = new DirectoryInfo(Path.Combine(AppPath,StartMenu));
			
             this.lstStartMenuShortCuts.Items.Clear();
             this.lstStartUpShortCuts.Items.Clear();
 
             fil = d.GetFiles();
			   
             foreach(FileInfo f in fil)
             { 
               this.lstStartMenuShortCuts.Items.Add(new MyApp.CustomItem(0,f.Name,f.FullName,false));
             }

             d = null;

             d = new DirectoryInfo(Path.Combine(AppPath,StartUp));
				
             fil = d.GetFiles();
			   
             foreach(FileInfo f in fil)
             { 
               this.lstStartUpShortCuts.Items.Add(new MyApp.CustomItem(0,f.Name,f.FullName,false));
             }
				
             d = null;

           }
           catch (Exception e) { ShowError(e.Message); }
           finally
           {
                this.Invalidate();
                this.Disconnect();
                Hourglass(false);
           } 

        }
        #endregion

    	#region Load Local Program Files
        private void LoadLocalProgramFiles()
        {

          TreeNode node;
          Hourglass(true);
          
          try
          {
				
                tvProgramFiles.Invalidate(); 
				
                DirectoryInfo d = new DirectoryInfo(Path.Combine(AppPath,ProgramFiles));
				
                this.tvProgramFiles.Nodes.Clear();

                this.tvProgramFiles.BeginUpdate(); 
				
                node = new TreeNode(ProgramFiles,0,0);
				
                node.Tag = new MyApp.CustomItem(0,d.Name,ProgramFiles,true);  

                this.tvProgramFiles.Nodes.Add(node);
				
                LoadFileList(d,node,false);
				 
                CollapseProgramFolders(node);

                tvProgramFiles.Refresh(); 
 
                node.Expand();

                node.EnsureVisible(); 

           }
           catch (Exception) { throw; }
           finally 
           {
                this.tvProgramFiles.EndUpdate(); 
                Hourglass(false);
           }

        }
        #endregion

        #region Load Mobile Start Menu List
        private void LoadMobileStartMenuList()
        {

            OpenNETCF.Desktop.Communication.FileList fl;
            OpenNETCF.Desktop.Communication.FileInformation fi;
 
            Hourglass(true);

            try
            {
  
               this.lstMobileStartMenuShortCuts.Items.Clear();

                if (!Connect()) { return; } 

                fl = rapi.EnumFiles(@"\Windows\" + StartMenu + @"\*.lnk");

                if (fl != null)
                {
                 for(int i=0;i 0)
              {
                 this.CheckAllChildNodes(node, nodeChecked);
              }
              if (!node.Checked)
              {
                  node.Collapse();
              }
              else
              {
                  node.ExpandAll();
              }
           }
        }
        #endregion

        #region tvMobileProgramFiles Right Click Delete
        private void tvMobileProgramFilesRightClickDelete(object sender, System.EventArgs e)
        {
          
           Hourglass(true);
 
          try
          {
      
             this.Connect();

             MyApp.CustomItem item = (MyApp.CustomItem)tvMobileProgramFiles.SelectedNode.Tag;

             DeleteDeviceFile(ConvertPath(item.Tag));

             this.LoadMobileProgramFiles(); 
         
          }
          catch (Exception err) { ShowError(err.Message); }
          finally { this.Disconnect(); Hourglass(false); }

        }
        #endregion

        #region tvMobileProgramFiles MouseUp
        private void tvMobileProgramFiles_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {

          if ( e.Button != MouseButtons.Right ) return;
          
          Point pt = new Point( e.X, e.Y );
          
          tvMobileProgramFiles.PointToClient(pt);
          
          TreeNode Node = tvMobileProgramFiles.GetNodeAt(pt);

          if ( Node == null ) return;
          
          if (!Node.Bounds.Contains(pt)) { return; }
          
          tvMobileProgramFiles.SelectedNode = Node;
          
          tvMobileProgramFilesMenu.Show(tvMobileProgramFiles,pt);

        }
        #endregion

        #region tvMobileProgramFiles KeyUp
        private void tvMobileProgramFiles_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
        
          if (e.KeyCode != Keys.Apps) { return; }
 
          Point pt = new Point(tvMobileProgramFiles.SelectedNode.Bounds.Left,
                               tvMobileProgramFiles.SelectedNode.Bounds.Bottom );

          tvMobileProgramFilesMenu.Show(tvMobileProgramFiles,pt);
 
        }
        #endregion

  }
}

 

Robbe has been a Microsoft MVP in C# since 2004.  He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.