#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 );
}