Browse Source

Refactor Solution decompilation to use Language instead of AssemblyTreeNode.

pull/1550/head
Siegfried Pammer 6 years ago
parent
commit
a63e94e5b4
  1. 1
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs
  2. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 56
      ICSharpCode.Decompiler/Solution/ProjectId.cs
  4. 55
      ICSharpCode.Decompiler/Solution/ProjectItem.cs
  5. 70
      ICSharpCode.Decompiler/Solution/SolutionCreator.cs
  6. 8
      ILSpy/Languages/CSharpLanguage.cs
  7. 7
      ILSpy/Languages/ILLanguage.cs
  8. 8
      ILSpy/Languages/Language.cs
  9. 85
      ILSpy/MainWindow.xaml.cs
  10. 140
      ILSpy/SolutionWriter.cs
  11. 4
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  12. 4
      ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
  13. 12
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  14. 3
      ILSpy/TreeNodes/BaseTypesEntryNode.cs
  15. 4
      ILSpy/TreeNodes/BaseTypesTreeNode.cs
  16. 3
      ILSpy/TreeNodes/DerivedTypesEntryNode.cs
  17. 3
      ILSpy/TreeNodes/DerivedTypesTreeNode.cs
  18. 3
      ILSpy/TreeNodes/EventTreeNode.cs
  19. 3
      ILSpy/TreeNodes/FieldTreeNode.cs
  20. 2
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  21. 3
      ILSpy/TreeNodes/MethodTreeNode.cs
  22. 3
      ILSpy/TreeNodes/ModuleReferenceTreeNode.cs
  23. 3
      ILSpy/TreeNodes/NamespaceTreeNode.cs
  24. 3
      ILSpy/TreeNodes/PropertyTreeNode.cs
  25. 3
      ILSpy/TreeNodes/ReferenceFolderTreeNode.cs
  26. 4
      ILSpy/TreeNodes/ResourceListTreeNode.cs
  27. 3
      ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs
  28. 3
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  29. 4
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  30. 4
      ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
  31. 6
      ILSpy/TreeNodes/ThreadingSupport.cs
  32. 3
      ILSpy/TreeNodes/TypeTreeNode.cs

1
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -35,6 +35,7 @@ using System.Reflection.Metadata;
using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions; using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions;
using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
namespace ICSharpCode.Decompiler.CSharp namespace ICSharpCode.Decompiler.CSharp
{ {

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -61,7 +61,9 @@
<Compile Include="CSharp\CSharpLanguageVersion.cs" /> <Compile Include="CSharp\CSharpLanguageVersion.cs" />
<Compile Include="CSharp\RequiredNamespaceCollector.cs" /> <Compile Include="CSharp\RequiredNamespaceCollector.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" /> <Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\SolutionCreator.cs" /> <Compile Include="Solution\ProjectId.cs" />
<Compile Include="Solution\ProjectItem.cs" />
<Compile Include="Solution\SolutionCreator.cs" />
<Compile Include="CSharp\Syntax\AstNode.cs" /> <Compile Include="CSharp\Syntax\AstNode.cs" />
<Compile Include="CSharp\Syntax\AstNodeCollection.cs" /> <Compile Include="CSharp\Syntax\AstNodeCollection.cs" />
<Compile Include="CSharp\Syntax\AstType.cs" /> <Compile Include="CSharp\Syntax\AstType.cs" />

56
ICSharpCode.Decompiler/Solution/ProjectId.cs

@ -0,0 +1,56 @@
// Copyright (c) 2019 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.
using System;
namespace ICSharpCode.Decompiler.Solution
{
/// <summary>
/// A container class that holds platform and GUID information about a Visual Studio project.
/// </summary>
public class ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectId"/> class.
/// </summary>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
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;
}
/// <summary>
/// Gets the GUID of this project.
/// </summary>
public Guid Guid { get; }
/// <summary>
/// Gets the platform name of this project. Only single platform per project is supported.
/// </summary>
public string PlatformName { get; }
}
}

55
ICSharpCode.Decompiler/Solution/ProjectItem.cs

