Example #1
0
        public void UpdaterDeploymentAndIPCWorks()
        {
            var dto = new NauIpc.NauDto
                      	{
                      		Configs = UpdateManager.Instance.Config,
                      		Tasks = new List<IUpdateTask>
                      		        	{
                                            new FileUpdateTask {Description = "Task #1", ExecutionStatus = TaskExecutionStatus.RequiresAppRestart},
                      		        	},
                      		AppPath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName,
                      		WorkingDirectory = Environment.CurrentDirectory,
                      		RelaunchApplication = false,
                      	};

            var path = dto.Configs.TempFolder;

            if (Directory.Exists(path))
                FileSystem.DeleteDirectory(path);

            NauIpc.ExtractUpdaterFromResource(path, dto.Configs.UpdateExecutableName);
            var info = new ProcessStartInfo
            {
                UseShellExecute = true,
                WorkingDirectory = Environment.CurrentDirectory,
                FileName = Path.Combine(path, dto.Configs.UpdateExecutableName),
                Arguments = string.Format(@"""{0}"" -showConsole", dto.Configs.UpdateProcessName),
            };

            var p = NauIpc.LaunchProcessAndSendDto(dto, info, dto.Configs.UpdateProcessName);
            Assert.IsNotNull(p);
            p.WaitForExit();

            Assert.IsTrue(Directory.Exists(path));
            Assert.IsTrue(File.Exists(Path.Combine(path, "Foo.exe")));
            Assert.IsTrue(File.Exists(Path.Combine(path, "AppUpdate.dll")));

            // Cleanup test
            FileSystem.DeleteDirectory(path);
        }
Example #2
0
        /// <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>
        /// <param name="updaterDoLogging">true if the updater writes to a log file; false otherwise</param>
        /// <param name="updaterShowConsole">true if the updater shows the console window; false otherwise</param>
        /// <returns>True if successful (unless a restart was required</returns>
        public void ApplyUpdates(bool relaunchApplication, bool updaterDoLogging, bool updaterShowConsole)
        {
            if (IsWorking)
                throw new InvalidOperationException("Another update process is already in progress");

            lock (UpdatesToApply)
            {
                using (WorkScope.New(isWorking => IsWorking = isWorking))
                {
                    bool revertToDefaultBackupPath = true;

                    // Set current directory the the application directory
                    // this prevents the updater from writing to e.g. c:\windows\system32
                    // if the process is started by autorun on windows logon.
            // ReSharper disable AssignNullToNotNullAttribute
                    Environment.CurrentDirectory = Path.GetDirectoryName(ApplicationPath);
            // ReSharper restore AssignNullToNotNullAttribute

                    // Make sure the current backup folder is accessible for writing from this process
                    string backupParentPath = Path.GetDirectoryName(Config.BackupFolder) ?? string.Empty;
                    if (Directory.Exists(backupParentPath) && 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(Config.BackupFolder))
                                FileSystem.DeleteDirectory(Config.BackupFolder);
                            revertToDefaultBackupPath = false;
                        }
                        catch (UnauthorizedAccessException)
                        {
                        }

                        // Attempt to (re-)create the backup folder
                        try
                        {
                            Directory.CreateDirectory(Config.BackupFolder);

                            if (!PermissionsCheck.HaveWritePermissionsForFolder(Config.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)
                    {
                        Config._backupFolder = Path.Combine(
                            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                            Config.UpdateProcessName + "UpdateBackups" + DateTime.UtcNow.Ticks);

                        try
                        {
                            Directory.CreateDirectory(Config.BackupFolder);
                        }
                        catch (UnauthorizedAccessException ex)
                        {
                            // We can't backup, so we abort
                            throw new UpdateProcessFailedException("Could not create backup folder " + Config.BackupFolder, ex);
                        }
                    }

                    bool runPrivileged = false, hasColdUpdates = false;
                    State = UpdateProcessState.RollbackRequired;
                    foreach (var task in UpdatesToApply)
                    {
                        IUpdateTask t = task;
                        task.ProgressDelegate += status => TaskProgressCallback(status, t);

                        try
                        {
                            // Execute the task
                            task.ExecutionStatus = task.Execute(false);
                        }
                        catch (Exception ex)
                        {
                            task.ExecutionStatus = TaskExecutionStatus.Failed; // mark the failing task before rethrowing
                            throw new UpdateProcessFailedException("Update task execution failed: " + task.Description, ex);
                        }

                        if (task.ExecutionStatus == TaskExecutionStatus.RequiresAppRestart
                            || task.ExecutionStatus == TaskExecutionStatus.RequiresPrivilegedAppRestart)
                        {
                            // Record that we have cold updates to run, and if required to run any of them privileged
                            runPrivileged = runPrivileged || task.ExecutionStatus == TaskExecutionStatus.RequiresPrivilegedAppRestart;
                            hasColdUpdates = true;
                            continue;
                        }

                        // We are being quite explicit here - only Successful return values are considered
                        // to be Ok (cold updates are already handled above)
                        if (task.ExecutionStatus != TaskExecutionStatus.Successful)
                            throw new UpdateProcessFailedException("Update task execution failed: " + task.Description);
                    }

                    // If an application restart is required
                    if (hasColdUpdates)
                    {
                        var dto = new NauIpc.NauDto
                                  	{
                                  		Configs = Instance.Config,
                                  		Tasks = Instance.UpdatesToApply,
                                  		AppPath = ApplicationPath,
                                  		WorkingDirectory = Environment.CurrentDirectory,
                                  		RelaunchApplication = relaunchApplication,
                                  		LogItems = Logger.LogItems,
                                  	};

                        NauIpc.ExtractUpdaterFromResource(Config.TempFolder, Instance.Config.UpdateExecutableName);

                        var info = new ProcessStartInfo
                                   	{
                                   		UseShellExecute = true,
                                   		WorkingDirectory = Environment.CurrentDirectory,
                                   		FileName = Path.Combine(Config.TempFolder, Instance.Config.UpdateExecutableName),
                                   		Arguments =
                                   			string.Format(@"""{0}"" {1} {2}", Config.UpdateProcessName,
                                   			              updaterShowConsole ? "-showConsole" : string.Empty,
                                   			              updaterDoLogging ? "-log" : string.Empty),
                                   	};

                        if (!updaterShowConsole)
                        {
                            info.WindowStyle = ProcessWindowStyle.Hidden;
                            info.CreateNoWindow = true;
                        }

                        // If we can't write to the destination folder, then lets try elevating priviledges.
                        if (runPrivileged || !PermissionsCheck.HaveWritePermissionsForFolder(Environment.CurrentDirectory))
                        {
                            info.Verb = "runas";
                        }

                        bool createdNew;
                        _shutdownMutex = new Mutex(true, Config.UpdateProcessName + "Mutex", out createdNew);
                        if (NauIpc.LaunchProcessAndSendDto(dto, info, Config.UpdateProcessName) == null)
                            throw new UpdateProcessFailedException("Could not launch cold update process");

                        Environment.Exit(0);
                    }

                    State = UpdateProcessState.AppliedSuccessfully;
                    UpdatesToApply.Clear();
                }
            }
        }