private void OnFileUpdateCompleted(PatchFile File) { FileUpdateCompleted?.Invoke(File, EventArgs.Empty); // Count file updated m_PatchFilesCount += 1; OnUpdateProgressChanged(m_PatchFilesCount, m_PatchFilesMax); }
/// <summary> /// Check cache from update and synchronize files from patches /// </summary> private void LoadCache() { var patchCachePath = Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + ".patch.cache"; if (File.Exists(patchCachePath)) { Version cacheVersion = null; Dictionary <string, PatchFile> cachePatch = null; // Read patch file structure using (var reader = new BinaryReader(new FileStream(patchCachePath, FileMode.Open))) { cacheVersion = new Version(reader.ReadString()); // for each patch file var filesCount = reader.ReadInt32(); cachePatch = new Dictionary <string, PatchFile>(filesCount); for (int j = 0; j < filesCount; j++) { var file = new PatchFile(reader.ReadString(), reader.ReadString(), reader.ReadString()); cachePatch.Add(file.FullPath, file); } } // Merge current patches with cache patch to avoid download all the patch again foreach (var version in Patches.Keys) { if (version == cacheVersion) { var patch = Patches[version]; // Create a keys copy to remove from dictionary without issues var paths = new List <string>(patch.Keys); // If file doesn't exist on cache patch, then is already updated foreach (var path in paths) { // Avoid update it again if (!cachePatch.ContainsKey(path)) { patch.Remove(path); PatchFiles.Remove(path); } } // Indicate update has been paused IsUpdatePaused = true; } } } }
private void OnFileDownloadReady(PatchFile File) { FileDownloadReady?.Invoke(File, EventArgs.Empty); }
/// <summary> /// Start/continue updating the application /// </summary> private async Task StartUpdating() { IsUpdatePaused = false; // Check there is files to update if (PatchFiles == null || PatchFiles.Count == 0) { return; } // Path to check if the executable needs to be updated var exePath = Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); // Start Downloading files from old patches to new ones var versions = new List <Version>(Patches.Keys); versions.Sort((a, b) => { return(a.CompareTo(b)); }); foreach (var version in versions) { var patch = Patches[version]; // Create a different instance of keys to remove it without issues var paths = new List <string>(patch.Keys); // Download and update each file from this version for (int i = 0; i < paths.Count; i++) { // track updating file var f = m_CurrentFileUpdating = patch[paths[i]]; // Check if the file is going to replace this executable bool isExecutable = f.FullPath == exePath; if (isExecutable) { // Check if is not last file to update if (i < (paths.Count - 1)) { // Add it again as last one paths.Add(f.FullPath); continue; } } // call event OnFileDownloadReady(f); // track event f.DownloadProgressChanged += OnFileDownloadProgressChanged; // download it await f.StartDownload(); // untrack event f.DownloadProgressChanged -= OnFileDownloadProgressChanged; // Check if download has been paused if (f.IsPaused) { break; } // call event var completedEventArgs = OnFileDownloadCompleted(f); // Check if the updating process is going to be handled by manager if (completedEventArgs.CancelUpdate) { await completedEventArgs.ExecuteAction(); } else { // Check if is executable if (isExecutable) { // update it slightly different (delete super old, move old and forget then move new) var thrashPath = Path.Combine(DownloadingPath, exePath + ".old"); await Task.Run(() => { if (File.Exists(thrashPath)) { File.Delete(thrashPath); } File.Move(exePath, thrashPath); File.Move(f.DownloadPath, exePath); }); } else { // update it await f.Update(); } } // Check if is the executable being updated if (isExecutable) { // delete cache DeleteCache(); } else { // Create backup to continue the update on restart SaveCache(version, patch); } // remove it from tracking patch.Remove(f.FullPath); PatchFiles.Remove(f.FullPath); // call event OnFileUpdateCompleted(f); // Start calling a events to restart the application if (isExecutable) { // call event OnPatchCompleted(version); // Check if this is the last file from everything for update if (PatchFiles.Count == 0) { // call event OnUpdateCompleted(); } // Restart application OnApplicationRestart(); // Execute new executable and exit from this one System.Diagnostics.Process.Start(exePath); Environment.Exit(0); } } // Stop tracking as updating m_CurrentFileUpdating = null; // Check if update has been paused if (IsUpdatePaused) { break; } // call event OnPatchCompleted(version); } // Patch finished if (PatchFiles.Count == 0) { // delete cache DeleteCache(); // call event OnUpdateCompleted(); } }
/// <summary> /// Initialize the pachting files to be downloaded and updated /// </summary> /// <param name="Patch">Patch to prepare</param> /// <param name="SupportOldVersion">Check if your application supports versions that are not found on patch</param> public async Task InitializeUpdate(PatchVersion Patch, bool SupportOldVersion = false) { // Set SSL/TLS is correctly being set ServicePointManager.Expect100Continue = true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // Create web client to check patch files using (WebClient web = new WebClient()) { // Patch to download var patchRequiredUrl = Patch.PatchUrl; // Avoid repeating files PatchFiles = new Dictionary <string, PatchFile>(); // Collect all files that will be used for patching Patches = new Dictionary <Version, Dictionary <string, PatchFile> >(); // Create downloading folder and hide it from user if (!Directory.Exists(DownloadingPath)) { DirectoryInfo dirInfo = Directory.CreateDirectory(DownloadingPath); dirInfo.Attributes = FileAttributes.Directory | FileAttributes.Hidden; } // Start reading web patches Version patchVersion = null; do { // Download, read and convert JSON to class var patchInfo = JsonDeserializer <PatchInfo>(await web.DownloadStringTaskAsync(patchRequiredUrl)); // If the version is the same as my current version, stop it patchVersion = new Version(patchInfo.Version); if (patchVersion == CurrentVersion) { break; } // Create version patch if doesn't exist if (!Patches.TryGetValue(patchVersion, out Dictionary <string, PatchFile> patch)) { patch = new Dictionary <string, PatchFile>(); Patches[patchVersion] = patch; } // Collects non repeated files foreach (string file in patchInfo.Files) { if (!PatchFiles.ContainsKey(file)) { // Add file to this patch var patchFile = new PatchFile(file, patchInfo.Host + file, Path.Combine(DownloadingPath, PatchFiles.Count.ToString().PadLeft(8, '0'))); patch.Add(file, patchFile); PatchFiles.Add(file, patchFile); } } // Remove it if this patch doesn't have files if (patch.Count == 0) { Patches.Remove(patchVersion); } // Collect files from patch dependencies patchRequiredUrl = patchInfo.PatchRequiredUrl; } while (!string.IsNullOrEmpty(patchRequiredUrl)); // Check if the update version is matching with app if (!SupportOldVersion && patchVersion > CurrentVersion) { throw new ApplicationException("The version of your application is too old to be updated."); } // Check if there is cache to synchronize LoadCache(); m_PatchFilesMax = PatchFiles.Count; m_PatchFilesCount = 0; } }