/// <summary> /// Starts the updater executable and sends update data to it /// </summary> /// <param name="relaunchApplication">true if relaunching the caller application is required; false otherwise</param> /// <returns>True if successful (unless a restart was required</returns> public bool ApplyUpdates(bool relaunchApplication) { lock (UpdatesToApply) { LatestError = null; // Make sure the current backup folder is accessible for writing from this process if (!Utils.PermissionsCheck.HaveWritePermissionsForFolder(BackupFolder)) { _BackupFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), UpdateProcessName + "UpdateBackups"); } else { // Remove old backup folder, in case this same folder was used previously, // and it wasn't removed for some reason if (Directory.Exists(BackupFolder)) { Directory.Delete(BackupFolder, true); } } Directory.CreateDirectory(BackupFolder); var executeOnAppRestart = new Dictionary <string, object>(); State = UpdateProcessState.RollbackRequired; foreach (var task in UpdatesToApply) { // First, execute the task if (!task.Execute()) { // TODO: notify about task execution failure using exceptions continue; } // Add any pending cold updates to the list var en = task.GetColdUpdates(); while (en.MoveNext()) { // Last write wins executeOnAppRestart[en.Current.Key] = en.Current.Value; } } // If an application restart is required if (executeOnAppRestart.Count > 0) { // Add some environment variables to the dictionary object which will be passed to the updater executeOnAppRestart["ENV:AppPath"] = ApplicationPath; executeOnAppRestart["ENV:WorkingDirectory"] = Environment.CurrentDirectory; executeOnAppRestart["ENV:TempFolder"] = TempFolder; executeOnAppRestart["ENV:BackupFolder"] = BackupFolder; executeOnAppRestart["ENV:RelaunchApplication"] = relaunchApplication; // Naming it updater.exe seem to trigger the UAC, and we don't want that var updStarter = new UpdateStarter(Path.Combine(TempFolder, "foo.exe"), executeOnAppRestart, UpdateProcessName); bool createdNew; using (var _ = new Mutex(true, UpdateProcessName, out createdNew)) { if (!updStarter.Start()) { return(false); } Environment.Exit(0); } } State = UpdateProcessState.AppliedSuccessfully; UpdatesToApply.Clear(); } return(true); }
/// <summary> /// Starts the updater executable and sends update data to it /// </summary> /// <param name="relaunchApplication">true if relaunching the caller application is required; false otherwise</param> /// <returns>True if successful (unless a restart was required</returns> public bool ApplyUpdates(bool relaunchApplication) { lock (UpdatesToApply) { LatestError = null; bool revertToDefaultBackupPath = true; // Make sure the current backup folder is accessible for writing from this process string backupParentPath = Path.GetDirectoryName(BackupFolder) ?? string.Empty; if (Directory.Exists(backupParentPath) && Utils.PermissionsCheck.HaveWritePermissionsForFolder(backupParentPath)) { // Remove old backup folder, in case this same folder was used previously, // and it wasn't removed for some reason try { if (Directory.Exists(BackupFolder)) Directory.Delete(BackupFolder, true); revertToDefaultBackupPath = false; } catch (UnauthorizedAccessException) { } // Attempt to (re-)create the backup folder try { Directory.CreateDirectory(BackupFolder); if (!Utils.PermissionsCheck.HaveWritePermissionsForFolder(BackupFolder)) revertToDefaultBackupPath = true; } catch (UnauthorizedAccessException) { // We're having permissions issues with this folder, so we'll attempt // using a backup in a default location revertToDefaultBackupPath = true; } } if (revertToDefaultBackupPath) { _backupFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), UpdateProcessName + "UpdateBackups"); try { Directory.CreateDirectory(BackupFolder); } catch (UnauthorizedAccessException ex) { // We can't backup, so we abort LatestError = ex.ToString(); return false; } } var executeOnAppRestart = new Dictionary<string, object>(); State = UpdateProcessState.RollbackRequired; foreach (var task in UpdatesToApply) { // First, execute the task if (!task.Execute()) { // TODO: notify about task execution failure using exceptions continue; } // Add any pending cold updates to the list var en = task.GetColdUpdates(); while (en.MoveNext()) { // Last write wins executeOnAppRestart[en.Current.Key] = en.Current.Value; } } // If an application restart is required if (executeOnAppRestart.Count > 0) { // Add some environment variables to the dictionary object which will be passed to the updater executeOnAppRestart["ENV:AppPath"] = ApplicationPath; executeOnAppRestart["ENV:WorkingDirectory"] = Environment.CurrentDirectory; executeOnAppRestart["ENV:TempFolder"] = TempFolder; executeOnAppRestart["ENV:BackupFolder"] = BackupFolder; executeOnAppRestart["ENV:RelaunchApplication"] = relaunchApplication; if (!Directory.Exists(TempFolder)) Directory.CreateDirectory(TempFolder); // Naming it updater.exe seem to trigger the UAC, and we don't want that var updStarter = new UpdateStarter(Path.Combine(TempFolder, "foo.exe"), executeOnAppRestart, UpdateProcessName); bool createdNew; using (var _ = new Mutex(true, UpdateProcessName, out createdNew)) { if (!updStarter.Start()) return false; Environment.Exit(0); } } State = UpdateProcessState.AppliedSuccessfully; UpdatesToApply.Clear(); } return true; }
/// <summary> /// Starts the updater executable and sends update data to it /// </summary> /// <param name="RestartApplication">true if relaunching the caller application is required; false otherwise</param> /// <returns>True if successful (unless a restart was required</returns> public bool ApplyUpdates(bool RelaunchApplication) { lock (UpdatesToApply) { // Make sure the current backup folder is accessible for writing from this process if (!Utils.PermissionsCheck.HaveWritePermissionsForFolder(BackupFolder)) { _BackupFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), UpdateProcessName + "UpdateBackups"); } else { // Remove old backup folder, in case this same folder was used previously, // and it wasn't removed for some reason if (Directory.Exists(BackupFolder)) Directory.Delete(BackupFolder, true); } Directory.CreateDirectory(BackupFolder); Dictionary<string, object> executeOnAppRestart = new Dictionary<string, object>(); State = UpdateProcessState.RollbackRequired; foreach (IUpdateTask task in UpdatesToApply) { if (!task.Execute()) { // TODO: notify about task execution failure using exceptions } // This is the only place where we have non-generalized code in UpdateManager. // The reason for that is the updater currently only supports replacing bytes by path, which // only FileUpdaterTask does - so there's no reason to generalize this portion too. else if (task is FileUpdateTask && task.Attributes.ContainsKey("localPath")) { if (!task.Attributes.ContainsKey("apply") || (task.Attributes.ContainsKey("apply") && "app-restart".Equals(task.Attributes["apply"]))) { FileUpdateTask fut = (FileUpdateTask)task; executeOnAppRestart[task.Attributes["localPath"]] = fut.tempFile; } } } // If an application restart is required if (executeOnAppRestart.Count > 0) { // Add some environment variables to the dictionary object which will be passed to the updater executeOnAppRestart["ENV:AppPath"] = ApplicationPath; executeOnAppRestart["ENV:TempFolder"] = TempFolder; executeOnAppRestart["ENV:BackupFolder"] = BackupFolder; executeOnAppRestart["ENV:RelaunchApplication"] = RelaunchApplication; UpdateStarter updStarter = new UpdateStarter(Path.Combine(TempFolder, "updater.exe"), executeOnAppRestart, UpdateProcessName); bool createdNew; using (Mutex mutex = new Mutex(true, UpdateProcessName, out createdNew)) { if (!updStarter.Start()) return false; Environment.Exit(0); } } State = UpdateProcessState.AppliedSuccessfully; UpdatesToApply.Clear(); } return true; }