using System;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Timers;
using System.Xml;
using System.Xml.Linq;
namespace TestIntelReporter {
///
/// Queries the version service to make sure we are running the most
/// recent version.
///
///
/// The URL must return an XML document with the following format:
///
///
///
///
///
///
///
[DefaultEvent("UpdateAvailable"), DefaultProperty("CheckUri")]
public class UpdateCheck : Component {
private const double intervalSecond = 1000.0;
private const double intervalMinute = intervalSecond * 60;
private const double intervalHour = intervalMinute * 60;
private const double intervalDay = intervalHour * 24;
// Default value for CheckInterval: One day
private const double defaultUpdateInterval = intervalDay;
// Timer object to periodically ping the server
private readonly System.Timers.Timer timer;
///
/// Initializes a new instance of the
/// class.
///
public UpdateCheck()
: this(null) {
}
///
/// Initializes a new instance of the
/// class with the specified container.
///
public UpdateCheck(IContainer container) {
this.timer = new System.Timers.Timer(defaultUpdateInterval);
this.timer.Elapsed += this.timer_Elapsed;
this.timer.AutoReset = true;
// Add to the parent container
if (container != null) {
container.Add(this);
}
}
///
/// Raised after pinging the server an a new version is available.
///
public event EventHandler UpdateAvailable;
///
/// Gets the name of the currently executing assembly.
///
///
/// uses as the
/// assembly to perform a version check on.
///
public string AssemblyName {
get {
var assembly = Assembly.GetEntryAssembly();
return (assembly != null) ? assembly.GetName().Name : String.Empty;
}
}
///
/// Gets the file version of the currently executing
/// assembly.
///
///
/// uses as
/// the "current version" when looking for updates.
///
public Version AssemblyVersion {
get {
var assembly = Assembly.GetEntryAssembly();
if (assembly != null) {
var attribute = assembly
.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true)
.Cast()
.SingleOrDefault();
return (attribute != null) ? Version.Parse(attribute.Version) : null;
} else {
return null;
}
}
}
///
/// The URI to use when downloading the list of assembly versions.
///
[DefaultValue(null)]
public string CheckUri { get; set; }
///
/// The URI to use when checking for new versions.
///
[DefaultValue(defaultUpdateInterval)]
public double CheckInterval {
get { return this.timer.Interval; }
set { this.timer.Interval = value; }
}
///
/// Gets or sets the instance of
/// to use when raising events.
///
[DefaultValue(null)]
public ISynchronizeInvoke SynchronizationObject { get; set; }
///
/// Starts checking for updates. Begins a background check
/// immediately.
///
public void Start() {
if (!this.timer.Enabled) {
ThreadPool.QueueUserWorkItem((state) => this.timer_Elapsed(null, null));
this.timer.Start();
}
}
///
/// Terminates checking for updates.
///
public void Stop() {
this.timer.Stop();
}
///
protected override void Dispose(bool disposing) {
if (disposing) {
this.timer.Dispose();
}
base.Dispose(disposing);
}
///
/// Called periodically by to download
/// the version list and check for updates.
///
private void timer_Elapsed(object sender, ElapsedEventArgs e) {
var requestUri = this.CheckUri;
if (String.IsNullOrEmpty(requestUri)) {
return;
}
// Grab the assembly information
var assemblyName = this.AssemblyName;
var assemblyVersion = this.AssemblyVersion;
if (assemblyName == null || assemblyVersion == null) {
return;
}
try {
// Figure out what the server has available
var doc = XDocument.Load(requestUri);
var newAssembly = doc.Root
.Elements("assembly")
.Select(x => new {
Name = x.Attribute("name"),
Version = x.Attribute("version"),
UpdateUri = x.Attribute("update-uri")
})
.Where(x => (x.Name != null)
&& (x.Name.Value == assemblyName)
&& (x.Version != null))
.Select(x => new {
Version = new Version(x.Version.Value),
UpdateUri = (x.UpdateUri != null) ? x.UpdateUri.Value : null
})
.OrderBy(x => x.Version)
.Last();
// Check if we have an update
if (newAssembly.Version > assemblyVersion) {
var handler = this.UpdateAvailable;
if (handler != null) {
var sync = this.SynchronizationObject;
var args = new UpdateEventArgs(
assemblyVersion,
newAssembly.Version,
newAssembly.UpdateUri);
if ((sync != null) && sync.InvokeRequired) {
sync.BeginInvoke(new Action(() => handler(this, args)), null);
} else {
ThreadPool.QueueUserWorkItem((state) => handler(this, args));
}
}
}
} catch (WebException) {
// Error downloading the document
} catch (FormatException) {
// The user's URI is invalid
} catch (XmlException) {
// Error parsing the XML
} catch (InvalidOperationException) {
// Our assembly isn't listed
}
}
}
}