From ae6df46d519043def41bafb325f3be7b8bb92458 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Thu, 4 Feb 2016 17:20:37 +0100 Subject: [PATCH 01/22] #54 Replaced the Win32 pipe handling with .NET --- .../NAppUpdate.Framework.csproj | 3 +- src/NAppUpdate.Framework/Utils/NauIpc.cs | 163 ++++-------------- 2 files changed, 37 insertions(+), 129 deletions(-) diff --git a/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj b/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj index b1ac3823..fa6afa94 100644 --- a/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj +++ b/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj @@ -10,7 +10,7 @@ Properties NAppUpdate.Framework NAppUpdate.Framework - v2.0 + v3.5 512 true NAppUpdate.snk @@ -33,6 +33,7 @@ false false true + true diff --git a/src/NAppUpdate.Framework/Utils/NauIpc.cs b/src/NAppUpdate.Framework/Utils/NauIpc.cs index 91f84a24..597aeb9f 100644 --- a/src/NAppUpdate.Framework/Utils/NauIpc.cs +++ b/src/NAppUpdate.Framework/Utils/NauIpc.cs @@ -9,6 +9,7 @@ using System.Runtime.Serialization.Formatters.Binary; using NAppUpdate.Framework.Common; using NAppUpdate.Framework.Tasks; +using System.IO.Pipes; namespace NAppUpdate.Framework.Utils { @@ -29,158 +30,64 @@ internal class NauDto public bool RelaunchApplication { get; set; } } - [DllImport("kernel32.dll", SetLastError = true)] - private static extern SafeFileHandle CreateNamedPipe( - String pipeName, - uint dwOpenMode, - uint dwPipeMode, - uint nMaxInstances, - uint nOutBufferSize, - uint nInBufferSize, - uint nDefaultTimeOut, - IntPtr lpSecurityAttributes); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern int ConnectNamedPipe( - SafeFileHandle hNamedPipe, - IntPtr lpOverlapped); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern SafeFileHandle CreateFile( - String pipeName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplate); - - //private const uint DUPLEX = (0x00000003); - private const uint WRITE_ONLY = (0x00000002); - private const uint FILE_FLAG_OVERLAPPED = (0x40000000); - - const uint GENERIC_READ = (0x80000000); - //static readonly uint GENERIC_WRITE = (0x40000000); - const uint OPEN_EXISTING = 3; - - //Which really isn't an error... - const uint ERROR_PIPE_CONNECTED = 535; - - internal static string GetPipeName(string syncProcessName) - { - return string.Format("\\\\.\\pipe\\{0}", syncProcessName); - } - - private class State - { - public readonly EventWaitHandle eventWaitHandle; - public int result { get; set; } - public SafeFileHandle clientPipeHandle { get; set; } - public Exception exception; - - public State() - { - eventWaitHandle = new ManualResetEvent(false); - } - } - - internal static uint BUFFER_SIZE = 4096; + private const int PIPE_TIMEOUT = 5000; + /// + /// Launches the specifies process and sends the dto object to it using a named pipe + /// + /// Dto object to send + /// Process info for the process to start + /// Name of the pipe to write to + /// The started process public static Process LaunchProcessAndSendDto(NauDto dto, ProcessStartInfo processStartInfo, string syncProcessName) { Process p; - State state = new State(); - - using (state.clientPipeHandle = CreateNamedPipe( - GetPipeName(syncProcessName), - WRITE_ONLY | FILE_FLAG_OVERLAPPED, - 0, - 1, // 1 max instance (only the updater utility is expected to connect) - BUFFER_SIZE, - BUFFER_SIZE, - 0, - IntPtr.Zero)) - { - //failed to create named pipe - if (state.clientPipeHandle.IsInvalid) - { - throw new Exception("Launch process client: Failed to create named pipe, handle is invalid."); - } - // This will throw Win32Exception if the user denies UAC + using (NamedPipeServerStream pipe = new NamedPipeServerStream(syncProcessName, PipeDirection.Out, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous)) + { p = Process.Start(processStartInfo); - ThreadPool.QueueUserWorkItem(ConnectPipe, state); - //A rather arbitary five seconds, perhaps better to be user configurable at some point? - state.eventWaitHandle.WaitOne(10000); + var asyncResult = pipe.BeginWaitForConnection(null, null); - //failed to connect client pipe - if (state.result == 0) + if (asyncResult.AsyncWaitHandle.WaitOne(PIPE_TIMEOUT)) { - throw new Exception("Launch process client: Failed to connect to named pipe", state.exception); - } + pipe.EndWaitForConnection(asyncResult); - //client connection successfull - using (var fStream = new FileStream(state.clientPipeHandle, FileAccess.Write, (int)BUFFER_SIZE, true)) + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(pipe, dto); + } + else { - new BinaryFormatter().Serialize(fStream, dto); - fStream.Flush(); - fStream.Close(); + throw new TimeoutException("The NamedPipeServerStream timed out waiting for a named pipe connection"); } } return p; } - internal static void ConnectPipe(object stateObject) + /// + /// Reads the dto object from the named pipe + /// + /// Name of the pipe to read from + /// The dto object read from the pipe + public static NauDto ReadDto(string syncProcessName) { - State state = (State)stateObject; + NauDto dto; - try + using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", syncProcessName, PipeDirection.In, PipeOptions.Asynchronous)) { - state.result = ConnectNamedPipe(state.clientPipeHandle, IntPtr.Zero); - } - catch (Exception e) - { - state.exception = e; - } + pipe.Connect(PIPE_TIMEOUT); - int error = Marshal.GetLastWin32Error(); - //Check for the oddball: ERROR - PIPE CONNECTED - //Ref: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365146%28v=vs.85%29.aspx - if (error == ERROR_PIPE_CONNECTED) - { - state.result = 1; + BinaryFormatter formatter = new BinaryFormatter(); + dto = formatter.Deserialize(pipe) as NauDto; } - else if (error != 0) - { - state.exception = new Win32Exception(error); - } - - state.eventWaitHandle.Set(); // signal we're done - } - - internal static object ReadDto(string syncProcessName) - { - using (SafeFileHandle pipeHandle = CreateFile( - GetPipeName(syncProcessName), - GENERIC_READ, - 0, - IntPtr.Zero, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - IntPtr.Zero)) + if (dto == null) { - - if (pipeHandle.IsInvalid) - return null; - - using (var fStream = new FileStream(pipeHandle, FileAccess.Read, (int)BUFFER_SIZE, true)) - { - return new BinaryFormatter().Deserialize(fStream); - } + throw new Exception("Failed to read the dto from the pipe stream"); } + + return dto; } internal static void ExtractUpdaterFromResource(string updaterPath, string hostExeName) @@ -212,4 +119,4 @@ internal static void ExtractUpdaterFromResource(string updaterPath, string hostE } } } -} \ No newline at end of file +} From 168588f2ba2ea1b27af602f9b1b9996e2ef16258 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Fri, 5 Feb 2016 14:58:49 +0100 Subject: [PATCH 02/22] #54 Increased the pipe timeout to 15s --- src/NAppUpdate.Framework/Utils/NauIpc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NAppUpdate.Framework/Utils/NauIpc.cs b/src/NAppUpdate.Framework/Utils/NauIpc.cs index 597aeb9f..a9c645a3 100644 --- a/src/NAppUpdate.Framework/Utils/NauIpc.cs +++ b/src/NAppUpdate.Framework/Utils/NauIpc.cs @@ -30,7 +30,7 @@ internal class NauDto public bool RelaunchApplication { get; set; } } - private const int PIPE_TIMEOUT = 5000; + private const int PIPE_TIMEOUT = 15000; /// /// Launches the specifies process and sends the dto object to it using a named pipe From 074039aaa3e18bc2e97dc354f26e53aa83341390 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Fri, 5 Feb 2016 15:02:48 +0100 Subject: [PATCH 03/22] #54 More detailed error handling in LaunchProcessAndSendDto --- src/NAppUpdate.Framework/Utils/NauIpc.cs | 11 ++++++++++- .../Utils/ProcessStartFailedException.cs | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/NAppUpdate.Framework/Utils/ProcessStartFailedException.cs diff --git a/src/NAppUpdate.Framework/Utils/NauIpc.cs b/src/NAppUpdate.Framework/Utils/NauIpc.cs index a9c645a3..00abaa12 100644 --- a/src/NAppUpdate.Framework/Utils/NauIpc.cs +++ b/src/NAppUpdate.Framework/Utils/NauIpc.cs @@ -47,6 +47,11 @@ public static Process LaunchProcessAndSendDto(NauDto dto, ProcessStartInfo proce { p = Process.Start(processStartInfo); + if (p == null) + { + throw new ProcessStartFailedException("The process failed to start"); + } + var asyncResult = pipe.BeginWaitForConnection(null, null); if (asyncResult.AsyncWaitHandle.WaitOne(PIPE_TIMEOUT)) @@ -56,9 +61,13 @@ public static Process LaunchProcessAndSendDto(NauDto dto, ProcessStartInfo proce BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(pipe, dto); } + else if (p.HasExited) + { + throw new TimeoutException(string.Format("The NamedPipeServerStream timed out waiting for a named pipe connection, but the process has exited with exit code: {0}", p.ExitCode)); + } else { - throw new TimeoutException("The NamedPipeServerStream timed out waiting for a named pipe connection"); + throw new TimeoutException("The NamedPipeServerStream timed out waiting for a named pipe connection."); } } diff --git a/src/NAppUpdate.Framework/Utils/ProcessStartFailedException.cs b/src/NAppUpdate.Framework/Utils/ProcessStartFailedException.cs new file mode 100644 index 00000000..fa52f874 --- /dev/null +++ b/src/NAppUpdate.Framework/Utils/ProcessStartFailedException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NAppUpdate.Framework.Utils +{ + /// + /// Thrown if the Process.Start() call returns null, e.g. the updater process fails to start at all. + /// + internal class ProcessStartFailedException : Exception + { + public ProcessStartFailedException(string message) : base(message) { } + } + +} From 413b8ebc5454fc8dd000f1651516a6676326e49c Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Fri, 5 Feb 2016 15:25:50 +0100 Subject: [PATCH 04/22] #54 Refactor of the updater --- src/NAppUpdate.Updater/AppStart.cs | 335 +++++++++++++++-------------- 1 file changed, 174 insertions(+), 161 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 5edc9a2b..95a6f8f6 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -16,15 +16,34 @@ internal static class AppStart private static ArgumentsParser _args; private static Logger _logger; private static ConsoleForm _console; + private static string _tempFolder = string.Empty; + private static string _logFilePath = string.Empty; + private static string _workingDir = string.Empty; private static void Main() { - //Debugger.Launch(); - string tempFolder = string.Empty; - string logFile = string.Empty; - _args = ArgumentsParser.Get(); + Setup(); + + try + { + PerformUpdates(); + } + catch (Exception ex) + { + Log(ex); + } + finally + { + Teardown(); + } + } + private static void Setup() + { + _workingDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); _logger = UpdateManager.Instance.Logger; + _args = ArgumentsParser.Get(); + _args.ParseCommandLineArgs(); if (_args.ShowConsole) { @@ -34,212 +53,206 @@ private static void Main() Log("Starting to process cold updates..."); - var workingDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); if (_args.Log) { // Setup a temporary location for the log file, until we can get the DTO - logFile = Path.Combine(workingDir, @"NauUpdate.log"); + _logFilePath = Path.Combine(_workingDir, @"NauUpdate.log"); } + } - try - { - // Get the update process name, to be used to create a named pipe and to wait on the application - // to quit - string syncProcessName = _args.ProcessName; - if (string.IsNullOrEmpty(syncProcessName)) //Application.Exit(); - throw new ArgumentException("The command line needs to specify the mutex of the program to update.", "ar" + "gs"); + private static void PerformUpdates() + { + // Get the update process name, to be used to create a named pipe and to wait on the application + // to quit + string syncProcessName = _args.ProcessName; + if (string.IsNullOrEmpty(syncProcessName)) //Application.Exit(); + throw new ArgumentException("The command line needs to specify the mutex of the program to update.", "ar" + "gs"); - Log("Update process name: '{0}'", syncProcessName); + Log("Update process name: '{0}'", syncProcessName); - // Load extra assemblies to the app domain, if present - var availableAssemblies = FileSystem.GetFiles(workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly); - foreach (var assemblyPath in availableAssemblies) - { - Log("Loading {0}", assemblyPath); + // Load extra assemblies to the app domain, if present + var availableAssemblies = FileSystem.GetFiles(_workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly); + foreach (var assemblyPath in availableAssemblies) + { + Log("Loading {0}", assemblyPath); - if (assemblyPath.Equals(Assembly.GetEntryAssembly().Location, StringComparison.InvariantCultureIgnoreCase) || assemblyPath.EndsWith("NAppUpdate.Framework.dll")) - { - Log("\tSkipping (part of current execution)"); - continue; - } + if (assemblyPath.Equals(Assembly.GetEntryAssembly().Location, StringComparison.InvariantCultureIgnoreCase) || assemblyPath.EndsWith("NAppUpdate.Framework.dll")) + { + Log("\tSkipping (part of current execution)"); + continue; + } - try - { - // ReSharper disable UnusedVariable - var assembly = Assembly.LoadFile(assemblyPath); - // ReSharper restore UnusedVariable - } - catch (BadImageFormatException ex) - { - Log("\tSkipping due to an error: {0}", ex.Message); - } + try + { + // ReSharper disable UnusedVariable + var assembly = Assembly.LoadFile(assemblyPath); + // ReSharper restore UnusedVariable + } + catch (BadImageFormatException ex) + { + Log("\tSkipping due to an error: {0}", ex.Message); } + } - // Connect to the named pipe and retrieve the updates list - var dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto; + // Connect to the named pipe and retrieve the updates list + var dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto; - // Make sure we start updating only once the application has completely terminated - Thread.Sleep(1000); // Let's even wait a bit - bool createdNew; - using (var mutex = new Mutex(false, syncProcessName + "Mutex", out createdNew)) + // Make sure we start updating only once the application has completely terminated + Thread.Sleep(1000); // Let's even wait a bit + bool createdNew; + using (var mutex = new Mutex(false, syncProcessName + "Mutex", out createdNew)) + { + try { - try - { - if (!createdNew) mutex.WaitOne(); - } - catch (AbandonedMutexException) - { - // An abandoned mutex is exactly what we are expecting... - } - finally - { - Log("The application has terminated (as expected)"); - } + if (!createdNew) mutex.WaitOne(); } + catch (AbandonedMutexException) + { + // An abandoned mutex is exactly what we are expecting... + } + finally + { + Log("The application has terminated (as expected)"); + } + } - bool updateSuccessful = true; + bool updateSuccessful = true; - if (dto == null || dto.Configs == null) throw new Exception("Invalid DTO received"); + if (dto == null || dto.Configs == null) throw new Exception("Invalid DTO received"); - if (dto.LogItems != null) // shouldn't really happen - _logger.LogItems.InsertRange(0, dto.LogItems); - dto.LogItems = _logger.LogItems; + if (dto.LogItems != null) // shouldn't really happen + _logger.LogItems.InsertRange(0, dto.LogItems); + dto.LogItems = _logger.LogItems; - // Get some required environment variables - string appPath = dto.AppPath; - string appDir = dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty; - tempFolder = dto.Configs.TempFolder; - string backupFolder = dto.Configs.BackupFolder; - bool relaunchApp = dto.RelaunchApplication; + // Get some required environment variables + string appPath = dto.AppPath; + string appDir = dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty; + _tempFolder = dto.Configs.TempFolder; + string backupFolder = dto.Configs.BackupFolder; + bool relaunchApp = dto.RelaunchApplication; - if (!string.IsNullOrEmpty(dto.AppPath)) logFile = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location + if (!string.IsNullOrEmpty(dto.AppPath)) _logFilePath = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location - if (dto.Tasks == null || dto.Tasks.Count == 0) throw new Exception("Could not find the updates list (or it was empty)."); + if (dto.Tasks == null || dto.Tasks.Count == 0) throw new Exception("Could not find the updates list (or it was empty)."); - Log("Got {0} task objects", dto.Tasks.Count); + Log("Got {0} task objects", dto.Tasks.Count); - //This can be handy if you're trying to debug the updater.exe! - //#if (DEBUG) + //This can be handy if you're trying to debug the updater.exe! + //#if (DEBUG) + { + if (_args.ShowConsole) { - if (_args.ShowConsole) - { - _console.WriteLine(); - _console.WriteLine("Pausing to attach debugger. Press any key to continue."); - _console.ReadKey(); - } - + _console.WriteLine(); + _console.WriteLine("Pausing to attach debugger. Press any key to continue."); + _console.ReadKey(); } - //#endif - // Perform the actual off-line update process - foreach (var t in dto.Tasks) - { - Log("Task \"{0}\": {1}", t.Description, t.ExecutionStatus); + } + //#endif - if (t.ExecutionStatus != TaskExecutionStatus.RequiresAppRestart && t.ExecutionStatus != TaskExecutionStatus.RequiresPrivilegedAppRestart) - { - Log("\tSkipping"); - continue; - } + // Perform the actual off-line update process + foreach (var t in dto.Tasks) + { + Log("Task \"{0}\": {1}", t.Description, t.ExecutionStatus); - Log("\tExecuting..."); + if (t.ExecutionStatus != TaskExecutionStatus.RequiresAppRestart && t.ExecutionStatus != TaskExecutionStatus.RequiresPrivilegedAppRestart) + { + Log("\tSkipping"); + continue; + } - // TODO: Better handling on failure: logging, rollbacks - try - { - t.ExecutionStatus = t.Execute(true); - } - catch (Exception ex) - { - Log(ex); - updateSuccessful = false; - t.ExecutionStatus = TaskExecutionStatus.Failed; - } + Log("\tExecuting..."); - if (t.ExecutionStatus == TaskExecutionStatus.Successful) continue; - Log("\tTask execution failed"); + // TODO: Better handling on failure: logging, rollbacks + try + { + t.ExecutionStatus = t.Execute(true); + } + catch (Exception ex) + { + Log(ex); updateSuccessful = false; - break; + t.ExecutionStatus = TaskExecutionStatus.Failed; } - if (updateSuccessful) + if (t.ExecutionStatus == TaskExecutionStatus.Successful) continue; + Log("\tTask execution failed"); + updateSuccessful = false; + break; + } + + if (updateSuccessful) + { + Log("Finished successfully"); + Log("Removing backup folder"); + if (Directory.Exists(backupFolder)) FileSystem.DeleteDirectory(backupFolder); + } + else + { + MessageBox.Show("Update Failed"); + Log(Logger.SeverityLevel.Error, "Update failed"); + } + + // Start the application only if requested to do so + if (relaunchApp) + { + Log("Re-launching process {0} with working dir {1}", appPath, appDir); + ProcessStartInfo info; + if (_args.ShowConsole) { - Log("Finished successfully"); - Log("Removing backup folder"); - if (Directory.Exists(backupFolder)) FileSystem.DeleteDirectory(backupFolder); + info = new ProcessStartInfo + { + UseShellExecute = false, + WorkingDirectory = appDir, + FileName = appPath, + }; } else { - MessageBox.Show("Update Failed"); - Log(Logger.SeverityLevel.Error, "Update failed"); + info = new ProcessStartInfo + { + UseShellExecute = true, + WorkingDirectory = appDir, + FileName = appPath, + }; } - // Start the application only if requested to do so - if (relaunchApp) + try { - Log("Re-launching process {0} with working dir {1}", appPath, appDir); - ProcessStartInfo info; - if (_args.ShowConsole) - { - info = new ProcessStartInfo - { - UseShellExecute = false, - WorkingDirectory = appDir, - FileName = appPath, - }; - } - else - { - info = new ProcessStartInfo - { - UseShellExecute = true, - WorkingDirectory = appDir, - FileName = appPath, - }; - } - - try - { - NauIpc.LaunchProcessAndSendDto(dto, info, syncProcessName); - } - catch (Exception ex) - { - throw new UpdateProcessFailedException("Unable to relaunch application and/or send DTO", ex); - } + NauIpc.LaunchProcessAndSendDto(dto, info, syncProcessName); + } + catch (Exception ex) + { + throw new UpdateProcessFailedException("Unable to relaunch application and/or send DTO", ex); } - - Log("All done"); - //Application.Exit(); } - catch (Exception ex) + + Log("All done"); + } + + private static void Teardown() + { + if (_args.Log) { - // supressing catch because if at any point we get an error the update has failed - Log(ex); + // at this stage we can't make any assumptions on correctness of the path + FileSystem.CreateDirectoryStructure(_logFilePath, true); + _logger.Dump(_logFilePath); } - finally + + if (_args.ShowConsole) { if (_args.Log) { - // at this stage we can't make any assumptions on correctness of the path - FileSystem.CreateDirectoryStructure(logFile, true); - _logger.Dump(logFile); - } - - if (_args.ShowConsole) - { - if (_args.Log) - { - _console.WriteLine(); - _console.WriteLine("Log file was saved to {0}", logFile); - _console.WriteLine(); - } _console.WriteLine(); - _console.WriteLine("Press any key or close this window to exit."); - _console.ReadKey(); + _console.WriteLine("Log file was saved to {0}", _logFilePath); + _console.WriteLine(); } - if (!string.IsNullOrEmpty(tempFolder)) SelfCleanUp(tempFolder); - Application.Exit(); + _console.WriteLine(); + _console.WriteLine("Press any key or close this window to exit."); + _console.ReadKey(); } + if (!string.IsNullOrEmpty(_tempFolder)) SelfCleanUp(_tempFolder); + Application.Exit(); } private static void SelfCleanUp(string tempFolder) From c5ba68e722bb6edd946c8ef2cbd3e92e2e318cf1 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 12:10:17 +0100 Subject: [PATCH 05/22] #54 Improved exception handling in Updater Exceptions will now be presented in a MessageBox even though neither logging or console is enabled, after they have been presented they will be rethrown. --- src/NAppUpdate.Updater/AppStart.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 95a6f8f6..40ed7caf 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -31,6 +31,13 @@ private static void Main() catch (Exception ex) { Log(ex); + + if (!_args.Log && !_args.ShowConsole) + { + MessageBox.Show(ex.ToString()); + } + + throw ex; } finally { @@ -62,11 +69,12 @@ private static void Setup() private static void PerformUpdates() { - // Get the update process name, to be used to create a named pipe and to wait on the application - // to quit string syncProcessName = _args.ProcessName; - if (string.IsNullOrEmpty(syncProcessName)) //Application.Exit(); - throw new ArgumentException("The command line needs to specify the mutex of the program to update.", "ar" + "gs"); + + if (string.IsNullOrEmpty(syncProcessName)) + { + throw new ArgumentException("Required command line argument is missing", "ProcessName"); + } Log("Update process name: '{0}'", syncProcessName); @@ -306,8 +314,6 @@ private static void Log(Exception ex) _console.WriteLine(); _console.WriteLine("The updater will close when you close this window."); } - - Application.DoEvents(); } } -} \ No newline at end of file +} From 1b7e41f06027241452487749f34bf024a2df45a3 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 12:27:14 +0100 Subject: [PATCH 06/22] #54 Code cleanup in the updater --- src/NAppUpdate.Updater/AppStart.cs | 69 ++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 40ed7caf..dff19b5a 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -78,6 +78,9 @@ private static void PerformUpdates() Log("Update process name: '{0}'", syncProcessName); + + // QUESTION(robin): WHAT DOES THIS PART DO? IT LOADS ALL ASSEMBLIES IN THE CURRENT WD? + // Load extra assemblies to the app domain, if present var availableAssemblies = FileSystem.GetFiles(_workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly); foreach (var assemblyPath in availableAssemblies) @@ -112,7 +115,10 @@ private static void PerformUpdates() { try { - if (!createdNew) mutex.WaitOne(); + if (!createdNew) + { + mutex.WaitOne(); + } } catch (AbandonedMutexException) { @@ -126,10 +132,18 @@ private static void PerformUpdates() bool updateSuccessful = true; - if (dto == null || dto.Configs == null) throw new Exception("Invalid DTO received"); + if (dto == null || dto.Configs == null) + { + throw new Exception("Received an invalid dto from the pipe"); + } - if (dto.LogItems != null) // shouldn't really happen + // shouldn't really happen + // QUESTION(robin): Why is it being checked then? + if (dto.LogItems != null) + { _logger.LogItems.InsertRange(0, dto.LogItems); + } + dto.LogItems = _logger.LogItems; // Get some required environment variables @@ -139,24 +153,21 @@ private static void PerformUpdates() string backupFolder = dto.Configs.BackupFolder; bool relaunchApp = dto.RelaunchApplication; - if (!string.IsNullOrEmpty(dto.AppPath)) _logFilePath = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location - - if (dto.Tasks == null || dto.Tasks.Count == 0) throw new Exception("Could not find the updates list (or it was empty)."); - - Log("Got {0} task objects", dto.Tasks.Count); - - //This can be handy if you're trying to debug the updater.exe! - //#if (DEBUG) + if (!string.IsNullOrEmpty(dto.AppPath)) { - if (_args.ShowConsole) - { - _console.WriteLine(); - _console.WriteLine("Pausing to attach debugger. Press any key to continue."); - _console.ReadKey(); - } + _logFilePath = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location + } + if (dto.Tasks == null) + { + throw new Exception("The Task list received in the dto is null"); + } + else if (dto.Tasks.Count == 0) + { + throw new Exception("The Task list received in the dto is empty"); } - //#endif + + Log("Got {0} task objects", dto.Tasks.Count); // Perform the actual off-line update process foreach (var t in dto.Tasks) @@ -183,7 +194,11 @@ private static void PerformUpdates() t.ExecutionStatus = TaskExecutionStatus.Failed; } - if (t.ExecutionStatus == TaskExecutionStatus.Successful) continue; + if (t.ExecutionStatus == TaskExecutionStatus.Successful) + { + continue; + } + Log("\tTask execution failed"); updateSuccessful = false; break; @@ -193,7 +208,12 @@ private static void PerformUpdates() { Log("Finished successfully"); Log("Removing backup folder"); - if (Directory.Exists(backupFolder)) FileSystem.DeleteDirectory(backupFolder); + + // QUESTION(robin): What is the difference between this clean up and the teardown? + if (Directory.Exists(backupFolder)) + { + FileSystem.DeleteDirectory(backupFolder); + } } else { @@ -234,8 +254,6 @@ private static void PerformUpdates() throw new UpdateProcessFailedException("Unable to relaunch application and/or send DTO", ex); } } - - Log("All done"); } private static void Teardown() @@ -259,7 +277,12 @@ private static void Teardown() _console.WriteLine("Press any key or close this window to exit."); _console.ReadKey(); } - if (!string.IsNullOrEmpty(_tempFolder)) SelfCleanUp(_tempFolder); + + if (!string.IsNullOrEmpty(_tempFolder)) + { + SelfCleanUp(_tempFolder); + } + Application.Exit(); } From caa69aa0a4ad8832a00f151dc4837fe22d30b4ad Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 12:38:18 +0100 Subject: [PATCH 07/22] #54 Additional clean up of the updater Cleaned up error handling and execution flow, also removed some redundant logic. --- src/NAppUpdate.Updater/AppStart.cs | 38 ++++++++++-------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index dff19b5a..553f911b 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -130,8 +130,6 @@ private static void PerformUpdates() } } - bool updateSuccessful = true; - if (dto == null || dto.Configs == null) { throw new Exception("Received an invalid dto from the pipe"); @@ -151,7 +149,6 @@ private static void PerformUpdates() string appDir = dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty; _tempFolder = dto.Configs.TempFolder; string backupFolder = dto.Configs.BackupFolder; - bool relaunchApp = dto.RelaunchApplication; if (!string.IsNullOrEmpty(dto.AppPath)) { @@ -180,18 +177,16 @@ private static void PerformUpdates() continue; } - Log("\tExecuting..."); - - // TODO: Better handling on failure: logging, rollbacks try { + Log("\tExecuting..."); t.ExecutionStatus = t.Execute(true); } catch (Exception ex) { - Log(ex); - updateSuccessful = false; t.ExecutionStatus = TaskExecutionStatus.Failed; + string exceptionMessage = string.Format("Update failed, task execution threw an exception, description: {0}, execution status: {1}", t.Description, t.ExecutionStatus); + throw new Exception(exceptionMessage, ex); } if (t.ExecutionStatus == TaskExecutionStatus.Successful) @@ -199,30 +194,23 @@ private static void PerformUpdates() continue; } - Log("\tTask execution failed"); - updateSuccessful = false; - break; + string taskFailedMessage = string.Format("Update failed, task execution failed, description: {0}, execution status: {1}", t.Description, t.ExecutionStatus); + + Log(Logger.SeverityLevel.Error, taskFailedMessage); + throw new Exception(taskFailedMessage); } - if (updateSuccessful) - { - Log("Finished successfully"); - Log("Removing backup folder"); + Log("Finished successfully"); + Log("Removing backup folder"); - // QUESTION(robin): What is the difference between this clean up and the teardown? - if (Directory.Exists(backupFolder)) - { - FileSystem.DeleteDirectory(backupFolder); - } - } - else + // QUESTION(robin): What is the difference between this clean up and the teardown? + if (Directory.Exists(backupFolder)) { - MessageBox.Show("Update Failed"); - Log(Logger.SeverityLevel.Error, "Update failed"); + FileSystem.DeleteDirectory(backupFolder); } // Start the application only if requested to do so - if (relaunchApp) + if (dto.RelaunchApplication) { Log("Re-launching process {0} with working dir {1}", appPath, appDir); ProcessStartInfo info; From 9826ca086433f7a2034fdd91e9e2b578ba168a64 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 12:39:06 +0100 Subject: [PATCH 08/22] #54 Commit of a csproj file that i forgot --- src/NAppUpdate.Framework/NAppUpdate.Framework.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj b/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj index fa6afa94..555394ac 100644 --- a/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj +++ b/src/NAppUpdate.Framework/NAppUpdate.Framework.csproj @@ -102,6 +102,7 @@ + From 92da31e405f3ea54b7e08b8bd20563a1fb0a27b3 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 12:50:57 +0100 Subject: [PATCH 09/22] #54 Refactored the process start options in updater.exe --- src/NAppUpdate.Updater/AppStart.cs | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 553f911b..29d5039e 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -213,25 +213,15 @@ private static void PerformUpdates() if (dto.RelaunchApplication) { Log("Re-launching process {0} with working dir {1}", appPath, appDir); - ProcessStartInfo info; - if (_args.ShowConsole) - { - info = new ProcessStartInfo - { - UseShellExecute = false, - WorkingDirectory = appDir, - FileName = appPath, - }; - } - else + + bool useShellExecute = !_args.ShowConsole; + + ProcessStartInfo info = new ProcessStartInfo { - info = new ProcessStartInfo - { - UseShellExecute = true, - WorkingDirectory = appDir, - FileName = appPath, - }; - } + UseShellExecute = useShellExecute, + WorkingDirectory = appDir, + FileName = appPath + }; try { From 491be066a33c9f687e60a8fa83e27a941e9b3131 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 13:01:59 +0100 Subject: [PATCH 10/22] #54 Some further clean up of the updater --- src/NAppUpdate.Updater/AppStart.cs | 49 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 29d5039e..7379baf5 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -16,7 +16,7 @@ internal static class AppStart private static ArgumentsParser _args; private static Logger _logger; private static ConsoleForm _console; - private static string _tempFolder = string.Empty; + private static NauIpc.NauDto _dto; private static string _logFilePath = string.Empty; private static string _workingDir = string.Empty; @@ -106,7 +106,7 @@ private static void PerformUpdates() } // Connect to the named pipe and retrieve the updates list - var dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto; + _dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto; // Make sure we start updating only once the application has completely terminated Thread.Sleep(1000); // Let's even wait a bit @@ -130,44 +130,42 @@ private static void PerformUpdates() } } - if (dto == null || dto.Configs == null) + if (_dto == null || _dto.Configs == null) { throw new Exception("Received an invalid dto from the pipe"); } // shouldn't really happen // QUESTION(robin): Why is it being checked then? - if (dto.LogItems != null) + if (_dto.LogItems != null) { - _logger.LogItems.InsertRange(0, dto.LogItems); + _logger.LogItems.InsertRange(0, _dto.LogItems); } - dto.LogItems = _logger.LogItems; + _dto.LogItems = _logger.LogItems; // Get some required environment variables - string appPath = dto.AppPath; - string appDir = dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty; - _tempFolder = dto.Configs.TempFolder; - string backupFolder = dto.Configs.BackupFolder; + string appPath = _dto.AppPath; + string appDir = _dto.WorkingDirectory ?? Path.GetDirectoryName(appPath) ?? string.Empty; - if (!string.IsNullOrEmpty(dto.AppPath)) + if (!string.IsNullOrEmpty(_dto.AppPath)) { - _logFilePath = Path.Combine(Path.GetDirectoryName(dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location + _logFilePath = Path.Combine(Path.GetDirectoryName(_dto.AppPath), @"NauUpdate.log"); // now we can log to a more accessible location } - if (dto.Tasks == null) + if (_dto.Tasks == null) { throw new Exception("The Task list received in the dto is null"); } - else if (dto.Tasks.Count == 0) + else if (_dto.Tasks.Count == 0) { throw new Exception("The Task list received in the dto is empty"); } - Log("Got {0} task objects", dto.Tasks.Count); + Log("Got {0} task objects", _dto.Tasks.Count); // Perform the actual off-line update process - foreach (var t in dto.Tasks) + foreach (var t in _dto.Tasks) { Log("Task \"{0}\": {1}", t.Description, t.ExecutionStatus); @@ -203,14 +201,13 @@ private static void PerformUpdates() Log("Finished successfully"); Log("Removing backup folder"); - // QUESTION(robin): What is the difference between this clean up and the teardown? - if (Directory.Exists(backupFolder)) + if (Directory.Exists(_dto.Configs.BackupFolder)) { - FileSystem.DeleteDirectory(backupFolder); + FileSystem.DeleteDirectory(_dto.Configs.BackupFolder); } // Start the application only if requested to do so - if (dto.RelaunchApplication) + if (_dto.RelaunchApplication) { Log("Re-launching process {0} with working dir {1}", appPath, appDir); @@ -225,7 +222,7 @@ private static void PerformUpdates() try { - NauIpc.LaunchProcessAndSendDto(dto, info, syncProcessName); + NauIpc.LaunchProcessAndSendDto(_dto, info, syncProcessName); } catch (Exception ex) { @@ -256,9 +253,9 @@ private static void Teardown() _console.ReadKey(); } - if (!string.IsNullOrEmpty(_tempFolder)) + if (_dto != null && !string.IsNullOrEmpty(_dto.Configs.TempFolder)) { - SelfCleanUp(_tempFolder); + SelfCleanUp(_dto.Configs.TempFolder); } Application.Exit(); @@ -296,9 +293,11 @@ private static void Log(Logger.SeverityLevel severity, string message, params ob message = string.Format(message, args); _logger.Log(severity, message); - if (_args.ShowConsole) _console.WriteLine(message); - Application.DoEvents(); + if (_args.ShowConsole) + { + _console.WriteLine(message); + } } private static void Log(Exception ex) From 8b752d0aaab75c4776b2a9bcdff3e1d354a4c3c7 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 13:20:05 +0100 Subject: [PATCH 11/22] #54 Removed my personal working notes --- src/NAppUpdate.Updater/AppStart.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 7379baf5..256b4284 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -78,9 +78,6 @@ private static void PerformUpdates() Log("Update process name: '{0}'", syncProcessName); - - // QUESTION(robin): WHAT DOES THIS PART DO? IT LOADS ALL ASSEMBLIES IN THE CURRENT WD? - // Load extra assemblies to the app domain, if present var availableAssemblies = FileSystem.GetFiles(_workingDir, "*.exe|*.dll", SearchOption.TopDirectoryOnly); foreach (var assemblyPath in availableAssemblies) @@ -135,13 +132,7 @@ private static void PerformUpdates() throw new Exception("Received an invalid dto from the pipe"); } - // shouldn't really happen - // QUESTION(robin): Why is it being checked then? - if (_dto.LogItems != null) - { - _logger.LogItems.InsertRange(0, _dto.LogItems); - } - + _logger.LogItems.InsertRange(0, _dto.LogItems); _dto.LogItems = _logger.LogItems; // Get some required environment variables From b8a89a57b70aa7d6ff06e002b8bad37e68b0f247 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 13:21:08 +0100 Subject: [PATCH 12/22] #54 Bumped up the updater.exe to .NET 3.5 --- src/NAppUpdate.Updater/NAppUpdate.Updater.csproj | 3 ++- src/NAppUpdate.Updater/Properties/Settings.Designer.cs | 4 ++-- src/NAppUpdate.Updater/app.config | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/NAppUpdate.Updater/NAppUpdate.Updater.csproj b/src/NAppUpdate.Updater/NAppUpdate.Updater.csproj index fba3a29d..bfdae1e2 100644 --- a/src/NAppUpdate.Updater/NAppUpdate.Updater.csproj +++ b/src/NAppUpdate.Updater/NAppUpdate.Updater.csproj @@ -10,7 +10,7 @@ Properties NAppUpdate.Updater NAppUpdate.Updater - v2.0 + v3.5 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 @@ -36,6 +36,7 @@ false true OnBuildSuccess + true diff --git a/src/NAppUpdate.Updater/Properties/Settings.Designer.cs b/src/NAppUpdate.Updater/Properties/Settings.Designer.cs index c10ec226..d8b18369 100644 --- a/src/NAppUpdate.Updater/Properties/Settings.Designer.cs +++ b/src/NAppUpdate.Updater/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.225 +// Runtime Version:4.0.30319.34014 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace NAppUpdate.Updater.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/src/NAppUpdate.Updater/app.config b/src/NAppUpdate.Updater/app.config index 0df7832f..ea93c858 100644 --- a/src/NAppUpdate.Updater/app.config +++ b/src/NAppUpdate.Updater/app.config @@ -1,3 +1,3 @@ - + From ed18e92f443d811394bac758e90b69ca79d44369 Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 15:32:14 +0100 Subject: [PATCH 13/22] #54 Minor code cleanup --- src/NAppUpdate.Framework/Utils/NauIpc.cs | 2 +- src/NAppUpdate.Updater/AppStart.cs | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/NAppUpdate.Framework/Utils/NauIpc.cs b/src/NAppUpdate.Framework/Utils/NauIpc.cs index 00abaa12..ba6c3a45 100644 --- a/src/NAppUpdate.Framework/Utils/NauIpc.cs +++ b/src/NAppUpdate.Framework/Utils/NauIpc.cs @@ -91,7 +91,7 @@ public static NauDto ReadDto(string syncProcessName) dto = formatter.Deserialize(pipe) as NauDto; } - if (dto == null) + if (dto == null || dto.Configs == null) { throw new Exception("Failed to read the dto from the pipe stream"); } diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 256b4284..869e70de 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -22,10 +22,9 @@ internal static class AppStart private static void Main() { - Setup(); - try { + Setup(); PerformUpdates(); } catch (Exception ex) @@ -37,7 +36,7 @@ private static void Main() MessageBox.Show(ex.ToString()); } - throw ex; + throw; } finally { @@ -103,7 +102,7 @@ private static void PerformUpdates() } // Connect to the named pipe and retrieve the updates list - _dto = NauIpc.ReadDto(syncProcessName) as NauIpc.NauDto; + _dto = NauIpc.ReadDto(syncProcessName); // Make sure we start updating only once the application has completely terminated Thread.Sleep(1000); // Let's even wait a bit @@ -127,11 +126,6 @@ private static void PerformUpdates() } } - if (_dto == null || _dto.Configs == null) - { - throw new Exception("Received an invalid dto from the pipe"); - } - _logger.LogItems.InsertRange(0, _dto.LogItems); _dto.LogItems = _logger.LogItems; From 504e5c37a9366187c1c6f7fedd7b5c6a48c898bb Mon Sep 17 00:00:00 2001 From: Robin Andersson Date: Wed, 10 Feb 2016 15:34:08 +0100 Subject: [PATCH 14/22] #54 Changed the AfterRestart check Changed it from being dependant on a command line argument passed in by the updater instead of being dependant on an invalid or timeout of pipe connection. The latter did not work well with the recent changes in the pipe management either. --- src/NAppUpdate.Framework/UpdateManager.cs | 27 +++++++++++++++++-- src/NAppUpdate.Framework/Updater/updater.exe | Bin 1 -> 81920 bytes 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/NAppUpdate.Framework/UpdateManager.cs b/src/NAppUpdate.Framework/UpdateManager.cs index 1fc1346d..91a7e39e 100644 --- a/src/NAppUpdate.Framework/UpdateManager.cs +++ b/src/NAppUpdate.Framework/UpdateManager.cs @@ -506,10 +506,20 @@ public void ApplyUpdates(bool relaunchApplication, bool updaterDoLogging, bool u public void ReinstateIfRestarted() { + if (!IsAfterRestart()) + { + return; + } + lock (UpdatesToApply) { - var dto = NauIpc.ReadDto(Config.UpdateProcessName) as NauIpc.NauDto; - if (dto == null) return; + NauIpc.NauDto dto = NauIpc.ReadDto(Config.UpdateProcessName); + + if (dto == null) + { + return; + } + Config = dto.Configs; UpdatesToApply = dto.Tasks; Logger = new Logger(dto.LogItems); @@ -581,5 +591,18 @@ public void CleanUp() ShouldStop = false; } } + + private bool IsAfterRestart() + { + foreach (string arg in Environment.GetCommandLineArgs()) + { + if (arg == "-nappupdate-afterrestart") + { + return true; + } + } + + return false; + } } } \ No newline at end of file diff --git a/src/NAppUpdate.Framework/Updater/updater.exe b/src/NAppUpdate.Framework/Updater/updater.exe index 0519ecba6ea913e21689ec692e81e9e4973fbf73..d1e2938adb4bd87b9667c0a8cf77099d60f1afa9 100644 GIT binary patch literal 81920 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 QyPs5#mxq7<>ooBH0WFG`!T Date: Thu, 11 Feb 2016 12:28:34 +0100 Subject: [PATCH 15/22] #54 Pass the -nappupdate-afterrestart argument when relaunching application --- src/NAppUpdate.Framework/Updater/updater.exe | Bin 81920 -> 82432 bytes src/NAppUpdate.Updater/AppStart.cs | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NAppUpdate.Framework/Updater/updater.exe b/src/NAppUpdate.Framework/Updater/updater.exe index d1e2938adb4bd87b9667c0a8cf77099d60f1afa9..63bc347c2104b48174f1ba729256e286d2e2ec92 100644 GIT binary patch delta 6211 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 diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 869e70de..7e861a5f 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -202,7 +202,8 @@ private static void PerformUpdates() { UseShellExecute = useShellExecute, WorkingDirectory = appDir, - FileName = appPath + FileName = appPath, + Arguments = "-nappupdate-afterrestart" }; try From 398456e99f7e532b84de64b1a605fb9cc94e0be4 Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 12:41:09 +0100 Subject: [PATCH 16/22] #54 Increased target framework version of FeedBuilder to .NET 3.5 --- FeedBuilder/FeedBuilder.csproj | 5 +++-- FeedBuilder/Properties/Resources.Designer.cs | 2 +- FeedBuilder/Properties/Settings.Designer.cs | 8 ++++++-- FeedBuilder/app.config | 3 +++ 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 FeedBuilder/app.config diff --git a/FeedBuilder/FeedBuilder.csproj b/FeedBuilder/FeedBuilder.csproj index aa6b2fd3..8fcafd30 100644 --- a/FeedBuilder/FeedBuilder.csproj +++ b/FeedBuilder/FeedBuilder.csproj @@ -12,7 +12,7 @@ FeedBuilder 512 WindowsForms - v2.0 + v3.5 1591 @@ -136,6 +136,7 @@ + SettingsSingleFileGenerator Settings.Designer.cs @@ -153,4 +154,4 @@ - + \ No newline at end of file diff --git a/FeedBuilder/Properties/Resources.Designer.cs b/FeedBuilder/Properties/Resources.Designer.cs index 8408bebd..12793502 100644 --- a/FeedBuilder/Properties/Resources.Designer.cs +++ b/FeedBuilder/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.269 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/FeedBuilder/Properties/Settings.Designer.cs b/FeedBuilder/Properties/Settings.Designer.cs index f7457cc0..263c0a84 100644 --- a/FeedBuilder/Properties/Settings.Designer.cs +++ b/FeedBuilder/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.269 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace FeedBuilder { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -37,6 +37,7 @@ public string BaseURL { } [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Configuration.SettingsProviderAttribute(typeof(FeedBuilder.FeedBuilderSettingsProvider))] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] public bool CleanUp { @@ -142,6 +143,9 @@ public bool IgnoreDebugSymbols { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.SettingsProviderAttribute(typeof(FeedBuilder.FeedBuilderSettingsProvider))] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\r\n\r\n *.pdb\r\n *.config\r\n")] public global::System.Collections.Specialized.StringCollection IgnoreFiles { get { return ((global::System.Collections.Specialized.StringCollection)(this["IgnoreFiles"])); diff --git a/FeedBuilder/app.config b/FeedBuilder/app.config new file mode 100644 index 00000000..2fa6e95d --- /dev/null +++ b/FeedBuilder/app.config @@ -0,0 +1,3 @@ + + + From cd6048f1758f480aa6649078bf2f6dc1c8099981 Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 16:17:06 +0100 Subject: [PATCH 17/22] #54 The calls to Application.DoEvents are needed to make the console update right away --- src/NAppUpdate.Updater/AppStart.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 7e861a5f..f0a5db8a 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -283,6 +283,8 @@ private static void Log(Logger.SeverityLevel severity, string message, params ob if (_args.ShowConsole) { _console.WriteLine(message); + + Application.DoEvents(); } } @@ -299,6 +301,8 @@ private static void Log(Exception ex) _console.WriteLine(); _console.WriteLine("The updater will close when you close this window."); + + Application.DoEvents(); } } } From 3a82629308f6a28e989cf6950e2f69ecd525a26b Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 21:29:28 +0100 Subject: [PATCH 18/22] #54 Pass HResult of exception as exit code if unhandled exceptions occur in Updater --- src/NAppUpdate.Framework/Utils/NauIpc.cs | 5 ++++- src/NAppUpdate.Updater/AppStart.cs | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/NAppUpdate.Framework/Utils/NauIpc.cs b/src/NAppUpdate.Framework/Utils/NauIpc.cs index ba6c3a45..52b08c54 100644 --- a/src/NAppUpdate.Framework/Utils/NauIpc.cs +++ b/src/NAppUpdate.Framework/Utils/NauIpc.cs @@ -63,7 +63,10 @@ public static Process LaunchProcessAndSendDto(NauDto dto, ProcessStartInfo proce } else if (p.HasExited) { - throw new TimeoutException(string.Format("The NamedPipeServerStream timed out waiting for a named pipe connection, but the process has exited with exit code: {0}", p.ExitCode)); + Type exceptionType = Marshal.GetExceptionForHR(p.ExitCode).GetType(); + + throw new TimeoutException(string.Format("The NamedPipeServerStream timed out waiting for a named pipe connection, " + + "but the process has exited with exit code: {0} ({1})", p.ExitCode, exceptionType.FullName)); } else { diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index f0a5db8a..ade97381 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -8,6 +8,7 @@ using NAppUpdate.Framework.Common; using NAppUpdate.Framework.Tasks; using NAppUpdate.Framework.Utils; +using System.Runtime.InteropServices; namespace NAppUpdate.Updater { @@ -29,14 +30,14 @@ private static void Main() } catch (Exception ex) { + Environment.ExitCode = Marshal.GetHRForException(ex); + Log(ex); if (!_args.Log && !_args.ShowConsole) { MessageBox.Show(ex.ToString()); } - - throw; } finally { From aa5c26b08387bcc238d870b09289da48a2d3b46a Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 21:31:14 +0100 Subject: [PATCH 19/22] #54 Instead of showing error message, always log unhandled exceptions to event log --- src/NAppUpdate.Updater/AppStart.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index ade97381..ced9ab1c 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -34,10 +34,7 @@ private static void Main() Log(ex); - if (!_args.Log && !_args.ShowConsole) - { - MessageBox.Show(ex.ToString()); - } + EventLog.WriteEntry("NAppUpdate.Updater", ex.ToString(), EventLogEntryType.Error); } finally { From f25af3ccb5efaf618495a2fef57074746d802d6d Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 21:50:48 +0100 Subject: [PATCH 20/22] #54 Simplified error handling logic of failed update tasks --- src/NAppUpdate.Updater/AppStart.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index ced9ab1c..38cc0d28 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -158,6 +158,8 @@ private static void PerformUpdates() continue; } + Exception exception = null; + try { Log("\tExecuting..."); @@ -166,19 +168,14 @@ private static void PerformUpdates() catch (Exception ex) { t.ExecutionStatus = TaskExecutionStatus.Failed; - string exceptionMessage = string.Format("Update failed, task execution threw an exception, description: {0}, execution status: {1}", t.Description, t.ExecutionStatus); - throw new Exception(exceptionMessage, ex); + exception = ex; } - if (t.ExecutionStatus == TaskExecutionStatus.Successful) + if (t.ExecutionStatus != TaskExecutionStatus.Successful) { - continue; + string taskFailedMessage = string.Format("Update failed, task execution failed, description: {0}, execution status: {1}", t.Description, t.ExecutionStatus); + throw new Exception(taskFailedMessage, exception); } - - string taskFailedMessage = string.Format("Update failed, task execution failed, description: {0}, execution status: {1}", t.Description, t.ExecutionStatus); - - Log(Logger.SeverityLevel.Error, taskFailedMessage); - throw new Exception(taskFailedMessage); } Log("Finished successfully"); From 95f9599a05ffb74f7640a936e9035a6953d4f6ae Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 22:12:13 +0100 Subject: [PATCH 21/22] #54 Added the message box again but only show it if the application is not currently running --- src/NAppUpdate.Updater/AppStart.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index 38cc0d28..e9fdd976 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -20,6 +20,7 @@ internal static class AppStart private static NauIpc.NauDto _dto; private static string _logFilePath = string.Empty; private static string _workingDir = string.Empty; + private static bool _appRunning = true; private static void Main() { @@ -34,6 +35,11 @@ private static void Main() Log(ex); + if (!_appRunning && !_args.Log && !_args.ShowConsole) + { + MessageBox.Show(ex.ToString()); + } + EventLog.WriteEntry("NAppUpdate.Updater", ex.ToString(), EventLogEntryType.Error); } finally @@ -121,6 +127,7 @@ private static void PerformUpdates() finally { Log("The application has terminated (as expected)"); + _appRunning = false; } } @@ -204,6 +211,7 @@ private static void PerformUpdates() try { NauIpc.LaunchProcessAndSendDto(_dto, info, syncProcessName); + _appRunning = true; } catch (Exception ex) { From f8267daa4c4a2407cc64f262f45688072ffebce0 Mon Sep 17 00:00:00 2001 From: Frank Wennerdahl Date: Thu, 11 Feb 2016 22:20:13 +0100 Subject: [PATCH 22/22] #54 Added check for null in Teardown --- src/NAppUpdate.Framework/Updater/updater.exe | Bin 82432 -> 82432 bytes src/NAppUpdate.Updater/AppStart.cs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NAppUpdate.Framework/Updater/updater.exe b/src/NAppUpdate.Framework/Updater/updater.exe index 63bc347c2104b48174f1ba729256e286d2e2ec92..94c4caa219213287b6920dade96fbe8b98d35156 100644 GIT binary patch delta 6225 zcmaJ_dwdjCn*Hvr>Z~6r1%kyj6Th4Sw0RHF>Pn&@I3v=Yt08)>muPmJD4nfiWvR1n zoBCJxU`eI*^S1HrTbjrG;+>a1ot*sYh4FhIfAX^ZE1S=~`>WFXn-71xp{LKA^xwB# zvg?C8&Kxsmx#HGKlG@x2f8W!)=$o=} z|NK&KM}6YRgD-Z4o?o!#PahmBX?i>M(I+2Ih;3T_z@Crpzv=qZSB{*hP>0_=+u`V= zqW})gHZ{j4GMy+ua|~#`I+0pCF8diTPQ`?4IH1DAxK?Pc5DJ_Tz(aIMb5z8?TP4bt z0C0uFG#f=2TA&3BNE4bek<-n>z~$s4)hz%xg@7~yRHsO2#eyPD71cFNKB>LgRZ6)w z5)N;CcDoXv=AsV{S~AlCxT{7^R4IZkAj|F*2`&~)!!Sl6q$bbXh;Z*cKyoyMwcgiR zrj8m@%V}BYAHnK4*imM_<`r>_8k1~B)nqkpv4E}C8g{_NE<>A21(u?~D@W8x zeacUZWqcP+1kgmlZ=zSsuUjk1_KJ4g2{7H$0Fn;5Q>Hudgy`16ifMlQ>9EUd?Fw6y z%U52jRrH=A{JQTKv2ylnN1r5!mG(|uJKuXI9JL`Yg{~*hg1t5;r(nC2eCKOJPOnlK zn%d#^@Z1nY4H=Eu{!3?hjr3(F3%D40LZNsgH zV~3qz0CS3H|JJi*xqMkph|oq(D9KqR6QW)cHBOrG>MMuGg5FK1ind`J1ujaN?m2)% zc`?&AiluFvu^{$?U%6;PrMDBQpy)^nL9#*F3Flm7#zJ<;4ko9$Y#JY}M#2aRgZPZ3i^;w)=L z$M9I%UQ|1=(ZfbQ-(ko-m=seV(!C{ca_3?YIme^*0(UkDZq4H6eF48*2^}Zlob30( z@u?3K)oFnCD2Y9UtTY!MO9oJpPGZtpj!%F8>_|C;_`FdOHWN&B$GTv zN2?;yrx%_~=c-;0ICthE^`xLvb6gzkx$N~Pvi)9k$umZt`M#xz#Y*ehBcORFEQR@WhX}BDH+-&Ld$#bM5zUrtrNGZ zwMtEl_Y8ws6Y}r!IKwjGt&!V>7vMP*7f-H$woZ(MJ1a#k*=xsx=_ZHxNVj{)_BLXY z@E%V&4asXo*s39ybDiMW;kh#T`N=gxSv#b7_lOzp8R7+Ue14X1!xZ&Ru_mQrO>P2lZq6wqh0+Hy!MR09`OZe%s)~EDG1aO|lNIngn;>=L zJmP7o;xXzU{9o&6@3{u{Z5amBjr*5at+JMGue0k)%rw#RVxl8KXssLlaq5f<~|J>9dW=^96tPeO@=_8X?Dt7_504+MA zysDT~tlkChHL8etQr)d;^qR!aB@PP+#gW(`@p6gRN!%O|S8s!|#FU!}-J%7i=;9zSO0St8LjT+p^^MSO z!=zauF^mNvF@W76G2kyleaj$C4BO#+x-6Usn{<6xjQ9ayKK)H9c@bfkMG}ZfNbHc< z2gJDHHR*BSPWlL^uSrFgn2GbOK2%<7EwL;b$Zp(RIm&>KVSCJ?D*PwOBJ3NMM{Tr( zB4lS@*<6|GW);{V&0NIw@e1G$UIu(h+V4s{DwPgp1N1MHJAf7HR%F$w_t496>?#_d zG^>QZ2v31vH%Rtnj%}2z8~a`Ow)v4_N76uwcCuO|zV1_ch@Fmai$g=K9xvuX$4`8S zO(&gx>9fL}p-M97amj8p{*BE9ds?!L@g{2mdp_s;57taUdL`%kg0+IZlk=q%J`;2F zLCzWDO<@0(bC$suqAzm3YWPC*ZO(TFZ^kE*S}D4AqcNSgf<+|TP&l8@!~Bkr>`q$D z7l2j!k?<`Ao8mL-NFmgsnK`-^PK(0gt4vHO@1$Y0K(ZTU(LB0dvJ6dER$*EDd>^$a z^RPGX9%8Lv`-a#$iqUg9){e*#bX>9wtyfYvfoj#F!3_PA(oY3s<=7t(T0|2i`y(A! zn!sk~e408Bdud0F$h*^MP`86UC7IvRV%jCyfLtc*IxKOkDsT~y({gENR7!yeNy$mK zNXMrC7yV=(voi87Ag5nRTU0x>nUlRI+xMv}DJ9lw4b8{;WhC}V9FVwG;&zFdS0ug-e3(84K1N4@^XM4u(b^ef&udFrkPd3ti0o@^IV)lXy2~nAbxL1{ z+c|nO)v?R;PO4|u>FZfN+o1Qc287OoewW_Q671Ld?W~#2=UZ7D(~W!a={?%`EgMcX z#xw9Hj2D2jjaN~v4g0@=mKe>HVVjJZ)W?ovDJyA*afl7De>IjO@;RfQZAG=zhip50 zA3>$`webn7lUPD(ka!*C1;fBmL3EBPfeAJ>SOz;8EaVMx#N%jVu!h&juAC{ma)8~9 z$OG)D;B@@RsjA@cti_#aEGwCS$ zH4o^TO~CbLD{za%2Y{8d-@J-5KA*SqHWm*(%1fy})Jlih)KD`O@iv?bqOqs>QMNMl zEHD#lqGRm#(2KGYhY|k}JHm_jHbkZbISR*f#@D2=CeOu7rLK?298w;N_84T9Z?*imZi_j68-`($F&;`#+=f@%th-0iTWZ zqv5w?(6PuJ%2uvfcSA9)CfY7LYSTn(hq5bYH(9$-t`*+BP`0vt{8yH#7RgRep!IZ2 zoq(e!1{_IKfj#s&u$tBa&!ArblPUTLw=(Jmo=v-f=S#bVUWYwj+CQUr)MYq&_uzH+ z2QF}_`mpjxsgyFIOq19kagoHu64Meh67Q3^UEXLnK|Wj=t(4)IV)&%O=L+zD_gy#G4vq&7xpzP;}dy( zwr5K+?kI^cLga-nFxrF9x;%X13Ht@ugW;{KS!m(n6)Wak+tr1Sy}|X{#s%tTE%uVj z7hg>mc6862bAjtMu3p-)qPx@WqQgMKHpuIec)cFym1xO zCihA@VKjDiduuLPv!a9MdY#=Jk`Dg+y=zoshAU%|*%|kj4JPj&#s)V&P|8_(cJfo@ zgAe}WI%V*^Ju|et$o^};=(%KT)9xec_~JzLH`y~EsZhAav-2Kl%_jt8vCH)I|vkEk%ngJ+45?}HBuXO`=S+~js1}Mdc0%`K|AN@V zMhfD83QLKT;Rg#D>OP?c;$){k)#4b*ryMtN*GRFfk`fKoEet4*0UZsI5>0;>EsCa( zSyS~wOSWoR(M*6x(=WnEAC8trOD(WtFw)0HmB=Qnb4Hbje<#`#c7RZrW45G;rewPr z+Ysaa4!Nd|#*X19t2{a*H41+?u#AV#TbQ!nFsF#g$&^|m1@{2@SW@VdN=&+)sr8HH zZgnES3i|1wU}qY9Eg^=SnI)6b+jC7dOPXoTzW(S!wLO|?ANq(OV*SM2`)=Bj$T delta 6186 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 Q`Dz_MIlS}y#xL3b1N;h4n*aa+ diff --git a/src/NAppUpdate.Updater/AppStart.cs b/src/NAppUpdate.Updater/AppStart.cs index e9fdd976..825206db 100644 --- a/src/NAppUpdate.Updater/AppStart.cs +++ b/src/NAppUpdate.Updater/AppStart.cs @@ -242,7 +242,7 @@ private static void Teardown() _console.ReadKey(); } - if (_dto != null && !string.IsNullOrEmpty(_dto.Configs.TempFolder)) + if (_dto != null && _dto.Configs != null & !string.IsNullOrEmpty(_dto.Configs.TempFolder)) { SelfCleanUp(_dto.Configs.TempFolder); }