#region Auto-Update Stuff private ApplicationUpdateManager _updater = null; private Thread _updaterThread = null; private const int UPDATERTHREAD_JOIN_TIMEOUT = 3 * 1000; private delegate void MarshalEventDelegate( object sender, UpdaterActionEventArgs e ); private void InitializeAutoUpdate() { // 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"]; this.Text = this.Text + String.Format(" v. {0}", version); } private void CurrentDomain_ProcessExit(object sender, EventArgs e) { StopUpdater(); } private void StopUpdater() { // tell updater to stop _updater.StopUpdater(); if( null != _updaterThread ) { // join the updater thread with a suitable timeout bool isThreadJoined = _updaterThread.Join( UPDATERTHREAD_JOIN_TIMEOUT ); // check if we joined, if we didn't interrupt the thread if( !isThreadJoined ) { _updaterThread.Interrupt(); } _updaterThread = null; } } /// /// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same /// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread /// /// marshalled reference to the original event's sender argument /// marshalled reference to the original event's args private void OnUpdaterDownloadStartedHandler( object sender, UpdaterActionEventArgs e ) { Debug.WriteLine("Thread: " + Thread.CurrentThread.GetHashCode().ToString()); Debug.WriteLine(String.Format( " DownloadStarted for application '{0}'", e.ApplicationName )); } /// /// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is /// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe. /// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke /// mechanism. /// /// 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 Debug.WriteLine( String.Format( "[OnUpdaterDownloadStarted]Thread: {0}", Thread.CurrentThread.GetHashCode().ToString()) ); this.Invoke( new MarshalEventDelegate( this.OnUpdaterDownloadStartedHandler ), new object[] { sender, e } ); } /// /// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same /// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread /// /// marshalled reference to the original event's sender argument /// marshalled reference to the original event's args private void OnUpdaterFilesValidatedHandler( object sender, UpdaterActionEventArgs e ) { Debug.WriteLine(String.Format("FilesValidated successfully for application '{0}' ", e.ApplicationName)); // ask user to use new app DialogResult dialog = MessageBox.Show( "Would you like to stop this application and open the new version?", "Open New Version?", MessageBoxButtons.YesNo ); if( DialogResult.Yes == dialog ) { StartNewVersion( e.ServerInformation ); } } /// /// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is /// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe. /// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke /// mechanism. /// /// 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 ) { // using the asynchronous "BeginInvoke". // we don't need/want to block here this.BeginInvoke( new MarshalEventDelegate( this.OnUpdaterFilesValidatedHandler ), new object[] { sender, e } ); } /// /// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same /// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread /// /// marshalled reference to the original event's sender argument /// marshalled reference to the original event's args private void OnUpdaterUpdateAvailableHandler( object sender, UpdaterActionEventArgs e ) { Debug.WriteLine("Thread: " + Thread.CurrentThread.GetHashCode().ToString()); string message = String.Format( "Update available: The new version on the server is {0} and current version is {1} would you like to upgrade?", e.ServerInformation.AvailableVersion, System.Configuration.ConfigurationSettings.AppSettings["version"] ) ; // for update available we actually WANT to block the downloading thread so we can refuse an update // and reset until next polling cycle; // NOTE that we don't block the thread _in the UI_, we have it blocked at the marshalling dispatcher "OnUpdaterUpdateAvailable" DialogResult dialog = MessageBox.Show( message, "Update Available", MessageBoxButtons.YesNo ); if( DialogResult.No == dialog ) { // if no, stop the updater for this app _updater.StopUpdater( e.ApplicationName ); Debug.WriteLine("Update Cancelled."); } else { Debug.WriteLine("Update in progress."); } } /// /// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is /// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe. /// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke /// mechanism. /// /// 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 ) { // 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 this.Invoke( new MarshalEventDelegate( this.OnUpdaterUpdateAvailableHandler ), new object[] { sender, e } ); } /// /// This handler gets fired by the Windows UI thread that is the main STA thread for THIS FORM. It takes the same /// arguments as the event handler below it--sender, e--and acts on them using the main thread NOT the eventing thread /// /// marshalled reference to the original event's sender argument /// marshalled reference to the original event's args private void OnUpdaterDownloadCompletedHandler( object sender, UpdaterActionEventArgs e ) { Debug.WriteLine("Download Completed."); } /// /// Event handler for Updater event. This event is fired by the originating thread from "inside" the Updater. While it is /// possible for this same thread to act on our UI, it is NOT a good thing to do--UI is not threadsafe. /// Therefore here we marshal from the Eventing thread (belongs to Updater) to our window thread using the synchronous Invoke /// mechanism. /// /// 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 ) { // 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 this.Invoke( new MarshalEventDelegate( this.OnUpdaterDownloadCompletedHandler ), new object[] { sender, e } ); } private void StartNewVersion( ServerApplicationInfo server ) { XmlDocument doc = new XmlDocument(); // load config file to get base dir doc.Load( AppDomain.CurrentDomain.SetupInformation.ConfigurationFile ); // get the base dir string baseDir = doc.SelectSingleNode("configuration/appUpdater/UpdaterConfiguration/application/client/baseDir").InnerText; string newDir = Path.Combine( baseDir, "AppStart.exe" ); ProcessStartInfo process = new ProcessStartInfo( newDir ); process.WorkingDirectory = Path.Combine( newDir , server.AvailableVersion ); // launch new version (actually, launch AppStart.exe which HAS pointer to new version ) Process.Start( process ); // tell updater to stop CurrentDomain_ProcessExit( null, null ); // leave this app Environment.Exit( 0 ); }