From a494bfadbe5327cd8577b53c40f744d0f39a7072 Mon Sep 17 00:00:00 2001 From: "Andrew Crawley (US - DIAGNOSTICS)" Date: Thu, 13 Oct 2022 17:22:27 -0700 Subject: [PATCH 1/3] Add progress reporting to PortablePdbWriter This commit adds a new parameter to PortablePdbWriter.WritePdb that allows the caller to provide an implementation of IProgress to receive status updates on the pdb generation process. Currently, the progress reports include the number of files generated so far and the total number of files. --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PdbGenerationTestRunner.cs | 58 +++++++++++++++++-- .../TestCases/PdbGen/CustomPdbId.xml | 4 +- .../TestCases/PdbGen/ProgressReporting.xml | 44 ++++++++++++++ .../DebugInfo/PortablePdbWriter.cs | 31 +++++++++- 5 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/PdbGen/ProgressReporting.xml diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index d342695c05..2e50ffcf1f 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -312,6 +312,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index 174711c8b5..c2a58c3d82 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; @@ -9,13 +8,10 @@ using System.Xml.Linq; using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Tests.Helpers; -using ICSharpCode.Decompiler.TypeSystem; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.DiaSymReader.Tools; using NUnit.Framework; @@ -72,6 +68,60 @@ public void CustomPdbId() } } + [Test] + public void ProgressReporting() + { + // Generate a PDB for an assembly and validate that the progress reporter is called with reasonable values + (string peFileName, string pdbFileName) = CompileTestCase(nameof(ProgressReporting)); + + var moduleDefinition = new PEFile(peFileName); + var resolver = new UniversalAssemblyResolver(peFileName, false, moduleDefinition.Metadata.DetectTargetFrameworkId(), null, PEStreamOptions.PrefetchEntireImage); + var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); + + var lastFilesWritten = 0; + var totalFiles = -1; + + Action reportFunc = progress => { + if (totalFiles == -1) + { + // Initialize value on first call + totalFiles = progress.TotalFiles; + } + + Assert.AreEqual(progress.TotalFiles, totalFiles); + Assert.AreEqual(progress.FilesWritten, lastFilesWritten + 1); + + lastFilesWritten = progress.FilesWritten; + }; + + using (FileStream pdbStream = File.Open(Path.Combine(TestCasePath, nameof(ProgressReporting) + ".pdb"), FileMode.OpenOrCreate, FileAccess.ReadWrite)) + { + pdbStream.SetLength(0); + PortablePdbWriter.WritePdb(moduleDefinition, decompiler, new DecompilerSettings(), pdbStream, noLogo: true, progress: new TestProgressReporter(reportFunc)); + + pdbStream.Position = 0; + var metadataReader = MetadataReaderProvider.FromPortablePdbStream(pdbStream).GetMetadataReader(); + var generatedPdbId = new BlobContentId(metadataReader.DebugMetadataHeader.Id); + } + + Assert.AreEqual(totalFiles, lastFilesWritten); + } + + private class TestProgressReporter : IProgress + { + private Action reportFunc; + + public TestProgressReporter(Action reportFunc) + { + this.reportFunc = reportFunc; + } + + public void Report(WritePortablePdbProgress value) + { + reportFunc(value); + } + } + private void TestGeneratePdb([CallerMemberName] string testName = null) { const PdbToXmlOptions options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.ThrowOnError | PdbToXmlOptions.IncludeTokens | PdbToXmlOptions.ResolveTokens | PdbToXmlOptions.IncludeMethodSpans; diff --git a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml index e0708da65a..e5d691c3da 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml +++ b/ICSharpCode.Decompiler.Tests/TestCases/PdbGen/CustomPdbId.xml @@ -1,11 +1,11 @@ - + + + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index cc8e342bde..3be497b77b 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -28,6 +28,7 @@ using System.Reflection.PortableExecutable; using System.Security.Cryptography; using System.Text; +using System.Threading; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; @@ -40,6 +41,12 @@ 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); @@ -49,7 +56,14 @@ public static bool HasCodeViewDebugDirectoryEntry(PEFile file) return file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView); } - public static void WritePdb(PEFile file, CSharpDecompiler decompiler, DecompilerSettings settings, Stream targetStream, bool noLogo = false, BlobContentId? pdbId = null) + public static void WritePdb( + PEFile file, + CSharpDecompiler decompiler, + DecompilerSettings settings, + Stream targetStream, + bool noLogo = false, + BlobContentId? pdbId = null, + IProgress progress = null) { MetadataBuilder metadata = new MetadataBuilder(); MetadataReader reader = file.Metadata; @@ -72,10 +86,23 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) return Path.Combine(ns, WholeProjectDecompiler.CleanUpFileName(typeName.Name) + ".cs"); } - foreach (var sourceFile in reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName)) + var sourceFiles = reader.GetTopLevelTypeDefinitions().GroupBy(BuildFileNameFromTypeName).ToList(); + WritePortablePdbProgress currentProgress = new WritePortablePdbProgress() { + TotalFiles = sourceFiles.Count, + FilesWritten = 0 + }; + + foreach (var sourceFile in sourceFiles) { // Generate syntax tree var syntaxTree = decompiler.DecompileTypes(sourceFile); + + if (progress != null) + { + currentProgress.FilesWritten++; + progress.Report(currentProgress); + } + if (!syntaxTree.HasChildren) continue; From 343694c5499d2dd68896563bbbfc72604231deef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 21 Oct 2022 09:16:44 +0200 Subject: [PATCH 2/3] Generalize progress reporting for decompilation and PDB generation. --- .../GetDecompiledProjectCmdlet.cs | 4 +- .../PdbGenerationTestRunner.cs | 18 +++---- .../WholeProjectDecompiler.cs | 20 ++------ .../DebugInfo/PortablePdbWriter.cs | 17 +++---- .../DecompilationProgress.cs | 48 +++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 1 + ILSpy/Commands/GeneratePdbContextMenuEntry.cs | 4 +- ILSpy/DecompilationOptions.cs | 9 ++++ ILSpy/Languages/CSharpLanguage.cs | 1 + ILSpy/MainWindow.xaml.cs | 6 ++- 10 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 ICSharpCode.Decompiler/DecompilationProgress.cs diff --git a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs index 4db4ad4554..9b87d99bee 100644 --- a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs +++ b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs @@ -33,8 +33,8 @@ public void Report(DecompilationProgress value) 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 c2a58c3d82..991c59caef 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -81,17 +81,17 @@ public void ProgressReporting() 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 @@ public void ProgressReporting() 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 3057cbb6f7..c49fde67cd 100644 --- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs @@ -223,8 +223,8 @@ CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) 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 @@ CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) 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 @@ public static bool CanUseSdkStyleProjectFormat(PEFile module) 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 3be497b77b..b0b419aab1 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -41,12 +41,6 @@ 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 @@ public static void WritePdb( 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 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) } 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 @@ string BuildFileNameFromTypeName(TypeDefinitionHandle handle) 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 0000000000..3d96349989 --- /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 b3b5cc7a6f..25d9b0bf98 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 3af1c3b643..84bafc4239 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -75,12 +75,14 @@ internal static void GeneratePdbForAssembly(LoadedAssembly assembly) 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 d241da8f14..7d6feb5c04 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 @@ public class DecompilationOptions /// 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 a1d9428829..492ecf2207 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -404,6 +404,7 @@ public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput 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 cf64691d78..3ffe81d2d3 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -105,7 +105,11 @@ public SearchPane SearchPane { 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() { From 4619b997e530eff34aadef5d034d2925a1d409c2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 21 Oct 2022 09:19:15 +0200 Subject: [PATCH 3/3] Implement better progress reporting in ILSpy. --- ILSpy/TextView/DecompilerTextView.cs | 39 ++++++++++++++++++-------- ILSpy/TextView/DecompilerTextView.xaml | 21 +++++++++----- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 02c00bfda2..539c0b4c4f 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.ILSpy.TextView /// Manages the TextEditor showing the decompiled code. /// Contains all the threading logic that makes the decompiler work in the background. /// - public sealed partial class DecompilerTextView : UserControl, IDisposable, IHaveState + public sealed partial class DecompilerTextView : UserControl, IDisposable, IHaveState, IProgress { readonly ReferenceElementGenerator referenceElementGenerator; readonly UIElementGenerator uiElementGenerator; @@ -540,18 +540,26 @@ void HighlightBrackets(object? sender, EventArgs e) #endregion #region RunWithCancellation - /// - /// Switches the GUI into "waiting" mode, then calls to create - /// the task. - /// When the task completes without being cancelled, the - /// callback is called on the GUI thread. - /// When the task is cancelled before completing, the callback is not called; and any result - /// of the task (including exceptions) are ignored. - /// - [Obsolete("RunWithCancellation(taskCreation).ContinueWith(taskCompleted) instead")] - public void RunWithCancellation(Func> taskCreation, Action> taskCompleted) + public void Report(DecompilationProgress value) { - RunWithCancellation(taskCreation).ContinueWith(taskCompleted, CancellationToken.None, TaskContinuationOptions.NotOnCanceled, TaskScheduler.FromCurrentSynchronizationContext()); + double v = (double)value.UnitsCompleted / value.TotalUnits; + Dispatcher.BeginInvoke(DispatcherPriority.Normal, delegate { + progressBar.IsIndeterminate = !double.IsFinite(v); + progressBar.Value = v * 100.0; + progressTitle.Text = !string.IsNullOrWhiteSpace(value.Title) ? value.Title : Properties.Resources.Decompiling; + progressText.Text = value.Status; + progressText.Visibility = !string.IsNullOrWhiteSpace(progressText.Text) ? Visibility.Visible : Visibility.Collapsed; + var taskBar = MainWindow.Instance.TaskbarItemInfo; + if (taskBar != null) + { + taskBar.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal; + taskBar.ProgressValue = v; + } + if (this.DataContext is TabPageModel model) + { + model.Title = progressTitle.Text; + } + }); } /// @@ -566,7 +574,10 @@ public Task RunWithCancellation(Func> taskCreat waitAdorner.Visibility = Visibility.Visible; // Work around a WPF bug by setting IsIndeterminate only while the progress bar is visible. // https://github.com/icsharpcode/ILSpy/issues/593 + progressTitle.Text = Properties.Resources.Decompiling; progressBar.IsIndeterminate = true; + progressText.Text = null; + progressText.Visibility = Visibility.Collapsed; waitAdorner.BeginAnimation(OpacityProperty, new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(0.5)), FillBehavior.Stop)); var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) @@ -605,6 +616,8 @@ public Task RunWithCancellation(Func> taskCreat currentCancellationTokenSource = null; waitAdorner.Visibility = Visibility.Collapsed; progressBar.IsIndeterminate = false; + progressText.Text = null; + progressText.Visibility = Visibility.Collapsed; var taskBar = MainWindow.Instance.TaskbarItemInfo; if (taskBar != null) { @@ -828,6 +841,7 @@ Task DoDecompile(DecompilationContext context, int outputLengthLimit) return RunWithCancellation( delegate (CancellationToken ct) { // creation of the background task context.Options.CancellationToken = ct; + context.Options.Progress = this; decompiledNodes = context.TreeNodes; return DecompileAsync(context, outputLengthLimit); }) @@ -1091,6 +1105,7 @@ Task SaveToDiskAsync(DecompilationContext context, string { bool originalProjectFormatSetting = context.Options.DecompilerSettings.UseSdkStyleProjectFormat; context.Options.EscapeInvalidIdentifiers = true; + context.Options.Progress = this; AvalonEditTextOutput output = new AvalonEditTextOutput { EnableHyperlinks = true, Title = string.Join(", ", context.TreeNodes.Select(n => n.Text)) diff --git a/ILSpy/TextView/DecompilerTextView.xaml b/ILSpy/TextView/DecompilerTextView.xaml index 66960f102d..6ff6d2130d 100644 --- a/ILSpy/TextView/DecompilerTextView.xaml +++ b/ILSpy/TextView/DecompilerTextView.xaml @@ -20,8 +20,7 @@ folding:FoldingMargin.FoldingMarkerBackgroundBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" folding:FoldingMargin.SelectedFoldingMarkerBackgroundBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" folding:FoldingMargin.FoldingMarkerBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" - folding:FoldingMargin.SelectedFoldingMarkerBrush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" - > + folding:FoldingMargin.SelectedFoldingMarkerBrush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}">