www.pudn.com > dotnetupdater.rar > AppUpdater.cs
using System;
using System.Net;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
namespace Microsoft.Samples.AppUpdater
{
public enum ChangeDetectionModes:int {DirectFileCheck=1, ServerManifestCheck=2};
//**************************************************************
// AppUpdater Class
// - This is the main AppUpdater object
//**************************************************************
public class AppUpdater: Component, ISupportInitialize
{
public const int RestartAppReturnValue = 2;
private System.ComponentModel.Container components = null;
//Used to prevent event recursion in OnAssemblyResolve
private bool LoadingAssembly;
private Control EventControl;
internal AppManifest Manifest;
//Handles downloading & applying the update
private AppDownloader _Downloader;
[TypeConverter(typeof(ExpandableObjectConverter))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Description("The object that downloads and installs new updates."),
Category("AppUpdate Configuration")]
public AppDownloader Downloader
{
get {return _Downloader;}
set {_Downloader=value;}
}
//Triggers update checks
private ServerPoller _Poller;
[TypeConverter(typeof(ExpandableObjectConverter))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Description("The object that periodically polls for new updates."),
Category("AppUpdate Configuration")]
public ServerPoller Poller
{
get {return _Poller;}
set {_Poller=value;}
}
//Instructs the updater where to check & subsequently download updates
private string _UpdateUrl = "http://localhost/";
[DefaultValue(@"http://localhost/")]
[Description("The Url to download updates from."),
Category("AppUpdate Configuration")]
public string UpdateUrl
{
get {return _UpdateUrl;}
set {_UpdateUrl = value;}
}
//Indicates the mechanism to check for updates
private ChangeDetectionModes _ChangeDetectionMode = ChangeDetectionModes.DirectFileCheck;
[DefaultValue(ChangeDetectionModes.DirectFileCheck)]
[Description("The way to detect new updates."),
Category("AppUpdate Configuration")]
public ChangeDetectionModes ChangeDetectionMode
{
get {return _ChangeDetectionMode;}
set {_ChangeDetectionMode = value;}
}
//Indicates whether the udpater should put up it's default UI
private bool _ShowDefaultUI = false;
[DefaultValue(false)]
[Description("Determines whether the default UI is shown or supressed."),
Category("AppUpdate Configuration")]
public bool ShowDefaultUI
{
get {return _ShowDefaultUI;}
set {_ShowDefaultUI = value;}
}
//Whether the updater should try to automatically download missing files
private bool _AutoFileLoad = false;
[DefaultValue(false)]
[Description("Enables auto-download of missing assemblies."),
Category("AppUpdate Configuration")]
public bool AutoFileLoad
{
get {return _AutoFileLoad;}
set {_AutoFileLoad = value;}
}
//**************************************************************
// OnCheckForUpdate Event
// This event is called everytime the appupdater attempts to check for
// an update. You can hook this event and perform your own update check.
// Return a boolean value indicating whether an update was found.
// Notes:
// * This event fires on the poller thread, so you can make network requests
// to check for updates & it will be done asyncronously.
// * Because this event has a non void return value, it can only
// be handled by one event handler. It can not be multi-cast.
//**************************************************************
public delegate bool CheckForUpdateEventHandler(object sender, EventArgs e);
public event CheckForUpdateEventHandler OnCheckForUpdate;
//**************************************************************
// UpdateDetected Event
// - Fired when a new udpate is detected.
// - Fired on the UI thread.
//**************************************************************
public delegate void UpdateDetectedEventHandler(object sender, EventArgs e);
public event UpdateDetectedEventHandler OnUpdateDetected;
//**************************************************************
// UpdateComplete Event
// - Fired when an update is complete.
// - Fired on the UI thread.
//**************************************************************
public delegate void UpdateCompleteEventHandler(object sender, UpdateCompleteEventArgs e);
public event UpdateCompleteEventHandler OnUpdateComplete;
//**************************************************************
// Constructor()
// If you use this contrusctor, be sure to call Initialize() after
// you set all of the properties.
//**************************************************************
public AppUpdater()
{
Poller = new ServerPoller(this);
Downloader = new AppDownloader(this);
}
//**************************************************************
// Constructor()
// - Design time constructor
//**************************************************************
public AppUpdater(System.ComponentModel.IContainer container)
{
Poller = new ServerPoller(this);
Downloader = new AppDownloader(this);
container.Add(this);
InitializeComponent();
}
//**************************************************************
// Constructor()
// - If you use this contructor, no need to call initialize
//**************************************************************
public AppUpdater(string updateUrl,
ChangeDetectionModes changeDetectionMode,
bool showDefaultUI,
bool autoFileLoad,
bool validateAssemblies)
{
//Load the Poller & Updater
Poller = new ServerPoller(this);
Downloader = new AppDownloader(this);
UpdateUrl = updateUrl;
ChangeDetectionMode = changeDetectionMode;
ShowDefaultUI = showDefaultUI;
AutoFileLoad = autoFileLoad;
Downloader.ValidateAssemblies = validateAssemblies;
Initialize();
}
//**************************************************************
// BeginInit()
//**************************************************************
public void BeginInit()
{
}
//**************************************************************
// EndInit()
// - Called by designer generated code, after designer generated
// code has set all properites
//**************************************************************
public void EndInit()
{
if (!this.DesignMode)
Initialize();
}
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
//**************************************************************
// Initialize()
// - Sets up the updater, generates the default manifest,
// starts the threads, etc...
//**************************************************************
public void Initialize()
{
//Load the Manifest Config File
string AppManifestPath =Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\";
string AppManifestName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location);
AppManifestName += ".xml";
AppManifestPath = Path.Combine(AppManifestPath,AppManifestName);
Manifest = AppManifest.Load(AppManifestPath);
if (ChangeDetectionMode == ChangeDetectionModes.DirectFileCheck)
EnableManifestGeneration();
if (AutoFileLoad == true)
EnableFileAutoLoad();
if (Poller.AutoStart == true)
Poller.Start();
Downloader.OnUpdateComplete += new AppDownloader.UpdateCompleteEventHandler(OnDownloaderComplete);
Application.ApplicationExit += new EventHandler(OnApplicationExit);
EventControl = new Control();
//Access the control handle to make sure it's bound to this thread
IntPtr h = EventControl.Handle;
//If an update was in progress when the app was shut down,
//continue the download now that the app has restarted.
if (Manifest.State.Phase != UpdatePhases.Complete)
{
Debug.WriteLine("APPMANAGER: Continuing update already in progress");
Downloader.Start();
}
}
//**************************************************************
// DownloadUpdate()
// - Starts downloading an update from the location specified at
// UpdateUrl. The udpate itself is done async.
//**************************************************************
public void DownloadUpdate()
{
Downloader.Start();
}
//**************************************************************
// CheckForUpdates()
// - Checks for an update
// - This is a sync call... normally called by the poller object
// on the poller thread.
//**************************************************************
public bool CheckForUpdates()
{
Debug.WriteLine("APPMANAGER: Checking for updates.");
bool retValue = false;
//If the OnCheckForUpdate event the caller is doing the check
if (OnCheckForUpdate != null)
retValue = OnCheckForUpdate(this, new EventArgs());
//otherwise do the check ourselves
else
//If versioning is enabled check to see if the version file has changed.
if (ChangeDetectionMode == ChangeDetectionModes.ServerManifestCheck)
{
ServerManifest vm = new ServerManifest();
vm.Load(UpdateUrl);
retValue = vm.IsServerVersionNewer(GetLatestInstalledVersion());
}
//If versioning is not enabled, check the files themselves
else
{
Resource currentResource;
foreach(Object r in Manifest.Resources.ResourceList)
{
currentResource = (Resource)(((DictionaryEntry)r).Value);
string url = UpdateUrl + currentResource.Name;
string FilePath = currentResource.FilePath;
if( WebFileLoader.CheckForFileUpdate(url, FilePath))
retValue = true;
}
}
//Fire the OnUpdateDetected Event on the UI thread
if (retValue == true)
{
if (OnUpdateDetected != null)
{
foreach ( UpdateDetectedEventHandler UC in OnUpdateDetected.GetInvocationList())
{
EventControl.BeginInvoke(UC,new object[] {this,new EventArgs()});
}
}
}
return retValue;
}
//**************************************************************
// GetLatestInstalledVersion()
//**************************************************************
public static Version GetLatestInstalledVersion()
{
//Load the AppStart config File
string ConfigFilePath =Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
ConfigFilePath = Path.Combine(Directory.GetParent(ConfigFilePath).FullName,"AppStart.config");
AppStartConfig Config = AppStartConfig.Load(ConfigFilePath);
AssemblyName AN = AssemblyName.GetAssemblyName(Config.AppExePath);
return AN.Version;
}
//**************************************************************
// RestartApp()
// - Shuts down the app, returns the AppStart restart return code
// - May not work for all apps. Application.Exit() is a soft shutdown
// - if your app has threads hanging around, the shutdown won't work
// - Use the ApplicationExit() event
//**************************************************************
public void RestartApp()
{
Environment.ExitCode = RestartAppReturnValue;
Application.Exit();
Poller.Stop();
Downloader.Stop();
}
//**************************************************************
// OnDownloaderComplete()
//**************************************************************
private void OnDownloaderComplete(object sender, UpdateCompleteEventArgs args)
{
if (OnUpdateComplete != null)
{
foreach ( UpdateCompleteEventHandler UC in OnUpdateComplete.GetInvocationList())
{
EventControl.BeginInvoke(UC,new object[] {sender, args});
}
}
EventControl.BeginInvoke(new UpdateCompleteEventHandler(UpdateCompleteOps),new object[] {sender, args});
}
//**************************************************************
// UpdateCompleteOps()
//**************************************************************
private void UpdateCompleteOps(object sender, UpdateCompleteEventArgs args)
{
if (ShowDefaultUI)
{
if (args.UpdateSucceeded)
{
UpdateForm F = new UpdateForm();
if (F.ShowDialog()==DialogResult.Yes)
RestartApp();
}
else
{
string ErrorMessage;
ErrorMessage = "The auto-update of this application failed with the following error message: \r\n\r\n"
+ args.ErrorMessage + "\r\n\r\n"
+ "To correct this problem, try rebooting the computer & re-launching this application.";
MessageBox.Show(ErrorMessage,"Application Update Failed",MessageBoxButtons.OK,MessageBoxIcon.Warning);
}
}
}
//**************************************************************
// OnAssemblyLoad()
// - Generates a manifest of files when doing DirectFile check style
// update checks
//**************************************************************
private void OnAssemblyLoad(Object sender, AssemblyLoadEventArgs args)
{
string[] AssemblyNames = args.LoadedAssembly.Location.Split(new Char[] {'\\'});
int index = AssemblyNames.Length-1;
string AssemblyName= AssemblyNames[index];
if (!Manifest.Resources.ResourceExists(AssemblyName) && IsLocalAssembly(args.LoadedAssembly))
{
Resource newResource = new Resource();
newResource.FilePath = args.LoadedAssembly.Location;
newResource.Name = AssemblyName;
newResource.AddedAtRuntime = true;
Manifest.Resources.AddResource(newResource);
Manifest.Update();
}
}
//**************************************************************
// OnAssemblyResolve()
// - This code is what does the auto-download of missing files
//**************************************************************
private Assembly OnAssemblyResolve(Object sender,ResolveEventArgs args)
{
//Check to see if the AssemblyLoad in this event is what caused the
//event to fire again. If so, the load failed.
if (LoadingAssembly==true)
return null;
LoadingAssembly=true;
string[] AssemblyNameParts = args.Name.Split(new Char[] {','}, 2);
string AssemblyName = AssemblyNameParts[0] + ".dll";
string FilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AssemblyName);
string url;
if (ChangeDetectionMode == ChangeDetectionModes.DirectFileCheck)
url = UpdateUrl + AssemblyName;
else
{
ServerManifest SM = new ServerManifest();
SM.Load(UpdateUrl);
url = Path.Combine(SM.ApplicationUrl,AssemblyName);
}
Debug.WriteLine("APPMANAGER: Auto-downloading assembly: " + AssemblyName + ". From: " + url);
try
{
WebFileLoader.UpdateFile(url, FilePath);
}
catch (Exception e)
{
Debug.WriteLine("APPMANAGER: Failed to download the missing assembly from the web server.");
Debug.WriteLine("APPMANAGER: " + e.ToString());
if (ShowDefaultUI)
MessageBox.Show("Unable to auto-download the missing parts of the application from:\r\n"
+ url + "\r\n\r\n"
+ "Make sure your connected to the network. If the problem persists re-install the application.");
return null;
}
Assembly assembly;
try
{
assembly = Assembly.Load(args.Name);
}
catch (Exception e)
{
Debug.WriteLine("APPMANAGER: Failed to load the auto-downloaded assembly.");
Debug.WriteLine("APPMANAGER: " + e.ToString());
return null;
}
finally
{
LoadingAssembly=false;
}
return assembly;
}
//**************************************************************
// IsLocalAssembly()
//**************************************************************
private bool IsLocalAssembly(Assembly assembly)
{
string AssemblyLocation = assembly.Location.Replace("/","\\").ToLower(new CultureInfo("en-US"));
string AppPath = AppDomain.CurrentDomain.BaseDirectory.ToLower(new CultureInfo("en-US"));
//Only log & replicate assemblies in the default app path
if ((AssemblyLocation.StartsWith(AppPath.ToLower(new CultureInfo("en-US")))) &&
(!assembly.FullName.StartsWith("AppManager")))
return true;
else
return false;
}
//**************************************************************
// OnThreadExit()
//**************************************************************
private void OnApplicationExit(Object sender, EventArgs args)
{
//stop the poller thread if it isn't already.
Poller.Stop();
Downloader.Stop();
}
//**************************************************************
// EnableFileAutoLoad()
//**************************************************************
private void EnableFileAutoLoad()
{
AppDomain App = AppDomain.CurrentDomain;
App.AssemblyResolve += new ResolveEventHandler(OnAssemblyResolve);
}
//**************************************************************
// DisableFileAutoLoad()
//**************************************************************
private void DisableFileAutoLoad()
{
AppDomain App = AppDomain.CurrentDomain;
App.AssemblyResolve -= new ResolveEventHandler(OnAssemblyResolve);
}
//**************************************************************
// EnableManifestGeneration()
//**************************************************************
private void EnableManifestGeneration()
{
//Register for Assembly Load events
AppDomain App = AppDomain.CurrentDomain;
App.AssemblyLoad += new AssemblyLoadEventHandler(OnAssemblyLoad);
//Make sure the currently loaded assemblies are in the manifest
//The assembly load event will catch any assemblies loaded later
Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i=0; i