@ -0,0 +1,55 @@
// Copyright (c) 2019 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.
using System;
using System.IO;
namespace ICSharpCode.Decompiler.Solution
{
/// <summary>
/// A container class that holds information about a Visual Studio project.
/// </summary>
public sealed class ProjectItem : ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectItem"/> class.
/// </summary>
/// <param name="projectFile">The full path of the project file.</param>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid)
: base(projectPlatform, projectGuid)
{
ProjectName = Path.GetFileNameWithoutExtension(projectFile);
FilePath = projectFile;
}
/// <summary>
/// Gets the name of the project.
/// </summary>
public string ProjectName { get; }
/// <summary>
/// Gets the full path to the project file.
/// </summary>
public string FilePath { get; }
}
}

70
ICSharpCode.Decompiler/CSharp/SolutionCreator.cs → ICSharpCode.Decompiler/Solution/SolutionCreator.cs

@ -1,4 +1,4 @@
// Copyright (c) 2016 Daniel Grunwald // Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of this // 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 // software and associated documentation files (the "Software"), to deal in the Software
@ -22,74 +22,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
namespace ICSharpCode.Decompiler.CSharp namespace ICSharpCode.Decompiler.Solution
{ {
/// <summary>
/// A container class that holds information about a Visual Studio project.
/// </summary>
public sealed class ProjectItem : ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectItem"/> class.
/// </summary>
/// <param name="projectFile">The full path of the project file.</param>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid)
: base(projectPlatform, projectGuid)
{
ProjectName = Path.GetFileNameWithoutExtension(projectFile);
FilePath = projectFile;
}
/// <summary>
/// Gets the name of the project.
/// </summary>
public string ProjectName { get; }
/// <summary>
/// Gets the full path to the project file.
/// </summary>
public string FilePath { get; }
}
/// <summary>
/// A container class that holds platform and GUID information about a Visual Studio project.
/// </summary>
public class ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectId"/> class.
/// </summary>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
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;
}
/// <summary>
/// Gets the GUID of this project.
/// </summary>
public Guid Guid { get; }
/// <summary>
/// Gets the platform name of this project. Only single platform per project is supported.
/// </summary>
public string PlatformName { get; }
}
/// <summary> /// <summary>
/// A helper class that can write a Visual Studio Solution file for the provided projects. /// A helper class that can write a Visual Studio Solution file for the provided projects.
/// </summary> /// </summary>

8
ILSpy/Languages/CSharpLanguage.cs

@ -34,6 +34,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.TreeNodes;
@ -343,7 +344,7 @@ namespace ICSharpCode.ILSpy
} }
} }
public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{ {
var module = assembly.GetPEFileOrNull(); var module = assembly.GetPEFileOrNull();
if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { if (options.FullDecompilation && options.SaveAsProjectDirectory != null) {
@ -389,7 +390,7 @@ namespace ICSharpCode.ILSpy
} }
if (metadata.IsAssembly) { if (metadata.IsAssembly) {
var asm = metadata.GetAssemblyDefinition(); var asm = metadata.GetAssemblyDefinition();
if (asm.HashAlgorithm != System.Reflection.AssemblyHashAlgorithm.None) if (asm.HashAlgorithm != AssemblyHashAlgorithm.None)
output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper()); output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper());
if (!asm.PublicKey.IsNil) { if (!asm.PublicKey.IsNil) {
output.Write("// Public key: "); output.Write("// Public key: ");
@ -415,8 +416,7 @@ namespace ICSharpCode.ILSpy
} }
WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem);
} }
return null;
return true;
} }
} }

7
ILSpy/Languages/ILLanguage.cs

@ -27,6 +27,8 @@ using System.Reflection.Metadata.Ecma335;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.Decompiler.Solution;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
@ -150,7 +152,7 @@ namespace ICSharpCode.ILSpy
dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken)); dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken));
} }
public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{ {
output.WriteLine("// " + assembly.FileName); output.WriteLine("// " + assembly.FileName);
output.WriteLine(); output.WriteLine();
@ -174,8 +176,7 @@ namespace ICSharpCode.ILSpy
dis.WriteModuleContents(module); dis.WriteModuleContents(module);
} }
} }
return null;
return true;
} }
} }
} }

8
ILSpy/Languages/Language.cs

