📄 downloader.cs
字号:
using System;
using System.Diagnostics;
using System.Threading;
using System.Collections;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Reflection;
using System.ComponentModel;
using System.Globalization;
namespace Microsoft.Samples.AppUpdater
{
//**************************************************************
// AppDownloader class
// - Responsible for downloading & applying updates.
//**************************************************************
public class AppDownloader
{
private AppUpdater AppMan;
private Thread UpdaterThread;
private AppStartConfig Config;
private UpdateLog Log;
private UpdateCompleteEventArgs UpdateEventArgs;
private AppKeys Keys;
//Enable checking of downloaded assemblies
private bool _ValidateAssemblies = false;
[DefaultValue(false)]
[Description("Specifies whether or not downloaded assemblies must be signed with valid public keys inorder to be downloaded.")]
public bool ValidateAssemblies
{
get {return _ValidateAssemblies;}
set {_ValidateAssemblies = value;}
}
//Delay between download retries when a network problem is encountered
private int _SecondsBetweenDownloadRetry = 60;
[DefaultValue(60)]
[Description("Seconds between download retry attempts.")]
public int SecondsBetweenDownloadRetry
{
get { return _SecondsBetweenDownloadRetry; }
set { _SecondsBetweenDownloadRetry = value; }
}
//Number of times to retry when a download error is encountered
private int _DownloadRetryAttempts = 3;
[DefaultValue(3)]
[Description("Number of times to retry downloads when an error is encountered.")]
public int DownloadRetryAttempts
{
get { return _DownloadRetryAttempts; }
set { _DownloadRetryAttempts = value; }
}
//Number of times to retry the overall update
private int _UpdateRetryAttempts = 2;
[DefaultValue(2)]
[Description("Number of times times to retry the app update.")]
public int UpdateRetryAttempts
{
get { return _UpdateRetryAttempts; }
set { _UpdateRetryAttempts = value; }
}
[Browsable(false)]
public UpdateState State
{
get
{
return AppMan.Manifest.State;
}
}
//**************************************************************
// OnUpdateComplete Event
//**************************************************************
internal delegate void UpdateCompleteEventHandler(object sender, UpdateCompleteEventArgs e);
internal event UpdateCompleteEventHandler OnUpdateComplete;
//**************************************************************
// AppUpdater Constructor
//**************************************************************
public AppDownloader(AppUpdater appMan)
{
AppMan = appMan;
Log = new UpdateLog();
UpdateEventArgs = new UpdateCompleteEventArgs();
}
//**************************************************************
// Start()
//**************************************************************
public void Start()
{
if (UpdaterThread == null)
UpdaterThread = new Thread(new ThreadStart(RunThread));
else if (!UpdaterThread.IsAlive)
UpdaterThread = new Thread(new ThreadStart(RunThread));
UpdaterThread.Name="Updater Thread";
if (!UpdaterThread.IsAlive)
UpdaterThread.Start();
}
//**************************************************************
// Stop()
//**************************************************************
public void Stop()
{
if (UpdaterThread != null && UpdaterThread.IsAlive)
{
UpdaterThread.Abort();
UpdaterThread = null;
}
}
//**************************************************************
// RunThread()
//**************************************************************
public void RunThread()
{
Debug.WriteLine("APPMANAGER: Starting Update");
//Load the AppStart config File
string ConfigFilePath =Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
ConfigFilePath = Path.Combine(Directory.GetParent(ConfigFilePath).FullName,"AppStart.config");
Config = AppStartConfig.Load(ConfigFilePath);
try
{
//Mark in the manifest that a download is in progress. Persisted in
//manifest in case the app is stop & restarted during the download.
if (AppMan.Manifest.State.Phase == UpdatePhases.Complete)
{
AppMan.Manifest.State.Phase = UpdatePhases.Scavenging;
AppMan.Manifest.State.UpdateFailureCount=0;
AppMan.Manifest.State.UpdateFailureEncoutered = false;
AppMan.Manifest.State.DownloadDestination = CreateTempDirectory();
if (AppMan.ChangeDetectionMode == ChangeDetectionModes.ServerManifestCheck)
{
ServerManifest SM = new ServerManifest();
SM.Load(AppMan.UpdateUrl);
AppMan.Manifest.State.DownloadSource = SM.ApplicationUrl;
}
else
AppMan.Manifest.State.DownloadSource = AppMan.UpdateUrl;
AppMan.Manifest.Update();
}
//Scavenge Existing Directories
if (AppMan.Manifest.State.Phase == UpdatePhases.Scavenging)
{
Debug.WriteLine("APPMANAGER: Scavaging older versions");
Scavenge();
AppMan.Manifest.State.Phase=UpdatePhases.Downloading;
AppMan.Manifest.Update();
}
//Download the New Version
if (AppMan.Manifest.State.Phase == UpdatePhases.Downloading)
{
Debug.WriteLine("APPMANAGER: Downloading new version");
Download();
AppMan.Manifest.State.Phase = UpdatePhases.Validating;
AppMan.Manifest.Update();
}
//Validate the downloaded bits
if (AppMan.Manifest.State.Phase == UpdatePhases.Validating)
{
Debug.WriteLine("APPMANAGER: Downloading new version");
Validate();
AppMan.Manifest.State.Phase = UpdatePhases.Merging;
AppMan.Manifest.Update();
}
//Merge the existing version into the new version
if (AppMan.Manifest.State.Phase == UpdatePhases.Merging)
{
Debug.WriteLine("APPMANAGER: Merging current & new versions");
MergeDirectory(AppDomain.CurrentDomain.BaseDirectory,AppMan.Manifest.State.DownloadDestination);
AppMan.Manifest.State.Phase = UpdatePhases.Finalizing;
AppMan.Manifest.Update();
}
//Finalize the update. Rename the new version directory, etc...
if (AppMan.Manifest.State.Phase == UpdatePhases.Finalizing)
{
Debug.WriteLine("APPMANAGER: Finalizing update");
FinalizeUpdate();
}
//Reset Update State
AppMan.Manifest.State.Phase = UpdatePhases.Complete;
AppMan.Manifest.State.UpdateFailureCount=0;
AppMan.Manifest.State.UpdateFailureEncoutered = false;
AppMan.Manifest.State.DownloadSource = "";
AppMan.Manifest.State.DownloadDestination = "";
AppMan.Manifest.State.NewVersionDirectory = "";
AppMan.Manifest.Update();
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
Debug.WriteLine("APPMANAGER: ThreadAborted: Updater Thread stopped, stopping download.");
return;
}
catch (Exception e)
{
UpdateEventArgs.FailureException = e;
}
if (AppMan.Manifest.State.Phase != UpdatePhases.Complete)
HandleUpdateFailure();
else
HandleUpdateSuccess();
}
//**************************************************************
// HandleUpdateSuccess()
//**************************************************************
private void HandleUpdateSuccess()
{
Debug.WriteLine("APPMANAGER: Update Succeeded");
try
{
Log.AddSuccess(GetFileVersion(Config.AppExePath));
}
catch (Exception e)
{
Debug.WriteLine("APPMANAGER: Failed to log update success");
Debug.WriteLine("APPMANAGER: " + e.ToString());
}
//Fire Update Event
if (OnUpdateComplete != null)
{
UpdateEventArgs.UpdateSucceeded = true;
UpdateEventArgs.NewVersion = new Version(GetFileVersion(Config.AppExePath));
if (UpdateEventArgs.ErrorMessage == "")
UpdateEventArgs.ErrorMessage = "Unknown Error";
OnUpdateComplete(this, UpdateEventArgs);
}
}
//**************************************************************
// HandleUpdateFailure()
//**************************************************************
private void HandleUpdateFailure()
{
Debug.WriteLine("APPMANAGER: Update Failed");
try
{
Log.AddError(UpdateEventArgs.FailureException.ToString());
}
catch (Exception e)
{
Debug.WriteLine("APPMANAGER: Failed to log update failure");
Debug.WriteLine("APPMANAGER: " + e.ToString());
}
AppMan.Manifest.State.UpdateFailureEncoutered = true;
AppMan.Manifest.State.UpdateFailureCount++;
AppMan.Manifest.Update();
//If the update has failed too many times
if (AppMan.Manifest.State.UpdateFailureCount >= UpdateRetryAttempts)
{
Debug.WriteLine("APPMANAGER: Updated Failed 2 times. Reseting Manifest");
AppMan.Manifest.State.Phase = UpdatePhases.Complete;
AppMan.Manifest.State.UpdateFailureEncoutered = false;
AppMan.Manifest.State.UpdateFailureCount = 0;
AppMan.Manifest.State.DownloadSource = "";
AppMan.Manifest.State.DownloadDestination = "";
AppMan.Manifest.Update();
}
if (OnUpdateComplete != null)
{
UpdateEventArgs.UpdateSucceeded = false;
if (UpdateEventArgs.ErrorMessage == "")
UpdateEventArgs.ErrorMessage = "Unknown Error";
OnUpdateComplete(this, UpdateEventArgs);
}
}
//**************************************************************
// Download()
//**************************************************************
private void Download()
{
bool DownloadInProgress = true;
int DownloadAttemptCount = 0;
int SecondsToSleep = 0;
while (DownloadInProgress)
{
Thread.Sleep(TimeSpan.FromSeconds(SecondsToSleep));
SecondsToSleep = SecondsBetweenDownloadRetry;
DownloadAttemptCount++;
Trace.WriteLine("APPMANAGER: Attempting to download update from: " + AppMan.Manifest.State.DownloadSource);
try
{
int UpdateCount = WebFileLoader.CopyDirectory(AppMan.Manifest.State.DownloadSource,AppMan.Manifest.State.DownloadDestination);
Debug.WriteLine("APPMANAGER: Number of files updated from the server: " + UpdateCount);
Debug.WriteLine("APPMANAGER: App update downloaded successfully");
DownloadInProgress = false;
}
//Things that could go wrong while downloading are pretty much any kind of
//network problem, like the client just going offline. However, this can cause
//itself to manifest in any number of ways... like exceptions on the stream
//objects used to copy the file to disk.
catch (WebException e)
{
Debug.WriteLine("APPMANAGER: Update download failed due to network exception:");
Debug.WriteLine("APPMANAGER: " + e.ToString());
if (DownloadAttemptCount >= DownloadRetryAttempts)
{
Debug.WriteLine("APPMANAGER: Download attempt has failed 3 times. Aborting Update");
UpdateEventArgs.ErrorMessage = "Download of a new update from '"+ AppMan.Manifest.State.DownloadSource
+"' failed with the network error: " + e.Message;
throw e;
}
}
catch (IOException e)
{
Debug.WriteLine("APPMANAGER: Update download failed due to file I/O exception:");
Debug.WriteLine("APPMANAGER: " + e.ToString());
if (DownloadAttemptCount >= DownloadRetryAttempts)
{
Debug.WriteLine("APPMANAGER: Download attempt has failed 3 times. Aborting Update");
UpdateEventArgs.ErrorMessage = "Saving the new update to disk at '" + AppMan.Manifest.State.DownloadDestination
+"' failed with the following error: " + e.Message;
throw e;
}
}
catch (Exception e)
{
Debug.WriteLine("APPMANAGER: Update download failed due to the following error: " + e.Message);
Debug.WriteLine("APPMANAGER: " + e.ToString());
if (DownloadAttemptCount >= DownloadRetryAttempts)
{
Debug.WriteLine("APPMANAGER: Download attempt has failed 3 times. Aborting Update");
UpdateEventArgs.ErrorMessage = "Update failed with the following error: '"+ e.Message + "'";
throw e;
}
}
}
}
//**************************************************************
// Validate()
//**************************************************************
private void Validate()
{
if (!ValidateAssemblies)
return;
//Initialize the Keys Object
Keys = new AppKeys(AppMan.Manifest.State.DownloadSource);
Keys.InitializeKeyCheck();
try
{
ValidateDirectory(AppMan.Manifest.State.DownloadDestination);
}
catch (Exception)
{
Keys.UnInitializeKeyCheck();
//Remove the downloaded files if any error occurs in validation.
HardDirectoryDelete(AppMan.Manifest.State.DownloadDestination);
//Set the update failure count to max.
//Don't retry the update another time. This will cause the update
//to reset.
AppMan.Manifest.State.UpdateFailureCount = UpdateRetryAttempts;
AppMan.Manifest.Update();
throw;
}
Keys.UnInitializeKeyCheck();
}
//**************************************************************
// ValidateDirectory()
//**************************************************************
private void ValidateDirectory(string source)
{
try
{
//Get a reference to the source directory
DirectoryInfo Source = new DirectoryInfo(source);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -