diff --git a/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs b/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs new file mode 100644 index 000000000..7c3f26e0e --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs @@ -0,0 +1,275 @@ +// Copyright (c) 2016 Daniel Grunwald +// +// 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. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace ICSharpCode.Decompiler.CSharp +{ + /// + /// A container class that holds information about a Visual Studio project. + /// + public sealed class ProjectItem : ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The full path of the project file. + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid) + : base(projectPlatform, projectGuid) + { + ProjectName = Path.GetFileNameWithoutExtension(projectFile); + FilePath = projectFile; + } + + /// + /// Gets the name of the project. + /// + public string ProjectName { get; } + + /// + /// Gets the full path to the project file. + /// + public string FilePath { get; } + } + + /// + /// A container class that holds platform and GUID information about a Visual Studio project. + /// + public class ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectId(string projectPlatform, Guid projectGuid) + { + if (string.IsNullOrWhiteSpace(projectPlatform)) { + throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); + } + + Guid = projectGuid; + PlatformName = projectPlatform; + } + + /// + /// Gets the GUID of this project. + /// + public Guid Guid { get; } + + /// + /// Gets the platform name of this project. Only single platform per project is supported. + /// + public string PlatformName { get; } + } + + /// + /// A helper class that can write a Visual Studio Solution file for the provided projects. + /// + public static class SolutionCreator + { + private static readonly XNamespace ProjectFileNamespace = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003"); + + /// + /// Writes a solution file to the specified . + /// + /// The full path of the file to write. + /// The projects contained in this solution. + /// + /// Thrown when is null or empty. + /// Thrown when is null. + /// Thrown when contains no items. + public static void WriteSolutionFile(string targetFile, IEnumerable projects) + { + if (string.IsNullOrWhiteSpace(targetFile)) { + throw new ArgumentException("The target file cannot be null or empty.", nameof(targetFile)); + } + + if (projects == null) { + throw new ArgumentNullException(nameof(projects)); + } + + if (!projects.Any()) { + throw new InvalidOperationException("At least one project is expected."); + } + + using (var writer = new StreamWriter(targetFile)) { + WriteSolutionFile(writer, projects, Path.GetDirectoryName(targetFile)); + } + + FixProjectReferences(projects); + } + + private static void WriteSolutionFile(TextWriter writer, IEnumerable projects, string solutionPath) + { + WriteHeader(writer); + WriteProjects(writer, projects, solutionPath); + + writer.WriteLine("Global"); + + var platforms = WriteSolutionConfigurations(writer, projects); + WriteProjectConfigurations(writer, projects, platforms); + + writer.WriteLine("\tGlobalSection(SolutionProperties) = preSolution"); + writer.WriteLine("\t\tHideSolutionNode = FALSE"); + writer.WriteLine("\tEndGlobalSection"); + + writer.WriteLine("EndGlobal"); + } + + private static void WriteHeader(TextWriter writer) + { + writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00"); + writer.WriteLine("# Visual Studio 14"); + writer.WriteLine("VisualStudioVersion = 14.0.24720.0"); + writer.WriteLine("MinimumVisualStudioVersion = 10.0.40219.1"); + } + + private static void WriteProjects(TextWriter writer, IEnumerable projects, string solutionPath) + { + var solutionGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); + + foreach (var project in projects) { + var projectRelativePath = GetRelativePath(solutionPath, project.FilePath); + var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); + + writer.WriteLine($"Project(\"{solutionGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); + writer.WriteLine("EndProject"); + } + } + + private static IEnumerable WriteSolutionConfigurations(TextWriter writer, IEnumerable projects) + { + var platforms = projects.GroupBy(p => p.PlatformName).Select(g => g.Key).ToList(); + + platforms.Sort(); + + writer.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + foreach (var platform in platforms) { + writer.WriteLine($"\t\tDebug|{platform} = Debug|{platform}"); + } + + foreach (var platform in platforms) { + writer.WriteLine($"\t\tRelease|{platform} = Release|{platform}"); + } + + writer.WriteLine("\tEndGlobalSection"); + + return platforms; + } + + private static void WriteProjectConfigurations( + TextWriter writer, + IEnumerable projects, + IEnumerable solutionPlatforms) + { + writer.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + + foreach (var project in projects) { + var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); + + foreach (var platform in solutionPlatforms) { + writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.ActiveCfg = Debug|{project.PlatformName}"); + writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.Build.0 = Debug|{project.PlatformName}"); + } + + foreach (var platform in solutionPlatforms) { + writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.ActiveCfg = Release|{project.PlatformName}"); + writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.Build.0 = Release|{project.PlatformName}"); + } + } + + writer.WriteLine("\tEndGlobalSection"); + } + + private static void FixProjectReferences(IEnumerable projects) + { + var projectsMap = projects.ToDictionary(p => p.ProjectName, p => p); + + foreach (var project in projects) { + var projectDirectory = Path.GetDirectoryName(project.FilePath); + XDocument projectDoc = XDocument.Load(project.FilePath); + + var referencesItemGroups = projectDoc.Root + .Elements(ProjectFileNamespace + "ItemGroup") + .Where(e => e.Elements(ProjectFileNamespace + "Reference").Any()); + + foreach (var itemGroup in referencesItemGroups) { + FixProjectReferences(projectDirectory, itemGroup, projectsMap); + } + + projectDoc.Save(project.FilePath); + } + } + + private static void FixProjectReferences(string projectDirectory, XElement itemGroup, IDictionary projects) + { + foreach (var item in itemGroup.Elements(ProjectFileNamespace + "Reference").ToList()) { + var assemblyName = item.Attribute("Include")?.Value; + if (assemblyName != null && projects.TryGetValue(assemblyName, out var referencedProject)) { + item.Remove(); + + var projectReference = new XElement(ProjectFileNamespace + "ProjectReference", + new XElement(ProjectFileNamespace + "Project", referencedProject.Guid.ToString("B").ToUpperInvariant()), + new XElement(ProjectFileNamespace + "Name", referencedProject.ProjectName)); + projectReference.SetAttributeValue("Include", GetRelativePath(projectDirectory, referencedProject.FilePath)); + + itemGroup.Add(projectReference); + } + } + } + + private static string GetRelativePath(string fromPath, string toPath) + { + Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath)); + Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath)); + + if (fromUri.Scheme != toUri.Scheme) { + return toPath; + } + + Uri relativeUri = fromUri.MakeRelativeUri(toUri); + string relativePath = Uri.UnescapeDataString(relativeUri.ToString()); + + if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase)) { + relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } + + return relativePath; + } + + private static string AppendDirectorySeparatorChar(string path) + { + return Path.HasExtension(path) || path.EndsWith(Path.DirectorySeparatorChar.ToString()) + ? path + : path + Path.DirectorySeparatorChar; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index d467ba883..6ec5d6802 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -101,7 +101,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - public void DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) + public ProjectId DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(targetDirectory)) { throw new InvalidOperationException("Must set TargetDirectory"); @@ -110,7 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp directories.Clear(); var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList(); files.AddRange(WriteResourceFilesInProject(moduleDefinition)); - WriteProjectFile(projectFileWriter, files, moduleDefinition); + return WriteProjectFile(projectFileWriter, files, moduleDefinition); } enum LanguageTargets @@ -120,11 +120,12 @@ namespace ICSharpCode.Decompiler.CSharp } #region WriteProjectFile - void WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) + ProjectId WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) { const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; string platformName = GetPlatformName(module); Guid guid = this.ProjectGuid ?? Guid.NewGuid(); + using (XmlTextWriter w = new XmlTextWriter(writer)) { w.Formatting = Formatting.Indented; w.WriteStartDocument(); @@ -281,6 +282,8 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteEndDocument(); } + + return new ProjectId(platformName, guid); } protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 8ab2fbd69..6efca3120 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -61,6 +61,7 @@ + diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 2441e0eb2..57cab0ee8 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -343,12 +343,12 @@ namespace ICSharpCode.ILSpy } } - public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { var module = assembly.GetPEFileOrNull(); if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { var decompiler = new ILSpyWholeProjectDecompiler(assembly, options); - decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); + return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); } else { AddReferenceAssemblyWarningMessage(module, output); AddReferenceWarningMessage(module, output); @@ -415,6 +415,8 @@ namespace ICSharpCode.ILSpy } WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); } + + return true; } } diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index e2ab9d1f3..da26caeb2 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -150,7 +150,7 @@ namespace ICSharpCode.ILSpy dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken)); } - public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { output.WriteLine("// " + assembly.FileName); output.WriteLine(); @@ -174,6 +174,8 @@ namespace ICSharpCode.ILSpy dis.WriteModuleContents(module); } } + + return true; } } } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index a1aad8d64..76f435244 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -131,11 +131,11 @@ namespace ICSharpCode.ILSpy WriteCommentLine(output, nameSpace); } - public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public virtual object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, assembly.FileName); var asm = assembly.GetPEFileOrNull(); - if (asm == null) return; + if (asm == null) return false; var metadata = asm.Metadata; if (metadata.IsAssembly) { var name = metadata.GetAssemblyDefinition(); @@ -147,6 +147,8 @@ namespace ICSharpCode.ILSpy } else { WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name)); } + + return true; } public virtual void WriteCommentLine(ITextOutput output, string comment) diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs index a4ebf5779..45fd3ce67 100644 --- a/ILSpy/SolutionWriter.cs +++ b/ILSpy/SolutionWriter.cs @@ -27,6 +27,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using Microsoft.Win32; @@ -130,6 +131,8 @@ namespace ICSharpCode.ILSpy { var solutionDirectory = Path.GetDirectoryName(solutionFilePath); var statusOutput = new ConcurrentBag(); + var projects = new ConcurrentBag(); + var result = new AvalonEditTextOutput(); var duplicates = new HashSet(); @@ -140,20 +143,45 @@ namespace ICSharpCode.ILSpy Stopwatch stopwatch = Stopwatch.StartNew(); - await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, ct))) - .ConfigureAwait(false); + try { + await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, projects, ct))) + .ConfigureAwait(false); + + await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects)) + .ConfigureAwait(false); + } catch (AggregateException ae) { + if (ae.Flatten().InnerExceptions.All(e => e is OperationCanceledException)) { + result.WriteLine(); + result.WriteLine("Generation was cancelled."); + return result; + } + result.WriteLine(); + result.WriteLine("Failed to generate the Visual Studio Solution. Errors:"); + ae.Handle(e => { + result.WriteLine(e.Message); + return true; + }); + + return result; + } foreach (var item in statusOutput) { result.WriteLine(item); } if (statusOutput.Count == 0) { - result.WriteLine("Successfully decompiled the following assemblies to a Visual Studio Solution:"); + result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:"); foreach (var item in assemblyNodes.Select(n => n.Text.ToString())) { result.WriteLine(item); } + result.WriteLine(); + + if (assemblyNodes.Count() == projects.Count) { + result.WriteLine("Created the Visual Studio Solution file."); + } + result.WriteLine(); result.WriteLine("Elapsed time: " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds."); result.WriteLine(); @@ -163,7 +191,12 @@ namespace ICSharpCode.ILSpy return result; } - private static void WriteProject(AssemblyTreeNode assemblyNode, string targetDirectory, ConcurrentBag statusOutput, CancellationToken ct) + private static void WriteProject( + AssemblyTreeNode assemblyNode, + string targetDirectory, + ConcurrentBag statusOutput, + ConcurrentBag targetContainer, + CancellationToken ct) { var loadedAssembly = assemblyNode.LoadedAssembly; @@ -186,12 +219,17 @@ namespace ICSharpCode.ILSpy FullDecompilation = true, CancellationToken = ct, SaveAsProjectDirectory = targetDirectory }; - - assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options); + + if (assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options) is ProjectId projectInfo) { + targetContainer.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); + } } - } catch (Exception e) { + } + catch (OperationCanceledException) { + throw; + } + catch (Exception e) { statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}"); - return; } } } diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index e53a3a883..3b8e5a519 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public Action Select = delegate { }; - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "List: " + assemblyList.ListName); output.WriteLine(); @@ -150,6 +150,8 @@ namespace ICSharpCode.ILSpy.TreeNodes output.WriteLine(); asm.Decompile(language, output, options); } + + return true; } #region Find*Node diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 8aa8f8c0d..192820246 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info); if (r.IsWindowsRuntime) { @@ -98,6 +98,8 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } + + return true; } } } diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 60f34e6b2..39669c15f 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { void HandleException(Exception ex, string message) { @@ -259,21 +259,21 @@ namespace ICSharpCode.ILSpy.TreeNodes switch (ex.InnerException) { case BadImageFormatException badImage: HandleException(badImage, "This file does not contain a managed assembly."); - return; + return null; case FileNotFoundException fileNotFound: HandleException(fileNotFound, "The file was not found."); - return; + return null; case DirectoryNotFoundException dirNotFound: HandleException(dirNotFound, "The directory was not found."); - return; + return null; case PEFileNotSupportedException notSupported: HandleException(notSupported, notSupported.Message); - return; + return null; default: throw; } } - language.DecompileAssembly(LoadedAssembly, output, options); + return language.DecompileAssembly(LoadedAssembly, output, options); } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs index 3c96c40dd..41edd0f8c 100644 --- a/ILSpy/TreeNodes/BaseTypesEntryNode.cs +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -97,9 +97,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); + return true; } IEntity IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 8cbdb2304..58565a938 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -69,12 +69,14 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); } + + return true; } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 91da3e033..f3d9fe29f 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -90,9 +90,10 @@ namespace ICSharpCode.ILSpy.TreeNodes e.Handled = BaseTypesEntryNode.ActivateItem(this, type); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); + return true; } IEntity IMemberTreeNode.Member => type; diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 3b6e50a30..8518e08e3 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -92,9 +92,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return typeRef.GetFullTypeName(referenceMetadata) == typeDef.GetFullTypeName(definitionMetadata); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { threading.Decompile(language, output, options, EnsureLazyChildren); + return true; } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 1aadff896..c1797f794 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -67,9 +67,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileEvent(EventDefinition, output, options); + return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 5d4d2f7b0..5d3323beb 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -68,9 +68,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileField(FieldDefinition, output, options); + return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 11fe3b278..515188374 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options); + public abstract object Decompile(Language language, ITextOutput output, DecompilationOptions options); /// /// Used to implement special view logic for some items. diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 9e4d95e45..89c12ec10 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -83,9 +83,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileMethod(MethodDefinition, output, options); + return true; } public override FilterResult Filter(FilterSettings settings) diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index 4d51f46af..a363c46db 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -72,10 +72,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, moduleName); language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata"); + return true; } } } diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index d054603dc..3b9ff4845 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -56,9 +56,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileNamespace(name, this.Children.OfType().Select(t => t.TypeDefinition), output, options); + return true; } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 14769ea66..3267a1e1a 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -74,9 +74,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileProperty(PropertyDefinition, output, options); + return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 331f8da08..9121e213f 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, $"Detected Target-Framework-Id: {parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().Result}"); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } - + return true; } } } diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index c67c9d4b1..79cc1df50 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -64,13 +64,15 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); output.WriteLine(); } + + return true; } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs index fb8b2199f..3805db739 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs @@ -75,9 +75,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { EnsureLazyChildren(); + return true; } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index ec74ad193..cf5a3d5ac 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -73,9 +73,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return result; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, string.Format("{0} = {1}", key, data)); + return true; } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index 3433f70d7..c9adaa49c 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes)); @@ -76,6 +76,8 @@ namespace ICSharpCode.ILSpy.TreeNodes smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); }); output.WriteLine(); } + + return true; } public override bool View(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs index c1d908481..a44edeae4 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return true; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { EnsureLazyChildren(); base.Decompile(language, output, options); @@ -168,6 +168,8 @@ namespace ICSharpCode.ILSpy.TreeNodes } output.WriteLine(); } + + return true; } internal class SerializedObjectRepresentation diff --git a/ILSpy/TreeNodes/ThreadingSupport.cs b/ILSpy/TreeNodes/ThreadingSupport.cs index e95787efc..8df078cad 100644 --- a/ILSpy/TreeNodes/ThreadingSupport.cs +++ b/ILSpy/TreeNodes/ThreadingSupport.cs @@ -128,8 +128,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Match; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { + return false; } } @@ -151,8 +152,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Match; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { + return false; } } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 3ef91c411..4464a3521 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -103,9 +103,10 @@ namespace ICSharpCode.ILSpy.TreeNodes public override bool CanExpandRecursively => true; - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileType(TypeDefinition, output, options); + return true; } public override object Icon => GetIcon(TypeDefinition);