@ -23,6 +23,7 @@ using System.Reflection.PortableExecutable;
using System.Text; using System.Text;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -131,11 +132,11 @@ namespace ICSharpCode.ILSpy
WriteCommentLine(output, nameSpace); WriteCommentLine(output, nameSpace);
} }
public virtual object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{ {
WriteCommentLine(output, assembly.FileName); WriteCommentLine(output, assembly.FileName);
var asm = assembly.GetPEFileOrNull(); var asm = assembly.GetPEFileOrNull();
if (asm == null) return false; if (asm == null) return null;
var metadata = asm.Metadata; var metadata = asm.Metadata;
if (metadata.IsAssembly) { if (metadata.IsAssembly) {
var name = metadata.GetAssemblyDefinition(); var name = metadata.GetAssemblyDefinition();
@ -147,8 +148,7 @@ namespace ICSharpCode.ILSpy
} else { } else {
WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name)); WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name));
} }
return null;
return true;
} }
public virtual void WriteCommentLine(ITextOutput output, string comment) public virtual void WriteCommentLine(ITextOutput output, string comment)

85
ILSpy/MainWindow.xaml.cs

@ -914,29 +914,80 @@ namespace ICSharpCode.ILSpy
void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{ {
var selectedNodes = SelectedNodes.ToList(); var selectedNodes = SelectedNodes.ToList();
if (selectedNodes.Count > 1) { if (selectedNodes.Count == 1) {
var assemblyNodes = selectedNodes // if there's only one treenode selected
.OfType<AssemblyTreeNode>() // we will invoke the custom Save logic
.Where(n => n.Language is CSharpLanguage) if (selectedNodes[0].Save(TextView))
.ToList();
if (assemblyNodes.Count == selectedNodes.Count) {
var initialPath = Path.GetDirectoryName(assemblyNodes[0].LoadedAssembly.FileName);
var selectedPath = SolutionWriter.SelectSolutionFile(initialPath);
if (!string.IsNullOrEmpty(selectedPath)) {
SolutionWriter.CreateSolution(TextView, selectedPath, assemblyNodes);
}
return; return;
} else if (selectedNodes.All(n => n is AssemblyTreeNode)) {
var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName);
var selectedPath = SelectSolutionFile(initialPath);
if (!string.IsNullOrEmpty(selectedPath)) {
var assemblies = selectedNodes.OfType<AssemblyTreeNode>()
.Select(n => n.LoadedAssembly)
.Where(a => a != null).ToArray();
SolutionWriter.CreateSolution(TextView, selectedPath, CurrentLanguage, assemblies);
} }
return;
} }
if (selectedNodes.Count != 1 || !selectedNodes[0].Save(TextView)) { // Fallback: if nobody was able to handle the request, use default behavior.
var options = new DecompilationOptions() { FullDecompilation = true }; // try to save all nodes to disk.
TextView.SaveToDisk(CurrentLanguage, selectedNodes, options); var options = new DecompilationOptions() { FullDecompilation = true };
TextView.SaveToDisk(CurrentLanguage, selectedNodes, options);
}
/// <summary>
/// Shows a File Selection dialog where the user can select the target file for the solution.
/// </summary>
/// <param name="path">The initial path to show in the dialog. If not specified, the 'Documents' directory
/// will be used.</param>
///
/// <returns>The full path of the selected target file, or <c>null</c> if the user canceled.</returns>
string SelectSolutionFile(string path)
{
const string SolutionExtension = ".sln";
const string DefaultSolutionName = "Solution";
if (string.IsNullOrWhiteSpace(path)) {
path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
} }
SaveFileDialog dlg = new SaveFileDialog();
dlg.InitialDirectory = path;
dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension);
dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension;
bool targetInvalid;
do {
if (dlg.ShowDialog() != true) {
return null;
}
string selectedPath = Path.GetDirectoryName(dlg.FileName);
try {
targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any();
} catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) {
MessageBox.Show(
"The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.",
"Solution directory not accessible",
MessageBoxButton.OK, MessageBoxImage.Error);
targetInvalid = true;
continue;
}
if (targetInvalid) {
MessageBox.Show(
"The directory is not empty. Please select an empty directory.",
"Solution directory not empty",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
} while (targetInvalid);
return dlg.FileName;
} }
public void RefreshDecompiledView() public void RefreshDecompiledView()
{ {
try { try {

140
ILSpy/SolutionWriter.cs

@ -22,15 +22,12 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
@ -38,73 +35,23 @@ namespace ICSharpCode.ILSpy
/// An utility class that creates a Visual Studio solution containing projects for the /// An utility class that creates a Visual Studio solution containing projects for the
/// decompiled assemblies. /// decompiled assemblies.
/// </summary> /// </summary>
internal static class SolutionWriter internal class SolutionWriter
{ {
private const string SolutionExtension = ".sln";
private const string DefaultSolutionName = "Solution";
/// <summary>
/// Shows a File Selection dialog where the user can select the target file for the solution.
/// </summary>
/// <param name="path">The initial path to show in the dialog. If not specified, the 'Documents' directory
/// will be used.</param>
///
/// <returns>The full path of the selected target file, or <c>null</c> if the user canceled.</returns>
public static string SelectSolutionFile(string path)
{
if (string.IsNullOrWhiteSpace(path)) {
path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
}
SaveFileDialog dlg = new SaveFileDialog();
dlg.InitialDirectory = path;
dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension);
dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension;
bool targetInvalid;
do {
if (dlg.ShowDialog() != true) {
return null;
}
string selectedPath = Path.GetDirectoryName(dlg.FileName);
try {
targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any();
} catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is SecurityException) {
MessageBox.Show(
"The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.",
"Solution directory not accessible",
MessageBoxButton.OK, MessageBoxImage.Error);
targetInvalid = true;
continue;
}
if (targetInvalid) {
MessageBox.Show(
"The directory is not empty. Please select an empty directory.",
"Solution directory not empty",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
} while (targetInvalid);
return dlg.FileName;
}
/// <summary> /// <summary>
/// Creates a Visual Studio solution that contains projects with decompiled code /// Creates a Visual Studio solution that contains projects with decompiled code
/// of the specified <paramref name="assemblyNodes"/>. The solution file will be saved /// of the specified <paramref name="assemblies"/>. The solution file will be saved
/// to the <paramref name="solutionFilePath"/>. The directory of this file must either /// to the <paramref name="solutionFilePath"/>. The directory of this file must either
/// be empty or not exist. /// be empty or not exist.
/// </summary> /// </summary>
/// <param name="textView">A reference to the <see cref="DecompilerTextView"/> instance.</param> /// <param name="textView">A reference to the <see cref="DecompilerTextView"/> instance.</param>
/// <param name="solutionFilePath">The target file path of the solution file.</param> /// <param name="solutionFilePath">The target file path of the solution file.</param>
/// <param name="assemblyNodes">The assembly nodes to decompile.</param> /// <param name="assemblies">The assembly nodes to decompile.</param>
/// ///
/// <exception cref="ArgumentException">Thrown when <paramref name="solutionFilePath"/> is null, /// <exception cref="ArgumentException">Thrown when <paramref name="solutionFilePath"/> is null,
/// an empty or a whitespace string.</exception> /// an empty or a whitespace string.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="textView"/>> or /// <exception cref="ArgumentNullException">Thrown when <paramref name="textView"/>> or
/// <paramref name="assemblyNodes"/> is null.</exception> /// <paramref name="assemblies"/> is null.</exception>
public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, IEnumerable<AssemblyTreeNode> assemblyNodes) public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, Language language, IEnumerable<LoadedAssembly> assemblies)
{ {
if (textView == null) { if (textView == null) {
throw new ArgumentNullException(nameof(textView)); throw new ArgumentNullException(nameof(textView));
@ -114,29 +61,37 @@ namespace ICSharpCode.ILSpy
throw new ArgumentException("The solution file path cannot be null or empty.", nameof(solutionFilePath)); throw new ArgumentException("The solution file path cannot be null or empty.", nameof(solutionFilePath));
} }
if (assemblyNodes == null) { if (assemblies == null) {
throw new ArgumentNullException(nameof(assemblyNodes)); throw new ArgumentNullException(nameof(assemblies));
} }
var writer = new SolutionWriter(solutionFilePath);
textView textView
.RunWithCancellation(ct => CreateSolution(solutionFilePath, assemblyNodes, ct)) .RunWithCancellation(ct => writer.CreateSolution(assemblies, language, ct))
.Then(output => textView.ShowText(output)) .Then(output => textView.ShowText(output))
.HandleExceptions(); .HandleExceptions();
} }
private static async Task<AvalonEditTextOutput> CreateSolution( readonly string solutionFilePath;
string solutionFilePath, readonly string solutionDirectory;
IEnumerable<AssemblyTreeNode> assemblyNodes, readonly ConcurrentBag<ProjectItem> projects;
CancellationToken ct) readonly ConcurrentBag<string> statusOutput;
SolutionWriter(string solutionFilePath)
{ {
var solutionDirectory = Path.GetDirectoryName(solutionFilePath); this.solutionFilePath = solutionFilePath;
var statusOutput = new ConcurrentBag<string>(); solutionDirectory = Path.GetDirectoryName(solutionFilePath);
var projects = new ConcurrentBag<ProjectItem>(); statusOutput = new ConcurrentBag<string>();
projects = new ConcurrentBag<ProjectItem>();
}
async Task<AvalonEditTextOutput> CreateSolution(IEnumerable<LoadedAssembly> assemblies, Language language, CancellationToken ct)
{
var result = new AvalonEditTextOutput(); var result = new AvalonEditTextOutput();
var duplicates = new HashSet<string>(); var duplicates = new HashSet<string>();
if (assemblyNodes.Any(n => !duplicates.Add(n.LoadedAssembly.ShortName))) { if (assemblies.Any(asm => !duplicates.Add(asm.ShortName))) {
result.WriteLine("Duplicate assembly names selected, cannot generate a solution."); result.WriteLine("Duplicate assembly names selected, cannot generate a solution.");
return result; return result;
} }
@ -144,7 +99,7 @@ namespace ICSharpCode.ILSpy
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
try { try {
await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, projects, ct))) await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct)))
.ConfigureAwait(false); .ConfigureAwait(false);
await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects)) await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects))
@ -162,7 +117,7 @@ namespace ICSharpCode.ILSpy
result.WriteLine(e.Message); result.WriteLine(e.Message);
return true; return true;
}); });
return result; return result;
} }
@ -172,13 +127,13 @@ namespace ICSharpCode.ILSpy
if (statusOutput.Count == 0) { if (statusOutput.Count == 0) {
result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:"); result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:");
foreach (var item in assemblyNodes.Select(n => n.Text.ToString())) { foreach (var item in assemblies.Select(n => n.Text.ToString())) {
result.WriteLine(item); result.WriteLine(item);
} }
result.WriteLine(); result.WriteLine();
if (assemblyNodes.Count() == projects.Count) { if (assemblies.Count() == projects.Count) {
result.WriteLine("Created the Visual Studio Solution file."); result.WriteLine("Created the Visual Studio Solution file.");
} }
@ -191,17 +146,10 @@ namespace ICSharpCode.ILSpy
return result; return result;
} }
private static void WriteProject( void WriteProject(LoadedAssembly loadedAssembly, Language language, string targetDirectory, CancellationToken ct)
AssemblyTreeNode assemblyNode,
string targetDirectory,
ConcurrentBag<string> statusOutput,
ConcurrentBag<ProjectItem> targetContainer,
CancellationToken ct)
{ {
var loadedAssembly = assemblyNode.LoadedAssembly;
targetDirectory = Path.Combine(targetDirectory, loadedAssembly.ShortName); targetDirectory = Path.Combine(targetDirectory, loadedAssembly.ShortName);
string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + assemblyNode.Language.ProjectFileExtension); string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + language.ProjectFileExtension);
if (!Directory.Exists(targetDirectory)) { if (!Directory.Exists(targetDirectory)) {
try { try {
@ -214,21 +162,19 @@ namespace ICSharpCode.ILSpy
try { try {
using (var projectFileWriter = new StreamWriter(projectFileName)) { using (var projectFileWriter = new StreamWriter(projectFileName)) {
var projectFileOutput = new PlainTextOutput(projectFileWriter); var projectFileOutput = new PlainTextOutput(projectFileWriter);
var options = new DecompilationOptions() { var options = new DecompilationOptions() {
FullDecompilation = true, FullDecompilation = true,
CancellationToken = ct, CancellationToken = ct,
SaveAsProjectDirectory = targetDirectory }; SaveAsProjectDirectory = targetDirectory
};
if (assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options) is ProjectId projectInfo) {
targetContainer.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); var projectInfo = language.DecompileAssembly(loadedAssembly, projectFileOutput, options);
if (projectInfo != null) {
projects.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid));
} }
} }
} } catch (Exception e) when (!(e is OperationCanceledException)) {
catch (OperationCanceledException) {
throw;
}
catch (Exception e) {
statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}"); statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}");
} }
} }

