public int GetOutputStreams(string workingDirectory, string command, string param, out List <string> standardOut, out List <string> standardErr) { Process process = CreateProcess(workingDirectory, command, param); var localStandardOut = new List <string>(); var localStandardErr = new List <string>(); process.OutputDataReceived += (sender, e) => localStandardOut.Add(e.Data); process.ErrorDataReceived += (sender, e) => localStandardErr.Add(e.Data); try { process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); var waiter = new ProcessWaiter(process); waiter.WaitForExit(); standardOut = localStandardOut.Where(s => s != null).ToList(); standardErr = localStandardErr.Where(s => s != null).ToList(); return(waiter.ProcessExitCode); } finally { process.Dispose(); } }
public static void FixPermissions([MarshalAs(UnmanagedType.LPWStr)] string path) { ProcessWaiter.CheckForProcessesBlockingDir(Path.GetFullPath(path)).ConfigureAwait(false).GetAwaiter().GetResult(); var batContents = $@" title Fixing permissions... rem Get the localized version of Y/N to pass to takeown to make this work in different locales for /f ""tokens=1,2 delims=[,]"" %%a in ('""choice <nul 2>nul""') do set ""yes=%%a"" & set ""no=%%b"" echo Press %yes% for yes and %no% for no set target={ path.Trim(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar, ' ') } echo off cls echo Taking ownership of %target% ... rem First find is to filter out success messages, second findstr is to filter out empty lines takeown /F ""%target%"" /R /SKIPSL /D %yes% | find /V ""SUCCESS: The file (or folder):"" | findstr /r /v ""^$"" echo. echo Fixing access rights ... icacls ""%target%"" /grant *S-1-1-0:(OI)(CI)F /T /C /L /Q "; var batPath = Path.Combine(Path.GetTempPath(), "hfpatch_fixperms.bat"); File.WriteAllText(batPath, batContents); Process.Start(new ProcessStartInfo("cmd", $"/C \"{batPath}\"") { WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true }); }
internal static bool CheckForRunningProcesses(string[] filters, bool doNotKillSteam, Form parentForm = null) { var idsToCheck = GetRelatedProcessIds(filters, doNotKillSteam); if (idsToCheck.Length > 0) { if (!ProcessWaiter.ShowDialog(parentForm ?? MessageBoxes.DefaultOwner, idsToCheck.ToArray(), false)) { return(false); } } return(true); }
public static async Task <FileInfo> GetTempDownloadFilename() { var tempPath = Path.Combine(InstallDirectoryHelper.GameDirectory.FullName, "temp\\KKManager_downloads"); retryCreate: try { Directory.CreateDirectory(tempPath); retry: var fileName = Path.Combine(tempPath, Path.GetRandomFileName()); if (File.Exists(fileName)) { goto retry; } return(new FileInfo(fileName)); } catch (IOException ex) { if (await ProcessWaiter.CheckForProcessesBlockingKoiDir() != true) { throw new IOException($"Failed to create file in directory {tempPath} because of an IO issue - {ex.Message}", ex); } goto retryCreate; } catch (SecurityException ex) { if (MessageBox.Show($"Failed to create file in directory {tempPath} because of a security issue - {ex.Message}\n\nDo you want KK Manager to attempt to fix the issue? Click cancel if you want to abort.", "Could not apply update", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) == DialogResult.OK) { var fixPermissions = ProcessTools.FixPermissions(InstallDirectoryHelper.GameDirectory.FullName); if (fixPermissions == null) { throw new IOException($"Failed to create file in directory {tempPath} because of a security issue - {ex.Message}", ex); } fixPermissions.WaitForExit(); goto retryCreate; } throw; } }
/// <summary> /// Helper method to copy the last 4 KB of the specified file into a temp file and launch it using the external viewer /// </summary> /// <param name="basefile">The filename</param> private static void LaunchRecentFile(string basefile) { const int tailSize = 4096; const int bufferSize = 100; if (!File.Exists(basefile)) { return; } try { string tempfile = Path.GetTempFileName(); FileStream outfile = File.Open(tempfile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); FileStream fs = File.Open(basefile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); byte[] buffer = new byte[bufferSize]; if (fs.Length > tailSize) { fs.Seek(-tailSize, SeekOrigin.End); } int count; do { count = fs.Read(buffer, 0, bufferSize); if (count > 0) { outfile.Write(buffer, 0, count); } } while (count > 0); outfile.Close(); fs.Close(); string xfile = tempfile + ".txt"; File.Move(tempfile, xfile); int pid = LaunchFile(xfile); ProcessWaiter pw = new ProcessWaiter(pid, xfile); Thread t = new Thread(new ThreadStart(pw.Execute)); t.IsBackground = true; t.Start(); } catch (IOException) {} }
public int ExecuteCommandBlocking(string command, string parameters, string workingDir, string pathExtension, Action <string> reportOutputLine) { if (reportOutputLine != null) { throw new ArgumentException(nameof(reportOutputLine)); } if (_processId.HasValue) { throw new InvalidOperationException(); } IDictionary <string, string> envVariables = new Dictionary <string, string>(); if (!string.IsNullOrEmpty(pathExtension)) { envVariables["PATH"] = Utils.GetExtendedPath(pathExtension); } _logger.DebugInfo($"Attaching debugger to '{command}' via {DebuggerKind.VsTestFramework} engine"); if (_printTestOutput) { _logger.DebugInfo( $"Note that due to restrictions of the VsTest framework, the test executable's output can not be displayed in the test console when debugging tests. Use '{SettingsWrapper.OptionDebuggerKind}' option to overcome this problem.'"); } _processId = _frameworkHandle.LaunchProcessWithDebuggerAttached(command, workingDir, parameters, envVariables); ProcessWaiter waiter; using (var process = Process.GetProcessById(_processId.Value)) { waiter = new ProcessWaiter(process); waiter.WaitForExit(); } _logger.DebugInfo($"Executable {command} returned with exit code {waiter.ProcessExitCode}"); return(waiter.ProcessExitCode); }
private async void ModUpdateProgress_Shown(object sender, EventArgs e) { var averageDownloadSpeed = new MovingAverage(20); var downloadStartTime = DateTime.MinValue; try { #region Initialize UI progressBar1.Style = ProgressBarStyle.Marquee; progressBar1.Value = 0; progressBar1.Maximum = 1; labelPercent.Text = ""; checkBoxSleep.Enabled = false; var random = new Random(); if (random.Next(0, 10) >= 8) { var offset = random.Next(20, 80); var offsetStr = new string(Enumerable.Repeat(' ', offset).ToArray()); labelPercent.Text = offsetStr + " ( ) ( )\n"; labelPercent.Text += offsetStr + "( o . o)"; } olvColumnProgress.Renderer = new BarRenderer(0, 100); olvColumnProgress.AspectGetter = rowObject => (int)Math.Round(((UpdateDownloadItem)rowObject).FinishPercent); olvColumnSize.AspectGetter = rowObject => ((UpdateDownloadItem)rowObject).TotalSize; //olvColumnDownloaded.AspectGetter = rowObject => ((UpdateDownloadItem)rowObject).GetDownloadedSize(); olvColumnStatus.AspectGetter = rowObject => { var item = (UpdateDownloadItem)rowObject; return(item.Exceptions.Count == 0 ? item.Status.ToString() : item.Status + " - " + string.Join("; ", item.Exceptions.Select(x => x.Message))); }; olvColumnName.AspectGetter = rowObject => ((UpdateDownloadItem)rowObject).DownloadPath.Name; olvColumnNo.AspectGetter = rowObject => ((UpdateDownloadItem)rowObject).Order; fastObjectListView1.PrimarySortColumn = olvColumnStatus; fastObjectListView1.SecondarySortColumn = olvColumnNo; fastObjectListView1.Sorting = SortOrder.Ascending; fastObjectListView1.PrimarySortOrder = SortOrder.Ascending; fastObjectListView1.SecondarySortOrder = SortOrder.Ascending; fastObjectListView1.ShowSortIndicators = true; fastObjectListView1.Sort(); fastObjectListView1.ShowSortIndicator(); _overallSize = _completedSize = FileSize.Empty; #endregion SetStatus("Preparing..."); if (await ProcessWaiter.CheckForProcessesBlockingKoiDir() == false) { throw new OperationCanceledException(); } #region Find and select updates SetStatus("Searching for mod updates..."); labelPercent.Text = "Please wait, this might take a couple of minutes."; var updateTasks = await UpdateSourceManager.GetUpdates(_cancelToken.Token, _updaters, _autoInstallGuids); _cancelToken.Token.ThrowIfCancellationRequested(); progressBar1.Style = ProgressBarStyle.Blocks; if (updateTasks.All(x => x.UpToDate)) { SetStatus("Everything is up to date!"); progressBar1.Value = progressBar1.Maximum; _cancelToken.Cancel(); return; } var isAutoInstall = _autoInstallGuids != null && _autoInstallGuids.Length > 0; if (!isAutoInstall) { SetStatus($"Found {updateTasks.Count} updates, waiting for user confirmation."); updateTasks = ModUpdateSelectDialog.ShowWindow(this, updateTasks); } else { var skipped = updateTasks.RemoveAll(x => x.UpToDate); SetStatus($"Found {updateTasks.Count} update tasks in silent mode, {skipped} are already up-to-date.", true, true); } if (updateTasks == null) { throw new OperationCanceledException(); } #endregion SleepControls.PreventSleepOrShutdown(Handle, "Update is in progress"); #region Set up update downloader and start downloading downloadStartTime = DateTime.Now; var downloader = UpdateDownloadCoordinator.Create(updateTasks); var downloadItems = downloader.UpdateItems; SetStatus($"{downloadItems.Count(items => items.DownloadSources.Count > 1)} out of {downloadItems.Count} items have more than 1 source", false, true); fastObjectListView1.Objects = downloadItems; progressBar1.Maximum = 1000; progressBar1.Value = 0; checkBoxSleep.Enabled = true; _overallSize = FileSize.SumFileSizes(downloadItems.Select(x => x.TotalSize)); var lastCompletedSize = FileSize.Empty; updateTimer.Tick += (o, args) => { var itemCount = fastObjectListView1.GetItemCount(); if (itemCount > 0) { fastObjectListView1.BeginUpdate(); fastObjectListView1.RedrawItems(0, itemCount - 1, true); // Needed if user changes sorting column //fastObjectListView1.SecondarySortColumn = olvColumnNo; fastObjectListView1.Sort(); fastObjectListView1.EndUpdate(); } _completedSize = FileSize.SumFileSizes(downloadItems.Select(x => x.GetDownloadedSize())); var totalPercent = (double)_completedSize.GetKbSize() / (double)_overallSize.GetKbSize() * 100d; if (double.IsNaN(totalPercent)) { totalPercent = 0; } // Download speed calc var secondsPassed = updateTimer.Interval / 1000d; var downloadedSinceLast = FileSize.FromKilobytes((long)((_completedSize - lastCompletedSize).GetKbSize() / secondsPassed)); lastCompletedSize = _completedSize; averageDownloadSpeed.Sample(downloadedSinceLast.GetKbSize()); var etaSeconds = (_overallSize - _completedSize).GetKbSize() / (double)averageDownloadSpeed.GetAverage(); var eta = double.IsNaN(etaSeconds) || etaSeconds <0 || etaSeconds> TimeSpan.MaxValue.TotalSeconds ? "Unknown" : TimeSpan.FromSeconds(etaSeconds).GetReadableTimespan(); labelPercent.Text = $"Overall: {totalPercent:F1}% done ({_completedSize} out of {_overallSize})\r\n" + $"Speed: {downloadedSinceLast}/s (ETA: {eta})"; //$"Speed: {downloadedSinceLast:F1}KB/s"; progressBar1.Value = Math.Min((int)Math.Round(totalPercent * 10), progressBar1.Maximum); }; updateTimer.Start(); SetStatus("Downloading updates...", true, true); await downloader.RunUpdate(_cancelToken.Token); _cancelToken.Token.ThrowIfCancellationRequested(); #endregion #region Show finish messages var failedItems = downloadItems.Where(x => x.Status == UpdateDownloadStatus.Failed).ToList(); var unfinishedCount = downloadItems.Count(x => x.Status != UpdateDownloadStatus.Finished); var s = $"Successfully updated/removed {downloadItems.Count - unfinishedCount} files from {updateTasks.Count} tasks."; if (failedItems.Any()) { s += $"\nFailed to update {failedItems.Count} files because some sources crashed. Check log for details."; } SetStatus(s, true, true); updateTimer.Stop(); progressBar1.Value = progressBar1.Maximum; labelPercent.Text = ""; if (failedItems.Any(x => x.Exceptions.Count > 0)) { var exceptionMessages = failedItems .SelectMany(x => x.Exceptions) .Where(y => !(y is DownloadSourceCrashedException)) // Deal with wrapped exceptions .Select(y => y.Message.Contains("InnerException") && y.InnerException != null ? y.InnerException.Message : y.Message) .Distinct(); var failDetails = "Reason(s) for failing:\n" + string.Join("\n", exceptionMessages); Console.WriteLine(failDetails); s += " " + failDetails; } // Sleep before showing a messagebox since the box will block until user clicks ok SleepIfNecessary(); MessageBox.Show(s, "Finished updating", MessageBoxButtons.OK, MessageBoxIcon.Information); #endregion } catch (OutdatedVersionException ex) { SetStatus("KK Manager needs to be updated to get updates.", true, true); ex.ShowKkmanOutdatedMessage(); } catch (OperationCanceledException) { SetStatus("Update was cancelled by the user.", true, true); } catch (Exception ex) { var exceptions = ex is AggregateException aex?aex.Flatten().InnerExceptions : (ICollection <Exception>) new[] { ex }; if (!exceptions.Any(x => x is OperationCanceledException)) { SleepIfNecessary(); } SetStatus("Unexpected crash while updating mods, aborting.", true, true); SetStatus(string.Join("\n---\n", exceptions), false, true); MessageBox.Show("Something unexpected happened and the update could not be completed. Make sure that your internet connection is stable, " + "and that you did not hit your download limits, then try again.\n\nError message (check log for more):\n" + string.Join("\n", exceptions.Select(x => x.Message)), "Update failed", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { updateTimer.Stop(); checkBoxSleep.Enabled = false; fastObjectListView1.EmptyListMsg = "Nothing was downloaded"; _cancelToken.Cancel(); labelPercent.Text = ""; if (_completedSize != FileSize.Empty) { labelPercent.Text += $"Downloaded {_completedSize} out of {_overallSize}"; if (downloadStartTime != DateTime.MinValue) { var timeSpent = DateTime.Now - downloadStartTime; labelPercent.Text += $" in {timeSpent.GetReadableTimespan()}"; } labelPercent.Text += "\n"; } var averageDlSpeed = averageDownloadSpeed.GetAverage(); if (averageDlSpeed > 0) { labelPercent.Text += $"Average download speed: {new FileSize(averageDlSpeed)}/s"; } progressBar1.Style = ProgressBarStyle.Blocks; button1.Enabled = true; button1.Text = "OK"; if (_autoInstallGuids != null && _autoInstallGuids.Length > 0) { Close(); } SleepControls.AllowSleepOrShutdown(Handle); } }
internal static bool CheckForRunningProcesses(string[] filters, bool doNotKillSteam, Form parentForm = null) { var myId = Process.GetCurrentProcess().Id; var idsToCheck = new List <int>(); foreach (var pr in Process.GetProcesses()) { try { if (pr.Id == myId) { continue; } if (doNotKillSteam && pr.ProcessName.Equals("steam", StringComparison.InvariantCultureIgnoreCase)) { continue; } if (string.IsNullOrEmpty(pr.MainModule.FileName) || pr.MainModule.FileName.StartsWith(WindowsTools.GetEnvironmentPath(CSIDL.CSIDL_SYSTEM), StringComparison.InvariantCultureIgnoreCase)) { continue; } var filenames = pr.Modules.Cast <ProcessModule>() .Select(x => x.FileName) .Where(s => !string.IsNullOrEmpty(s)) .Distinct(); if (filenames.Any(filename => filters.Any(filter => { if (string.IsNullOrEmpty(filename)) { return(false); } if (!Path.IsPathRooted(filename)) { return(false); } return(filename.StartsWith(filter, StringComparison.InvariantCultureIgnoreCase)); }))) { idsToCheck.Add(pr.Id); } } catch { // Ignore invalid processes } } if (idsToCheck.Count > 0) { if (!ProcessWaiter.ShowDialog(parentForm ?? MessageBoxes.DefaultOwner, idsToCheck.ToArray(), false)) { return(false); } } return(true); }
private async void ModUpdateProgress_Shown(object sender, EventArgs e) { try { progressBar1.Style = ProgressBarStyle.Marquee; progressBar1.Value = 0; progressBar1.Maximum = 1; labelPercent.Text = ""; var random = new Random(); if (random.Next(0, 10) >= 9) { var offset = random.Next(0, 136); var offsetStr = new string(Enumerable.Repeat(' ', offset).ToArray()); labelPercent.Text = offsetStr + " ( ) ( )\n"; labelPercent.Text += offsetStr + "( o . o)"; } SetStatus("Preparing..."); if (await ProcessWaiter.CheckForProcessesBlockingKoiDir() == false) { throw new OperationCanceledException(); } SetStatus("Searching for mod updates..."); var updateTasks = await UpdateSourceManager.GetUpdates(_cancelToken.Token, _updaters, _autoInstallGuids); _cancelToken.Token.ThrowIfCancellationRequested(); progressBar1.Style = ProgressBarStyle.Blocks; if (updateTasks.All(x => x.UpToDate)) { SetStatus("Everything is up to date!"); progressBar1.Value = progressBar1.Maximum; _cancelToken.Cancel(); return; } if (_autoInstallGuids == null || _autoInstallGuids.Length == 0) { SetStatus($"Found {updateTasks.Count} updates, waiting for user confirmation."); updateTasks = ModUpdateSelectDialog.ShowWindow(this, updateTasks); } else { var skipped = updateTasks.RemoveAll(x => x.UpToDate); SetStatus($"Found {updateTasks.Count} update tasks in silent mode, {skipped} are already up-to-date.", true, true); } if (updateTasks == null) { throw new OperationCanceledException(); } _overallSize = FileSize.SumFileSizes(updateTasks.Select(x => x.TotalUpdateSize)); _completedSize = FileSize.Empty; var allItems = updateTasks.SelectMany(x => x.GetUpdateItems()) // Remove unnecessary to avoid potential conflicts if the update is aborted midway and a newer version is added .OrderByDescending(sources => sources.Any(x => x.Item2 is DeleteFileUpdateItem)) // Try items with a single source first since they are the most risky .ThenBy(sources => sources.Count()) .ThenBy(sources => sources.FirstOrDefault()?.Item2.TargetPath.FullName) .ToList(); SetStatus($"{allItems.Count(items => items.Count() > 1)} out of {allItems.Count} items have more than 1 source", false, true); progressBar1.Maximum = 1000; for (var index = 0; index < allItems.Count; index++) { _cancelToken.Token.ThrowIfCancellationRequested(); var task = allItems[index]; SetStatus("Downloading " + task.First().Item2.TargetPath.Name); await UpdateSingleItem(task); } var s = $"Successfully updated/removed {allItems.Count - _failedItems.Count} files from {updateTasks.Count} tasks."; if (_failedItems.Any()) { s += $"\nFailed to update {_failedItems.Count} files because some sources crashed. Check log for details."; } SetStatus(s, true, true); if (_failedExceptions.Any()) { var failDetails = "Reason(s) for failing:\n" + string.Join("\n", _failedExceptions.Select(x => x.Message).Distinct()); Console.WriteLine(failDetails); s += " " + failDetails; } MessageBox.Show(s, "Finished updating", MessageBoxButtons.OK, MessageBoxIcon.Information); PerformAutoScale(); } catch (OutdatedVersionException ex) { SetStatus("KK Manager needs to be updated to get updates.", true, true); ex.ShowKkmanOutdatedMessage(); } catch (OperationCanceledException) { SetStatus("Operation was cancelled by the user.", true, true); } catch (Exception ex) { var exceptions = ex is AggregateException aex?aex.Flatten().InnerExceptions : (ICollection <Exception>) new[] { ex }; SetStatus("Crash while updating mods, aborting.", true, true); SetStatus(string.Join("\n---\n", exceptions), false, true); MessageBox.Show("Something unexpected happened and the update could not be completed. Make sure that your internet connection is stable, " + "and that you did not hit your download limits, then try again.\n\nError message (check log for more):\n" + string.Join("\n", exceptions.Select(x => x.Message)), "Update failed", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { var wasCancelled = _cancelToken.IsCancellationRequested; _cancelToken.Cancel(); labelPercent.Text = wasCancelled ? "Update was cancelled" : "Update finished"; if (_completedSize != FileSize.Empty) { labelPercent.Text += $"\nSuccessfully downloaded {_completedSize} out of {_overallSize}."; } progressBar1.Style = ProgressBarStyle.Blocks; button1.Enabled = true; button1.Text = "OK"; if (_autoInstallGuids != null && _autoInstallGuids.Length > 0) { Close(); } } }
/// <exception cref="IOException">Failed to apply the update.</exception> public async Task Update(Progress <double> progressCallback, CancellationToken cancellationToken) { var downloadTarget = await GetTempDownloadFilename(); // Need to store the filename because MoveTo changes it to the new filename var downloadFilename = downloadTarget.FullName; if (RemoteFile != null) { Console.WriteLine($"Attempting download of {TargetPath.Name} from source {RemoteFile.Source.Origin}"); await RetryHelper.RetryOnExceptionAsync(async() => await RemoteFile.Download(downloadTarget, progressCallback, cancellationToken), 2, TimeSpan.FromSeconds(10), cancellationToken); downloadTarget.Refresh(); if (!downloadTarget.Exists || downloadTarget.Length != RemoteFile.ItemSize) { throw new IOException($"Failed to download the update file {RemoteFile.Name} - the downloaded file doesn't exist or is corrupted"); } Console.WriteLine($"Downloaded {downloadTarget.Length} bytes successfully"); } retryDelete: try { Directory.CreateDirectory(Path.GetDirectoryName(TargetPath.FullName)); try { if (TargetPath.Exists) { Console.WriteLine($"Deleting old file {TargetPath.FullName}"); // Prevent issues removing readonly files TargetPath.Attributes = FileAttributes.Normal; TargetPath.Delete(); // Make sure the file gets deleted before continuing await Task.Delay(200, cancellationToken); } if (RemoteFile != null) { downloadTarget.MoveTo(TargetPath.FullName); } } catch (IOException) { if (RemoteFile != null) { await Task.Delay(1000, cancellationToken); downloadTarget.Replace(TargetPath.FullName, TargetPath.FullName + ".old", true); await Task.Delay(1000, cancellationToken); File.Delete(TargetPath.FullName + ".old"); } else { throw; } } } catch (IOException ex) { if (await ProcessWaiter.CheckForProcessesBlockingKoiDir() != true) { throw RetryHelper.DoNotAttemptToRetry(new IOException($"Failed to apply update {TargetPath.FullName} because of an IO issue - {ex.Message}", ex)); } goto retryDelete; } catch (SecurityException ex) { if (MessageBox.Show($"Failed to apply update {TargetPath.FullName} because of a security issue - {ex.Message}\n\nDo you want KK Manager to attempt to fix the issue? Click cancel if you want to abort.", "Could not apply update", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) != DialogResult.OK) { throw; } var fixPermissions = ProcessTools.FixPermissions(InstallDirectoryHelper.GameDirectory.FullName); if (fixPermissions == null) { throw RetryHelper.DoNotAttemptToRetry(new IOException($"Failed to create file in directory {TargetPath.FullName} because of a security issue - {ex.Message}", ex)); } fixPermissions.WaitForExit(); goto retryDelete; } finally { try { File.Delete(downloadFilename); } catch (SystemException ex) { Console.WriteLine(ex); } } }