How do I write a wrapper DLL so that I can use it over and over without duplicating all of the self-updating code and use it in both VB.NET and C# Applications? In this article, I will show your the C# Code for a DLL that wraps the Upadater Application Block. To set up to run this application, you can refer to the article on Directories and Files for the Updater Application Block.
In the code for this article, I will provide the code for a test application and a reusable DLL. Both are written in C#, but could easily converted to VB.NET. Figure 1 shows the Test Application, version 1. It is logging the activities of the Updater Application Block and the communications between it and the client application through the Wrapper DLL. You can copy the code for the form and dll into a blank solution and add two projects, copying the code to each of the respective projects. You can put al of the code into the same project, but I kept the DLL code separate so that I can use it in multiple projects as a comipled assembly.
Figure 1 - Test Applicatio Version 1.0.0.0

When the test program loads, it retrieves the version from its app.exe.config file. To start the Updater Application Block, I clicked the Start Updater Button. The remainder of the messages in the log text box result from starting the updater. The Application Block raises events back to the Wrapper, which in turn, raises the same events back to the Test Client Form.
Once the files have been downloaded, I clicked the Start New Version button and the Wrapper DLL initiates the starting of the newly downloaded version 2.0.0.0, and the shutdown of the old version. Figure 2 shows the new version running.
Figure 2 - Test Application Version 2.0.0.0