4
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public Action<SharpTreeNode> Select = delegate { }; public Action<SharpTreeNode> Select = delegate { };
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, "List: " + assemblyList.ListName); language.WriteCommentLine(output, "List: " + assemblyList.ListName);
output.WriteLine(); output.WriteLine();
@ -150,8 +150,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.WriteLine(); output.WriteLine();
asm.Decompile(language, output, options); asm.Decompile(language, output, options);
} }
return true;
} }
#region Find*Node #region Find*Node

4
ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs

@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info); var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info);
if (r.IsWindowsRuntime) { if (r.IsWindowsRuntime) {
@ -98,8 +98,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.Unindent(); output.Unindent();
output.WriteLine(); output.WriteLine();
} }
return true;
} }
} }
} }

12
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Recurse; return FilterResult.Recurse;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
void HandleException(Exception ex, string message) void HandleException(Exception ex, string message)
{ {
@ -259,21 +259,21 @@ namespace ICSharpCode.ILSpy.TreeNodes
switch (ex.InnerException) { switch (ex.InnerException) {
case BadImageFormatException badImage: case BadImageFormatException badImage:
HandleException(badImage, "This file does not contain a managed assembly."); HandleException(badImage, "This file does not contain a managed assembly.");
return null; return;
case FileNotFoundException fileNotFound: case FileNotFoundException fileNotFound:
HandleException(fileNotFound, "The file was not found."); HandleException(fileNotFound, "The file was not found.");
return null; return;
case DirectoryNotFoundException dirNotFound: case DirectoryNotFoundException dirNotFound:
HandleException(dirNotFound, "The directory was not found."); HandleException(dirNotFound, "The directory was not found.");
return null; return;
case PEFileNotSupportedException notSupported: case PEFileNotSupportedException notSupported:
HandleException(notSupported, notSupported.Message); HandleException(notSupported, notSupported.Message);
return null; return;
default: default:
throw; throw;
} }
} }
return language.DecompileAssembly(LoadedAssembly, output, options); language.DecompileAssembly(LoadedAssembly, output, options);
} }
public override bool Save(DecompilerTextView textView) public override bool Save(DecompilerTextView textView)

