diff --git a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs index 4db4ad455..9b87d99be 100644 --- a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs +++ b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs @@ -33,8 +33,8 @@ namespace ICSharpCode.Decompiler.PowerShell lock (syncObject) { completed++; - progress = new ProgressRecord(1, "Decompiling " + fileName, $"Completed {completed} of {value.TotalNumberOfFiles}: {value.Status}") { - PercentComplete = (int)(completed * 100.0 / value.TotalNumberOfFiles) + progress = new ProgressRecord(1, "Decompiling " + fileName, $"Completed {completed} of {value.TotalUnits}: {value.Status}") { + PercentComplete = (int)(completed * 100.0 / value.TotalUnits) }; } } diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index c2a58c3d8..991c59cae 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -81,17 +81,17 @@ namespace ICSharpCode.Decompiler.Tests var lastFilesWritten = 0; var totalFiles = -1; - Action reportFunc = progress => { + Action reportFunc = progress => { if (totalFiles == -1) { // Initialize value on first call - totalFiles = progress.TotalFiles; + totalFiles = progress.TotalUnits; } - Assert.AreEqual(progress.TotalFiles, totalFiles); - Assert.AreEqual(progress.FilesWritten, lastFilesWritten + 1); + Assert.AreEqual(progress.TotalUnits, totalFiles); + Assert.AreEqual(progress.UnitsCompleted, lastFilesWritten + 1); - lastFilesWritten = progress.FilesWritten; + lastFilesWritten = progress.UnitsCompleted; }; using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) @@ -107,16 +107,16 @@ namespace ICSharpCode.Decompiler.Tests Assert.AreEqual(totalFiles, lastFilesWritten); } - private class TestProgressReporter : IProgress + private class TestProgressReporter : IProgress { - private Action reportFunc; + private Action reportFunc; - public TestProgressReporter(Action reportFunc) + public TestProgressReporter(Action reportFunc) { this.reportFunc = reportFunc; } - public void Report(WritePortablePdbProgress value) + public void Report(DecompilationProgress value) { reportFunc(value); } diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs index 3057cbb6f..c49fde67c 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -223,8 +223,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler return Path.Combine(dir, file); } }, StringComparer.OrdinalIgnoreCase).ToList(); - int total = files.Count; - var progress = ProgressIndicator; + var progressReporter = ProgressIndicator; + var progress = new DecompilationProgress { TotalUnits = files.Count, Title = "Exporting project..." }; DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings); Parallel.ForEach( Partitioner.Create(files, loadBalance: true), @@ -253,7 +253,9 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException); } } - progress?.Report(new DecompilationProgress(total, file.Key)); + progress.Status = file.Key; + Interlocked.Increment(ref progress.UnitsCompleted); + progressReporter?.Report(progress); }); return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken)); } @@ -705,16 +707,4 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler return TargetServices.DetectTargetFramework(module).Moniker != null; } } - - public readonly struct DecompilationProgress - { - public readonly int TotalNumberOfFiles; - public readonly string Status; - - public DecompilationProgress(int total, string status = null) - { - this.TotalNumberOfFiles = total; - this.Status = status ?? ""; - } - } } diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index 3be497b77..b0b419aab 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -41,12 +41,6 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.DebugInfo { - public struct WritePortablePdbProgress - { - public int TotalFiles { get; internal set; } - public int FilesWritten { get; internal set; } - } - public class PortablePdbWriter { static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location); @@ -63,7 +57,7 @@ namespace ICSharpCode.Decompiler.DebugInfo Stream targetStream, bool noLogo = false, BlobContentId? pdbId = null, - IProgress progress = null) + IProgress progress = null) { MetadataBuilder metadata = new MetadataBuilder(); MetadataReader reader = file.Metadata; @@ -87,9 +81,10 @@ namespace ICSharpCode.Decompiler.DebugInfo } var sourceFiles = reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName).ToList(); - WritePortablePdbProgress currentProgress = new WritePortablePdbProgress() { - TotalFiles = sourceFiles.Count, - FilesWritten = 0 + DecompilationProgress currentProgress = new() { + TotalUnits = sourceFiles.Count, + UnitsCompleted = 0, + Title = "Generating portable PDB..." }; foreach (var sourceFile in sourceFiles) @@ -99,7 +94,7 @@ namespace ICSharpCode.Decompiler.DebugInfo if (progress != null) { - currentProgress.FilesWritten++; + currentProgress.UnitsCompleted++; progress.Report(currentProgress); } diff --git a/ICSharpCode.Decompiler/DecompilationProgress.cs b/ICSharpCode.Decompiler/DecompilationProgress.cs new file mode 100644 index 000000000..3d9634998 --- /dev/null +++ b/ICSharpCode.Decompiler/DecompilationProgress.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2022 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#nullable enable + +namespace ICSharpCode.Decompiler +{ + /// + /// Information used for (optional) progress reporting by the decompiler. + /// + public struct DecompilationProgress + { + /// + /// The total number of units to process. If set to a value <= 0, an indeterminate progress bar is displayed. + /// + public int TotalUnits; + + /// + /// The number of units currently completed. Should be a positive number. + /// + public int UnitsCompleted; + + /// + /// Optional information displayed alongside the progress bar. + /// + public string? Status; + + /// + /// Optional custom title for the operation. + /// + public string? Title; + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index b3b5cc7a6..25d9b0bf9 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -90,6 +90,7 @@ + diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index 3af1c3b64..84bafc423 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -75,12 +75,14 @@ namespace ICSharpCode.ILSpy Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task.Factory.StartNew(() => { AvalonEditTextOutput output = new AvalonEditTextOutput(); Stopwatch stopwatch = Stopwatch.StartNew(); + options.CancellationToken = ct; using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) { try { var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(), options.DecompilerSettings); - PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream); + decompiler.CancellationToken = ct; + PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream, progress: options.Progress); } catch (OperationCanceledException) { diff --git a/ILSpy/DecompilationOptions.cs b/ILSpy/DecompilationOptions.cs index d241da8f1..7d6feb5c0 100644 --- a/ILSpy/DecompilationOptions.cs +++ b/ILSpy/DecompilationOptions.cs @@ -19,6 +19,7 @@ using System; using System.Threading; +using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpyX; @@ -55,6 +56,14 @@ namespace ICSharpCode.ILSpy /// public CancellationToken CancellationToken { get; set; } + /// + /// Gets the progress reporter. + /// + /// + /// If decompilers do not implement progress reporting, an indeterminate wait bar is displayed. + /// + public IProgress Progress { get; set; } + /// /// Gets the settings for the decompiler. /// diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index a1d942882..492ecf220 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -404,6 +404,7 @@ namespace ICSharpCode.ILSpy options.DecompilerSettings.UseSdkStyleProjectFormat = false; } var decompiler = new ILSpyWholeProjectDecompiler(assembly, options); + decompiler.ProgressIndicator = options.Progress; return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); } else diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index cf64691d7..3ffe81d2d 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -105,7 +105,11 @@ namespace ICSharpCode.ILSpy public DisplaySettings CurrentDisplaySettings { get; internal set; } - public DecompilationOptions CreateDecompilationOptions() => new DecompilationOptions(CurrentLanguageVersion, CurrentDecompilerSettings, CurrentDisplaySettings); + public DecompilationOptions CreateDecompilationOptions() + { + var decompilerView = DockWorkspace.Instance.ActiveTabPage.Content as IProgress; + return new DecompilationOptions(CurrentLanguageVersion, CurrentDecompilerSettings, CurrentDisplaySettings) { Progress = decompilerView }; + } public MainWindow() {