Figure 3 shows the code for the Test Form. This form calls the UAB (Updater Application Block) Wrapper DLL to serve as an interface with the Application Block. The DLL exposes several methods and events. The code for the test form uses all of the exposed events and methods. There is an anamoly that I found in the UAB. If you bypass validation, for testing reasons, the Download Completed event does not have the server information property set in the EventArgs parameter. It is set in Download Started and FilesValidated, so you must save it from one of those event in order to start the new version. It is very dangerous to turn off Validation as that leaves a huge security hole.
Figure 3 - Code for Test Form
| using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using UABWrapper; using System.Diagnostics; using System.Configuration; namespace TestUABSelfUpdater { /// <summary> /// Summary description for Form1. /// public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Button button1; private System.Windows.Forms.Button btnStartUpdater; private System.Windows.Forms.TextBox txtLog; private System.Windows.Forms.Button btnStopUpdater; private System.Windows.Forms.Button btnStartNewVerson; /// <summary> /// Required designer variable. /// private System.ComponentModel.Container components = null; /// <summary> /// Constructor /// public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } /// <summary> /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button(); this.btnStartUpdater = new System.Windows.Forms.Button(); this.txtLog = new System.Windows.Forms.TextBox(); this.btnStopUpdater = new System.Windows.Forms.Button(); this.btnStartNewVerson = new System.Windows.Forms.Button(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(144, 16); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(112, 23); this.button1.TabIndex = 1; this.button1.Text = "Show Version"; this.button1.Click += new System.EventHandler(this.button1_Click); // // btnStartUpdater // this.btnStartUpdater.Location = new System.Drawing.Point(16, 16); this.btnStartUpdater.Name = "btnStartUpdater"; this.btnStartUpdater.Size = new System.Drawing.Size(96, 23); this.btnStartUpdater.TabIndex = 0; this.btnStartUpdater.Text = "Start Updater"; this.btnStartUpdater.Click += new System.EventHandler(this.btnStartUpdater_Click); // // txtLog // this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.txtLog.Location = new System.Drawing.Point(16, 88); this.txtLog.Multiline = true; this.txtLog.Name = "txtLog"; this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.txtLog.Size = new System.Drawing.Size(404, 200); this.txtLog.TabIndex = 2; this.txtLog.Text = ""; // // btnStopUpdater // this.btnStopUpdater.Enabled = false; this.btnStopUpdater.Location = new System.Drawing.Point(16, 48); this.btnStopUpdater.Name = "btnStopUpdater"; this.btnStopUpdater.Size = new System.Drawing.Size(96, 24); this.btnStopUpdater.TabIndex = 3; this.btnStopUpdater.Text = "Stop Updater"; this.btnStopUpdater.Click += new System.EventHandler(this.btnStopUpdater_Click); // // btnStartNewVerson // this.btnStartNewVerson.Enabled = false; this.btnStartNewVerson.Location = new System.Drawing.Point(144, 48); this.btnStartNewVerson.Name = "btnStartNewVerson"; this.btnStartNewVerson.Size = new System.Drawing.Size(112, 24); this.btnStartNewVerson.TabIndex = 4; this.btnStartNewVerson.Text = "Start New Version"; this.btnStartNewVerson.Click += new System.EventHandler(this.btnStartNewVerson_Click); // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(440, 294); this.Controls.Add(this.btnStartNewVerson); this.Controls.Add(this.btnStopUpdater); this.Controls.Add(this.txtLog); this.Controls.Add(this.btnStartUpdater); this.Controls.Add(this.button1); this.Name = "Form1"; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; this.Text = "Test UAB Wrapper"; this.Closing += new System.ComponentModel.CancelEventHandler(this.Form1_Closing); this.Load += new System.EventHandler(this.Form1_Load); this.ResumeLayout(false); } #endregion /// <summary> /// The main entry point for the application. /// [STAThread] static void Main() { Application.Run(new Form1()); } private UABWrapper uab; /// <summary> /// Hook up the event handlers for the UABWrapper so we can /// handle the notification of various events within the /// Application Updater. /// /// /// private void Form1_Load(object sender, System.EventArgs e) { this.txtLog.AppendText("TestUAB Started at: " + DateTime.Now.ToString()+Environment.NewLine); string v = System.Configuration.ConfigurationSettings.AppSettings["Version"]; this.txtLog.AppendText("Version: " + v + Environment.NewLine); this.Text="Test UAB Updater Version: " + v; } /// <summary> /// Fired when new version is available. /// private void uab_Available(object sender, EventArgs e) { // fires when new version is available this.txtLog.AppendText("New Version available at: " + DateTime.Now.ToString()+Environment.NewLine); } /// <summary> /// Fired when download is complete. /// /// /// private void uab_DownloadCompleted(object sender, EventArgs e) { this.txtLog.AppendText("Download Completed at: " + DateTime.Now.ToString()+Environment.NewLine); } /// <summary> /// Fired when download of new version starts /// /// /// private void uab_DownloadStarted(object sender, EventArgs e) { this.txtLog.AppendText("Download Started at: " + DateTime.Now.ToString()+Environment.NewLine); } /// <summary> /// Fires when downloaded files have been validated. /// /// /// private void uab_FilesValidated(object sender, EventArgs e) { this.txtLog.AppendText("Files Validated at: " + DateTime.Now.ToString()+Environment.NewLine); } private void button1_Click(object sender, System.EventArgs e) { string v = System.Configuration.ConfigurationSettings.AppSettings["Version"]; MessageBox.Show("Version: " + v); } private void btnStartUpdater_Click(object sender, System.EventArgs e) { try { uab = new UABWrapper(3,UABWrapper.UABAction.AutoDownload); uab.Available += new EventHandler(uab_Available); uab.DownloadCompleted += new EventHandler(uab_DownloadCompleted); uab.DownloadStarted += new EventHandler(uab_DownloadStarted); uab.FilesValidated += new EventHandler(uab_FilesValidated); uab.NewVersionStarting+=new EventHandler(uab_NewVersionStarting); uab.NewVersionStartFailed+=new EventHandler(uab_NewVersionStartFailed); uab.CantStartNewVersion+=new EventHandler(uab_CantStartNewVersion); this.txtLog.AppendText("Started Updater at: " + DateTime.Now.ToString()+Environment.NewLine); this.btnStopUpdater.Enabled=true; this.btnStartNewVerson.Enabled=true; } catch(System.Exception ex) { // add your code here MessageBox.Show(ex.ToString()); } } private void btnStopUpdater_Click(object sender, System.EventArgs e) { try { uab.StopUAB(); this.txtLog.AppendText("Updater told to stop at: " + DateTime.Now.ToString()+Environment.NewLine); this.btnStopUpdater.Enabled=false; } catch(System.Exception ex) { // add your code here MessageBox.Show(ex.ToString()); } } private void btnStartNewVerson_Click(object sender, System.EventArgs e) { this.txtLog.AppendText("Asking Updater to start new version at: " + DateTime.Now.ToString()+Environment.NewLine); uab.StartNewVersion(); } private void uab_NewVersionStarting(object sender, EventArgs e) { this.txtLog.AppendText("New Version attempting to start: " + DateTime.Now.ToString()+Environment.NewLine); } private void uab_NewVersionStartFailed(object sender, EventArgs e) { this.txtLog.AppendText("New Version failed to start: " + DateTime.Now.ToString()+Environment.NewLine); this.txtLog.AppendText(sender.ToString()); } private void uab_CantStartNewVersion(object sender, EventArgs e) { this.txtLog.AppendText("Cant start New Version: " + DateTime.Now.ToString()+Environment.NewLine); } private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e) { try { uab.Available -= new EventHandler(uab_Available); uab.DownloadCompleted -= new EventHandler(uab_DownloadCompleted); uab.DownloadStarted -= new EventHandler(uab_DownloadStarted); uab.FilesValidated -= new EventHandler(uab_FilesValidated); uab.NewVersionStarting-=new EventHandler(uab_NewVersionStarting); uab.NewVersionStartFailed-=new EventHandler(uab_NewVersionStartFailed); uab.CantStartNewVersion-=new EventHandler(uab_CantStartNewVersion); } catch(System.Exception ex) { // add your code here } } } } |
| using System; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Threading; using System.IO; using System.Xml; using Microsoft.ApplicationBlocks.ApplicationUpdater; using System.Diagnostics; using System.IO; namespace UABWrapper { /// <summary> /// Summary description for UABWrapper. /// UABWrapper is a wrapper dll for the Updater Applications Block. /// It encapsulates the code requried to call the UAB and at the /// same time provide a standard interface for applications while /// adding a minimum amount of code to an existing application. /// Allows you to convert an existing application to a self-updating /// application. /// /// <summary> /// Potential status values for state of UAB. /// public enum UABStatus { Inactive, Active, DownloadStarted, DownloadCompleted, FilesValidated, UpdateAvailable} /// <summary> /// Determines whether the dll will start download when one is available /// or wait on user to request the down load. /// public enum UABAction { DownloadOnDemand, AutoDownload} public interface IUABWrapper { event EventHandler Available; event EventHandler DownloadStarted; event EventHandler DownloadCompleted; event EventHandler FilesValidated; event EventHandler NewVersionStarting; } public class UABWrapper { #region Class Constructor /// <summary> /// Class Constructor sets up polling interval, and action to take /// when download becomes available. /// /// /// /// public UABWrapper(int pollMins, UABAction action) { pollingInterval = pollMins; Action = action; this.ForkUABThread(); } #endregion #region Class Level Variables private UABAction _Action; private int pollingInterval = 0; public event EventHandler Available; public event EventHandler DownloadStarted; public event EventHandler DownloadCompleted; public event EventHandler FilesValidated; public event EventHandler NewVersionStarting; public event EventHandler CantStartNewVersion; public event EventHandler NewVersionStartFailed; private UABStatus _Status = UABStatus.Inactive; private ApplicationUpdateManager _updater = null; private Thread _updaterThread = null; private const int UPDATERTHREAD_JOIN_TIMEOUT = 3 * 1000; private ServerApplicationInfo server; #endregion #region Public Properties public UABAction Action { get{return _Action;} set{_Action = value;} } public UABStatus Status { get{return _Status;} set{_Status = value;} } #endregion /// <summary> /// This method should only be called when the app has been notified /// that a new app has been downloaded and validated. It takes the /// current app down and starts a new one. /// /// <returns> public void StartNewVersion() { if (Status == UABStatus.DownloadCompleted) { NewVersionStarting(new object(),new System.EventArgs()); System.Threading.Thread.Sleep(1000); this.StartNewDownloadedVersion(server); } else this.CantStartNewVersion(new object(), new EventArgs()); } /// <summary> /// Starts the AppStart exe with new version and takes calling app down. /// // public void Restart() // { // } /// <summary> /// Stops the UAB thread. /// public void StopUAB() { StopUpdater(); } /// <summary> /// This method starts the UAB on a new thread. The UAB will examine the /// app.config file for the executing application (who called this dll) to /// get the server and manifest file. It will use that to ck the server for /// a new version of the calling application. /// private void ForkUABThread() { // hook ProcessExit for a chance to clean up when closed peremptorily AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); // make an Updater for use in-process with us _updater = new ApplicationUpdateManager(); // hook Updater events _updater.DownloadStarted += new UpdaterActionEventHandler( OnUpdaterDownloadStarted ); _updater.FilesValidated += new UpdaterActionEventHandler( OnUpdaterFilesValidated ); _updater.UpdateAvailable += new UpdaterActionEventHandler( OnUpdaterUpdateAvailable ); _updater.DownloadCompleted += new UpdaterActionEventHandler(OnUpdaterDownloadCompleted); // start the updater on a separate thread so that our UI remains responsive _updaterThread = new Thread( new ThreadStart( _updater.StartUpdater ) ); _updaterThread.Start(); // get version from config, set caption correctly string version = System.Configuration.ConfigurationSettings.AppSettings["version"]; } private void CurrentDomain_ProcessExit(object sender, EventArgs e) { StopUpdater(); } /// <summary> /// Event handler for Updater event. /// This event is fired by the originating thread from "inside" the Updater. /// /// event sender in this case ApplicationUpdaterManager /// the UpdaterActionEventArgs packaged by Updater, /// which gives us access to update information private void OnUpdaterDownloadStarted( object sender, UpdaterActionEventArgs e ) { // using the synchronous "Invoke". This marshals from the eventing thread-- // which comes from the Updater and should not // be allowed to enter and "touch" the UI's window thread // so we use Invoke which allows us to block the Updater thread at // will while only allowing window thread to update UI Status = UABStatus.DownloadStarted; DownloadStarted(sender, e); } /// <summary> /// Event handler for Updater event. This event is fired by the originating /// thread from "inside" the Updater. /// /// event sender in this case ApplicationUpdaterManager /// the UpdaterActionEventArgs packaged by Updater, /// which gives us access to update information private void OnUpdaterFilesValidated( object sender, UpdaterActionEventArgs e ) { Status = UABStatus.FilesValidated; server = e.ServerInformation; FilesValidated(sender, e); } /// <summary> /// Event handler for Updater event. Fires when a new version is available /// for download. /// Stop the updater from polling until the app requests a new version. /// Server is not available on download completed, but is at start event. /// Save it here, for we may not validate. /// /// event sender in this case ApplicationUpdaterManager /// the UpdaterActionEventArgs packaged by Updater, /// which gives us access to update information private void OnUpdaterUpdateAvailable( object sender, UpdaterActionEventArgs e ) { Status = UABStatus.UpdateAvailable; server = e.ServerInformation; Available(sender, e); //StopUpdater(); } /// <summary> /// Event handler for Updater event. The download has completed, set the status /// and fire an event in the caller to tell that the requested download is complete. /// /// event sender in this case ApplicationUpdaterManager /// the UpdaterActionEventArgs packaged by Updater, /// which gives us access to update information private void OnUpdaterDownloadCompleted( object sender, UpdaterActionEventArgs e ) { try { Status = UABStatus.DownloadCompleted; if(server==null) { throw(new Exception("e.ServerInformation is null at DownloadComplete")); } DownloadCompleted(sender, e); } catch(System.Exception ex) { // add your code here this.NewVersionStartFailed(ex, new EventArgs()); } } /// <summary> /// /// /// private void StartNewDownloadedVersion( ServerApplicationInfo server ) { try { if (server==null) { throw(new ArgumentNullException()); } XmlDocument doc = new XmlDocument(); // load config file to get base dir doc.Load( AppDomain.CurrentDomain.SetupInformation.Confi |