3
ILSpy/TreeNodes/BaseTypesEntryNode.cs

@ -97,10 +97,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return false; return false;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true));
return true;
} }
IEntity IMemberTreeNode.Member { IEntity IMemberTreeNode.Member {

4
ILSpy/TreeNodes/BaseTypesTreeNode.cs

@ -69,14 +69,12 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (ILSpyTreeNode child in this.Children) { foreach (ILSpyTreeNode child in this.Children) {
child.Decompile(language, output, options); child.Decompile(language, output, options);
} }
return true;
} }
} }
} }

3
ILSpy/TreeNodes/DerivedTypesEntryNode.cs

@ -90,10 +90,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
e.Handled = BaseTypesEntryNode.ActivateItem(this, type); e.Handled = BaseTypesEntryNode.ActivateItem(this, type);
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true));
return true;
} }
IEntity IMemberTreeNode.Member => type; IEntity IMemberTreeNode.Member => type;

3
ILSpy/TreeNodes/DerivedTypesTreeNode.cs

@ -92,10 +92,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return typeRef.GetFullTypeName(referenceMetadata) == typeDef.GetFullTypeName(definitionMetadata); return typeRef.GetFullTypeName(referenceMetadata) == typeDef.GetFullTypeName(definitionMetadata);
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
threading.Decompile(language, output, options, EnsureLazyChildren); threading.Decompile(language, output, options, EnsureLazyChildren);
return true;
} }
} }
} }

3
ILSpy/TreeNodes/EventTreeNode.cs

@ -67,10 +67,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden; return FilterResult.Hidden;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.DecompileEvent(EventDefinition, output, options); language.DecompileEvent(EventDefinition, output, options);
return true;
} }
public override bool IsPublicAPI { public override bool IsPublicAPI {

3
ILSpy/TreeNodes/FieldTreeNode.cs

@ -68,10 +68,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden; return FilterResult.Hidden;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.DecompileField(FieldDefinition, output, options); language.DecompileField(FieldDefinition, output, options);
return true;
} }
public override bool IsPublicAPI { public override bool IsPublicAPI {

2
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden; return FilterResult.Hidden;
} }
public abstract object Decompile(Language language, ITextOutput output, DecompilationOptions options); public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options);
/// <summary> /// <summary>
/// Used to implement special view logic for some items. /// Used to implement special view logic for some items.

3
ILSpy/TreeNodes/MethodTreeNode.cs

@ -83,10 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.DecompileMethod(MethodDefinition, output, options); language.DecompileMethod(MethodDefinition, output, options);
return true;
} }
public override FilterResult Filter(FilterSettings settings) public override FilterResult Filter(FilterSettings settings)

3
ILSpy/TreeNodes/ModuleReferenceTreeNode.cs

@ -72,11 +72,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, moduleName); language.WriteCommentLine(output, moduleName);
language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata"); language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata");
return true;
} }
} }
} }

3
ILSpy/TreeNodes/NamespaceTreeNode.cs

@ -56,10 +56,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Recurse; return FilterResult.Recurse;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.DecompileNamespace(name, this.Children.OfType<TypeTreeNode>().Select(t => t.TypeDefinition), output, options); language.DecompileNamespace(name, this.Children.OfType<TypeTreeNode>().Select(t => t.TypeDefinition), output, options);
return true;
} }
} }
} }

3
ILSpy/TreeNodes/PropertyTreeNode.cs

@ -74,10 +74,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden; return FilterResult.Hidden;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.DecompileProperty(PropertyDefinition, output, options); language.DecompileProperty(PropertyDefinition, output, options);
return true;
} }
public override bool IsPublicAPI { public override bool IsPublicAPI {

3
ILSpy/TreeNodes/ReferenceFolderTreeNode.cs

@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata));
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, $"Detected Target-Framework-Id: {parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().Result}"); language.WriteCommentLine(output, $"Detected Target-Framework-Id: {parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().Result}");
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
@ -86,7 +86,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.Unindent(); output.Unindent();
output.WriteLine(); output.WriteLine();
} }
return true;
} }
} }
} }

4
ILSpy/TreeNodes/ResourceListTreeNode.cs

@ -64,15 +64,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Recurse; return FilterResult.Recurse;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (ILSpyTreeNode child in this.Children) { foreach (ILSpyTreeNode child in this.Children) {
child.Decompile(language, output, options); child.Decompile(language, output, options);
output.WriteLine(); output.WriteLine();
} }
return true;
} }
} }
} }

3
ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs

@ -75,10 +75,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
EnsureLazyChildren(); EnsureLazyChildren();
return true;
} }
} }
} }

3
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -73,10 +73,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return result; return result;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, string.Format("{0} = {1}", key, data)); language.WriteCommentLine(output, string.Format("{0} = {1}", key, data));
return true;
} }
public override bool Save(DecompilerTextView textView) public override bool Save(DecompilerTextView textView)

4
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden; return FilterResult.Hidden;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes)); language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes));
@ -76,8 +76,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); }); smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); });
output.WriteLine(); output.WriteLine();
} }
return true;
} }
public override bool View(DecompilerTextView textView) public override bool View(DecompilerTextView textView)

4
ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return true; return true;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
EnsureLazyChildren(); EnsureLazyChildren();
base.Decompile(language, output, options); base.Decompile(language, output, options);
@ -168,8 +168,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
} }
output.WriteLine(); output.WriteLine();
} }
return true;
} }
internal class SerializedObjectRepresentation internal class SerializedObjectRepresentation

6
ILSpy/TreeNodes/ThreadingSupport.cs

@ -128,9 +128,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Match; return FilterResult.Match;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
return false;
} }
} }
@ -152,9 +151,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Match; return FilterResult.Match;
} }
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
return false;
} }
} }

3
ILSpy/TreeNodes/TypeTreeNode.cs

@ -103,10 +103,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool CanExpandRecursively => true; public override bool CanExpandRecursively => true;
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{ {
language.DecompileType(TypeDefinition, output, options); language.DecompileType(TypeDefinition, output, options);
return true;
} }
public override object Icon => GetIcon(TypeDefinition); public override object Icon => GetIcon(TypeDefinition);

Loading…
Cancel
Save