Browse Source

Remove WPF ILSpy host (prep for Avalonia rewrite)

Strip the WPF ILSpy/, ILSpy.Tests/, and ILSpy.BamlDecompiler/ projects
and their solution entries so this commit can serve as the base for
replaying the rewritten Avalonia history on top. The BAML plugin needed
UseWpf + AvalonEdit and had a ProjectReference to ILSpy/ILSpy.csproj;
once both are gone it cannot build and would block bisect for every
intermediate commit. The Avalonia main app will absorb its three exports
(factory, file handler, entry node) in the later "BAML to XAML
decompiler integration" commit.
pull/3755/head
Siegfried Pammer 4 months ago
parent
commit
b729995435
  1. 4
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  2. 2
      ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj
  3. 0
      ICSharpCode.ILSpyX/MermaidDiagrammer/html/ILSpy.ico
  4. 2
      ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj
  5. 76
      ILSpy.BamlDecompiler/BamlResourceEntryNode.cs
  6. 85
      ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs
  7. 35
      ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj
  8. 36
      ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs
  9. 9
      ILSpy.BamlDecompiler/Properties/launchSettings.json
  10. 66
      ILSpy.Tests/Analyzers/AnalyzerScopeTests.cs
  11. 43
      ILSpy.Tests/Analyzers/ExportAnalyzerAttributeTests.cs
  12. 190
      ILSpy.Tests/Analyzers/MemberImplementsInterfaceAnalyzerTests.cs
  13. 52
      ILSpy.Tests/Analyzers/MethodUsesAnalyzerTests.cs
  14. 21
      ILSpy.Tests/Analyzers/TestCases/MainAssembly.cs
  15. 61
      ILSpy.Tests/Analyzers/TypeUsedByAnalyzerTests.cs
  16. BIN
      ILSpy.Tests/BitmapContainer.resources
  17. 127
      ILSpy.Tests/CommandLineArgumentsTests.cs
  18. 91
      ILSpy.Tests/ILSpy.Tests.csproj
  19. BIN
      ILSpy.Tests/Icon.ico
  20. 124
      ILSpy.Tests/IconContainer.resx
  21. 232
      ILSpy.Tests/ResourceReaderWriterTests.cs
  22. BIN
      ILSpy.Tests/Test.resources
  23. 109
      ILSpy.Tests/UpdateServiceTests.cs
  24. 39
      ILSpy.sln
  25. 241
      ILSpy/AboutPage.cs
  26. 93
      ILSpy/Analyzers/AnalyzeCommand.cs
  27. 78
      ILSpy/Analyzers/AnalyzerEntityTreeNode.cs
  28. 59
      ILSpy/Analyzers/AnalyzerRootNode.cs
  29. 136
      ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
  30. 62
      ILSpy/Analyzers/AnalyzerTreeNode.cs
  31. 22
      ILSpy/Analyzers/AnalyzerTreeView.xaml
  32. 50
      ILSpy/Analyzers/AnalyzerTreeView.xaml.cs
  33. 134
      ILSpy/Analyzers/AnalyzerTreeViewModel.cs
  34. 58
      ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs
  35. 51
      ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs
  36. 40
      ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs
  37. 85
      ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs
  38. 60
      ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs
  39. 62
      ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs
  40. 94
      ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs
  41. 68
      ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs
  42. 59
      ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs
  43. 39
      ILSpy/App.xaml
  44. 277
      ILSpy/App.xaml.cs
  45. 9
      ILSpy/AppEnv/AppEnvironment.cs
  46. 119
      ILSpy/AppEnv/CommandLineArguments.cs
  47. 241
      ILSpy/AppEnv/CommandLineTools.cs
  48. 286
      ILSpy/AppEnv/SingleInstance.cs
  49. 55
      ILSpy/AssemblyTree/AssemblyListPane.xaml
  50. 86
      ILSpy/AssemblyTree/AssemblyListPane.xaml.cs
  51. 1111
      ILSpy/AssemblyTree/AssemblyTreeModel.cs
  52. 83
      ILSpy/AssemblyTree/AssemblyTreeModel.wpf.cs
  53. 168
      ILSpy/AvalonEdit/ITextMarker.cs
  54. 372
      ILSpy/AvalonEdit/TextMarkerService.cs
  55. 65
      ILSpy/Commands/BrowseBackCommand.cs
  56. 65
      ILSpy/Commands/BrowseForwardCommand.cs
  57. 34
      ILSpy/Commands/CheckForUpdatesCommand.cs
  58. 66
      ILSpy/Commands/CommandWrapper.cs
  59. 53
      ILSpy/Commands/CompareContextMenuEntry.cs
  60. 50
      ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs
  61. 124
      ILSpy/Commands/CreateDiagramContextMenuEntry.cs
  62. 122
      ILSpy/Commands/DecompileAllCommand.cs
  63. 72
      ILSpy/Commands/DecompileCommand.cs
  64. 96
      ILSpy/Commands/DecompileInNewViewCommand.cs
  65. 73
      ILSpy/Commands/DelegateCommand.cs
  66. 89
      ILSpy/Commands/DisassembleAllCommand.cs
  67. 33
      ILSpy/Commands/ExitCommand.cs
  68. 113
      ILSpy/Commands/ExportCommandAttribute.cs
  69. 237
      ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs
  70. 210
      ILSpy/Commands/GeneratePdbContextMenuEntry.cs
  71. 30
      ILSpy/Commands/IProtocolHandler.cs
  72. 46
      ILSpy/Commands/ManageAssemblyListsCommand.cs
  73. 57
      ILSpy/Commands/OpenCommand.cs
  74. 48
      ILSpy/Commands/OpenFromGacCommand.cs
  75. 97
      ILSpy/Commands/Pdb2XmlCommand.cs
  76. 45
      ILSpy/Commands/RefreshCommand.cs
  77. 70
      ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs
  78. 140
      ILSpy/Commands/SaveCodeContextMenuEntry.cs
  79. 45
      ILSpy/Commands/SaveCommand.cs
  80. 96
      ILSpy/Commands/ScopeSearchToAssembly.cs
  81. 94
      ILSpy/Commands/ScopeSearchToNamespace.cs
  82. 141
      ILSpy/Commands/SearchMsdnContextMenuEntry.cs
  83. 67
      ILSpy/Commands/SelectPdbContextMenuEntry.cs
  84. 18
      ILSpy/Commands/SetThemeCommand.cs
  85. 77
      ILSpy/Commands/ShowCFGContextMenuEntry.cs
  86. 25
      ILSpy/Commands/ShowPane.cs
  87. 51
      ILSpy/Commands/SimpleCommand.cs
  88. 52
      ILSpy/Commands/SortAssemblyListCommand.cs
  89. 375
      ILSpy/ContextMenuEntry.cs
  90. 209
      ILSpy/Controls/CollapsiblePanel.cs
  91. 47
      ILSpy/Controls/CultureSelectionConverter.cs
  92. 169
      ILSpy/Controls/CustomDialog.cs
  93. 80
      ILSpy/Controls/ExtensionMethods.cs
  94. 108
      ILSpy/Controls/GridViewColumnAutoSize.cs
  95. 53
      ILSpy/Controls/MainMenu.xaml
  96. 212
      ILSpy/Controls/MainMenu.xaml.cs
  97. 88
      ILSpy/Controls/MainToolBar.xaml
  98. 237
      ILSpy/Controls/MainToolBar.xaml.cs
  99. 69
      ILSpy/Controls/MarkupExtensions.cs
  100. 82
      ILSpy/Controls/ResourceObjectTable.xaml
  101. Some files were not shown because too many files have changed in this diff Show More

4
ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj

@ -11,6 +11,10 @@ @@ -11,6 +11,10 @@
<ItemGroup>
<PackageReference Include="PowerShellStandard.Library" />
<PackageReference Include="Mono.Cecil" />
<!-- Unify System.Memory with the version ICSharpCode.Decompiler binds to;
without this, PowerShellStandard.Library's transitive 4.5.5 wins as
"primary" and conflicts with Decompiler.dll's 4.6.x binding (MSB3277). -->
<PackageReference Include="System.Memory" />
</ItemGroup>
<ItemGroup>

2
ICSharpCode.ILSpyX/ICSharpCode.ILSpyX.csproj

@ -81,7 +81,7 @@ @@ -81,7 +81,7 @@
<None Remove="MermaidDiagrammer\html\package-lock.json" />
<None Remove="MermaidDiagrammer\html\package.json" />
<None Remove="MermaidDiagrammer\html\styles.less" />
<EmbeddedResource Include="..\ILSpy\Images\ILSpy.ico" Link="MermaidDiagrammer\html\ILSpy.ico" />
<EmbeddedResource Include="MermaidDiagrammer\html\ILSpy.ico" />
<EmbeddedResource Include="MermaidDiagrammer\html\script.js" />
<EmbeddedResource Include="MermaidDiagrammer\html\styles.css" />
<EmbeddedResource Include="MermaidDiagrammer\html\template.html" />

0
ILSpy/Images/ILSpy.ico → ICSharpCode.ILSpyX/MermaidDiagrammer/html/ILSpy.ico

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

2
ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj

@ -46,8 +46,6 @@ @@ -46,8 +46,6 @@
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.Decompiler.Tests\ICSharpCode.Decompiler.Tests.csproj" />
<ProjectReference Include="..\ILSpy.BamlDecompiler\ILSpy.BamlDecompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj" />
</ItemGroup>
<ItemGroup>

76
ILSpy.BamlDecompiler/BamlResourceEntryNode.cs

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
// Copyright (c) 2020 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;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.ILSpy;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
namespace ILSpy.BamlDecompiler
{
public sealed class BamlResourceEntryNode : ResourceEntryNode
{
public BamlResourceEntryNode(string key, Func<Stream> data) : base(key, data)
{
}
public override bool View(TabPageModel tabPage)
{
IHighlightingDefinition highlighting = null;
tabPage.SupportsLanguageSwitching = false;
tabPage.ShowTextView(textView => textView.RunWithCancellation(
token => Task.Factory.StartNew(
() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
try
{
LoadBaml(output, token);
highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml");
}
catch (Exception ex)
{
output.Write(ex.ToString());
}
return output;
}, token))
.Then(output => textView.ShowNode(output, this, highlighting))
.HandleExceptions());
return true;
}
void LoadBaml(AvalonEditTextOutput output, CancellationToken cancellationToken)
{
var asm = this.Ancestors().OfType<AssemblyTreeNode>().First().LoadedAssembly;
using var data = OpenStream();
BamlDecompilerTypeSystem typeSystem = new BamlDecompilerTypeSystem(asm.GetMetadataFileOrNull(), asm.GetAssemblyResolver());
var decompiler = new XamlDecompiler(typeSystem, new BamlDecompilerSettings());
decompiler.CancellationToken = cancellationToken;
var result = decompiler.Decompile(data);
output.Write(result.Xaml.ToString());
}
}
}

85
ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs

@ -1,85 +0,0 @@ @@ -1,85 +0,0 @@
// Copyright (c) 2020 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.Composition;
using System.IO;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Abstractions;
namespace ILSpy.BamlDecompiler
{
[Export(typeof(IResourceNodeFactory))]
[Shared]
public sealed class BamlResourceNodeFactory : IResourceNodeFactory
{
public ITreeNode CreateNode(Resource resource)
{
if (resource.Name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase))
return new BamlResourceEntryNode(resource.Name, resource.TryOpenStream);
else
return null;
}
}
[Export(typeof(IResourceFileHandler))]
[Shared]
public sealed class BamlResourceFileHandler : IResourceFileHandler
{
public string EntryType => "Page";
public bool CanHandle(string name, ResourceFileHandlerContext context) => name.EndsWith(".baml", StringComparison.OrdinalIgnoreCase);
public string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, ResourceFileHandlerContext context)
{
BamlDecompilerTypeSystem typeSystem = new BamlDecompilerTypeSystem(assembly.GetMetadataFileOrNull(), assembly.GetAssemblyResolver());
var decompiler = new XamlDecompiler(typeSystem, new BamlDecompilerSettings() {
ThrowOnAssemblyResolveErrors = context.DecompilationOptions.DecompilerSettings.ThrowOnAssemblyResolveErrors
});
decompiler.CancellationToken = context.DecompilationOptions.CancellationToken;
var result = decompiler.Decompile(stream);
var typeDefinition = result.TypeName.HasValue ? typeSystem.MainModule.GetTypeDefinition(result.TypeName.Value.TopLevelTypeName) : null;
if (typeDefinition != null)
{
fileName = WholeProjectDecompiler.SanitizeFileName(typeDefinition.ReflectionName + ".xaml");
var partialTypeInfo = new PartialTypeInfo(typeDefinition);
foreach (var member in result.GeneratedMembers)
{
partialTypeInfo.AddDeclaredMember(member);
}
context.AddPartialTypeInfo(partialTypeInfo);
}
else
{
fileName = Path.ChangeExtension(fileName, ".xaml");
}
context.AdditionalProperties.Add("Generator", "MSBuild:Compile");
context.AdditionalProperties.Add("SubType", "Designer");
string saveFileName = Path.Combine(context.DecompilationOptions.SaveAsProjectDirectory, fileName);
Directory.CreateDirectory(Path.GetDirectoryName(saveFileName));
result.Xaml.Save(saveFileName);
return fileName;
}
}
}

35
ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj

@ -1,35 +0,0 @@ @@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>ILSpy.BamlDecompiler.Plugin</AssemblyName>
<TargetFramework>net10.0-windows</TargetFramework>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<IsPackable>false</IsPackable>
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<BaseAddress>6488064</BaseAddress>
<UseWpf>true</UseWpf>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\ILSpy\bin\$(Configuration)\</OutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.BamlDecompiler\ICSharpCode.BamlDecompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj" />
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
</ItemGroup>
</Project>

36
ILSpy.BamlDecompiler/Properties/AssemblyInfo.cs

@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
#region Using directives
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
#endregion
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ILSpy.BamlDecompiler.Plugin")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ILSpy.BamlDecompiler.Plugin")]
[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
[assembly: TargetPlatform("Windows10.0")]
[assembly: SupportedOSPlatform("Windows7.0")]
[assembly: InternalsVisibleTo("ILSpy.BamlDecompiler.Tests")]
// The assembly version has following format :
//
// Major.Minor.Build.Revision
//
// You can specify all the values or you can use the default the Revision and
// Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]

9
ILSpy.BamlDecompiler/Properties/launchSettings.json

@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
{
"profiles": {
"ILSpy.BamlDecompiler": {
"commandName": "Executable",
"executablePath": "$(SolutionDir)\\ILSpy\\bin\\Debug\\net6.0-windows\\ILSpy.exe",
"commandLineArgs": "/separate"
}
}
}

66
ILSpy.Tests/Analyzers/AnalyzerScopeTests.cs

@ -1,66 +0,0 @@ @@ -1,66 +0,0 @@
// Copyright (c) 2024 Yuriy Zatuchnyy
//
// 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.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX.Analyzers;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests.Analyzers
{
[TestFixture]
public class AnalyzerScopeTests
{
public class TestClass
{
}
[Test]
public void WhenPublicNestedClass_ThenNotInfiniteLoop()
{
// Given
ILSpyX.AssemblyList assemblyList = new ILSpyX.AssemblyList();
var file = new PEFile(this.GetType().Assembly.Location);
var td = file.Metadata.TypeDefinitions.First(td => td.GetFullTypeName(file.Metadata).Name == nameof(TestClass));
Decompiler.Metadata.IAssemblyResolver assemblyResolver = new UniversalAssemblyResolver(null, false, null);
ICompilation compilation = new DecompilerTypeSystem(file, assemblyResolver);
ITypeResolveContext context = new CSharpResolver(compilation);
var module = ((IModuleReference)file).Resolve(context) as MetadataModule;
IEntity entity = module.GetDefinition(td);
// When
var task = Task.Run(() => {
var target = new AnalyzerScope(assemblyList, entity);
});
var result = Task.WaitAny(new[] { task, Task.Delay(500) }); // 0.5 seconds
// Then
Assert.That(result == 0, "The constructor should complete in less than 10 seconds");
}
}
}

43
ILSpy.Tests/Analyzers/ExportAnalyzerAttributeTests.cs

@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
// Copyright (c) 2024 Andreas Weizel
//
// 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.Linq;
using ICSharpCode.ILSpyX.Analyzers;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests.Analyzers
{
[TestFixture]
public class ExportAnalyzerAttributeTests
{
[Test]
public void CollectAnalyzers()
{
var analyzerNames = ExportAnalyzerAttribute.GetAnnotatedAnalyzers()
.Select(analyzer => analyzer.AnalyzerType.Name)
.ToArray();
Assert.That(analyzerNames.Contains("AttributeAppliedToAnalyzer"));
Assert.That(analyzerNames.Contains("EventImplementedByAnalyzer"));
Assert.That(analyzerNames.Contains("MethodUsedByAnalyzer"));
Assert.That(analyzerNames.Contains("PropertyOverriddenByAnalyzer"));
Assert.That(analyzerNames.Contains("TypeInstantiatedByAnalyzer"));
}
}
}

190
ILSpy.Tests/Analyzers/MemberImplementsInterfaceAnalyzerTests.cs

@ -1,190 +0,0 @@ @@ -1,190 +0,0 @@
// Copyright (c) 2020 Siegfried Pammer
//
// 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;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.ILSpyX.Analyzers;
using ICSharpCode.ILSpyX.Analyzers.Builtin;
using NSubstitute;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests.Analyzers
{
[TestFixture]
public class MemberImplementsInterfaceAnalyzerTests
{
static readonly SymbolKind[] ValidSymbolKinds = { SymbolKind.Event, SymbolKind.Indexer, SymbolKind.Method, SymbolKind.Property };
static readonly SymbolKind[] InvalidSymbolKinds =
Enum.GetValues(typeof(SymbolKind)).Cast<SymbolKind>().Except(ValidSymbolKinds).ToArray();
static readonly TypeKind[] ValidTypeKinds = { TypeKind.Class, TypeKind.Struct };
static readonly TypeKind[] InvalidTypeKinds = Enum.GetValues(typeof(TypeKind)).Cast<TypeKind>().Except(ValidTypeKinds).ToArray();
private ICompilation testAssembly;
[OneTimeSetUp]
public void Setup()
{
string fileName = GetType().Assembly.Location;
using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage, MetadataReaderOptions.None);
testAssembly = new SimpleCompilation(module.WithOptions(TypeSystemOptions.Default), MinimalCorlib.Instance);
}
}
[Test]
public void VerifyDoesNotShowForNoSymbol()
{
// Arrange
var analyzer = new MemberImplementsInterfaceAnalyzer();
// Act
var shouldShow = analyzer.Show(symbol: null);
// Assert
Assert.That(!shouldShow, $"The analyzer will be unexpectedly shown for no symbol");
}
[Test]
[TestCaseSource(nameof(InvalidSymbolKinds))]
public void VerifyDoesNotShowForNonMembers(SymbolKind symbolKind)
{
// Arrange
var symbolMock = Substitute.For<ISymbol>();
symbolMock.SymbolKind.Returns(symbolKind);
var analyzer = new MemberImplementsInterfaceAnalyzer();
// Act
var shouldShow = analyzer.Show(symbolMock);
// Assert
Assert.That(!shouldShow, $"The analyzer will be unexpectedly shown for symbol '{symbolKind}'");
}
[Test]
[TestCaseSource(nameof(ValidSymbolKinds))]
public void VerifyDoesNotShowForStaticMembers(SymbolKind symbolKind)
{
// Arrange
var memberMock = SetupMemberMock(symbolKind, TypeKind.Unknown, isStatic: true);
var analyzer = new MemberImplementsInterfaceAnalyzer();
// Act
var shouldShow = analyzer.Show(memberMock);
// Assert
Assert.That(!shouldShow, $"The analyzer will be unexpectedly shown for static symbol '{symbolKind}'");
}
[Test]
[Pairwise]
public void VerifyDoesNotShowForUnsupportedTypes(
[ValueSource(nameof(ValidSymbolKinds))] SymbolKind symbolKind,
[ValueSource(nameof(InvalidTypeKinds))] TypeKind typeKind)
{
// Arrange
var memberMock = SetupMemberMock(symbolKind, typeKind, isStatic: true);
var analyzer = new MemberImplementsInterfaceAnalyzer();
// Act
var shouldShow = analyzer.Show(memberMock);
// Assert
Assert.That(!shouldShow, $"The analyzer will be unexpectedly shown for symbol '{symbolKind}' and '{typeKind}'");
}
[Test]
[Pairwise]
public void VerifyShowsForSupportedTypes(
[ValueSource(nameof(ValidSymbolKinds))] SymbolKind symbolKind,
[ValueSource(nameof(ValidTypeKinds))] TypeKind typeKind)
{
// Arrange
var memberMock = SetupMemberMock(symbolKind, typeKind, isStatic: false);
var analyzer = new MemberImplementsInterfaceAnalyzer();
// Act
var shouldShow = analyzer.Show(memberMock);
// Assert
Assert.That(shouldShow, $"The analyzer will not be shown for symbol '{symbolKind}' and '{typeKind}'");
}
[Test]
public void VerifyReturnsOnlyInterfaceMembers()
{
// Arrange
var symbol = SetupSymbolForAnalysis(typeof(TestClass), nameof(TestClass.TestMethod));
var analyzer = new MemberImplementsInterfaceAnalyzer();
// Act
var results = analyzer.Analyze(symbol, new AnalyzerContext() { AssemblyList = new ILSpyX.AssemblyList(), Language = new CSharpLanguage() });
// Assert
Assert.That(results, Is.Not.Null);
Assert.That(results.Count(), Is.EqualTo(1));
var result = results.FirstOrDefault() as IMethod;
Assert.That(result, Is.Not.Null);
Assert.That(result.DeclaringTypeDefinition, Is.Not.Null);
Assert.That(result.DeclaringTypeDefinition.Kind, Is.EqualTo(TypeKind.Interface));
Assert.That(result.DeclaringTypeDefinition.Name, Is.EqualTo(nameof(ITestInterface)));
}
private ISymbol SetupSymbolForAnalysis(Type type, string methodName)
{
var typeDefinition = testAssembly.FindType(type).GetDefinition();
return typeDefinition.Methods.First(m => m.Name == methodName);
}
private static IMember SetupMemberMock(SymbolKind symbolKind, TypeKind typeKind, bool isStatic)
{
var memberMock = Substitute.For<IMember>();
memberMock.SymbolKind.Returns(symbolKind);
memberMock.DeclaringTypeDefinition.Kind.Returns(typeKind);
memberMock.IsStatic.Returns(isStatic);
return memberMock;
}
private interface ITestInterface
{
void TestMethod();
}
private class BaseClass
{
public virtual void TestMethod() => throw new NotImplementedException();
}
private class TestClass : BaseClass, ITestInterface
{
public override void TestMethod() => throw new NotImplementedException();
}
}
}

52
ILSpy.Tests/Analyzers/MethodUsesAnalyzerTests.cs

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Analyzers;
using ICSharpCode.ILSpyX.Analyzers.Builtin;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests.Analyzers
{
[TestFixture]
public class MethodUsesAnalyzerTests
{
AssemblyList assemblyList;
CSharpLanguage language;
LoadedAssembly testAssembly;
ICompilation testAssemblyTypeSystem;
ITypeDefinition typeDefinition;
[OneTimeSetUp]
public void Setup()
{
assemblyList = new AssemblyList();
testAssembly = assemblyList.OpenAssembly(typeof(MethodUsesAnalyzerTests).Assembly.Location);
assemblyList.OpenAssembly(typeof(void).Assembly.Location);
testAssemblyTypeSystem = testAssembly.GetTypeSystemOrNull();
language = new CSharpLanguage();
typeDefinition = testAssemblyTypeSystem.FindType(typeof(TestCases.Main.MainAssembly)).GetDefinition();
}
[Test]
public void MainAssemblyUsesSystemStringEmpty()
{
var context = new AnalyzerContext { AssemblyList = assemblyList, Language = language };
IMethod symbol = typeDefinition.Methods.First(m => m.Name == "UsesSystemStringEmpty");
var results = new MethodUsesAnalyzer().Analyze(symbol, context).ToList();
Assert.That(results.Count == 1);
var field = results.Single() as IField;
Assert.That(field, Is.Not.Null);
Assert.That(!field.MetadataToken.IsNil);
Assert.That("System.String.Empty", Is.EqualTo(field.FullName));
}
}
}

21
ILSpy.Tests/Analyzers/TestCases/MainAssembly.cs

@ -1,21 +0,0 @@ @@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.ILSpy.Tests.Analyzers.TestCases.Main
{
class MainAssembly
{
public string UsesSystemStringEmpty()
{
return string.Empty;
}
public int UsesInt32()
{
return int.Parse("1234");
}
}
}

61
ILSpy.Tests/Analyzers/TypeUsedByAnalyzerTests.cs

@ -1,61 +0,0 @@ @@ -1,61 +0,0 @@
// Copyright (c) 2020 Siegfried Pammer
//
// 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.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Analyzers;
using ICSharpCode.ILSpyX.Analyzers.Builtin;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests.Analyzers
{
[TestFixture]
public class TypeUsedByAnalyzerTests
{
AssemblyList assemblyList;
CSharpLanguage language;
LoadedAssembly testAssembly;
ICompilation testAssemblyTypeSystem;
[OneTimeSetUp]
public void Setup()
{
assemblyList = new AssemblyList();
testAssembly = assemblyList.OpenAssembly(typeof(MethodUsesAnalyzerTests).Assembly.Location);
testAssemblyTypeSystem = new DecompilerTypeSystem(testAssembly.GetMetadataFileOrNull(), testAssembly.GetAssemblyResolver());
language = new CSharpLanguage();
}
[Test]
public void SystemInt32UsedByMainAssembly()
{
var context = new AnalyzerContext { AssemblyList = assemblyList, Language = language };
var symbol = testAssemblyTypeSystem.FindType(typeof(int)).GetDefinition();
var results = new TypeUsedByAnalyzer().Analyze(symbol, context).ToList();
Assert.That(results, Is.Not.Empty);
var method = results.OfType<IMethod>().SingleOrDefault(m => m.FullName == "ICSharpCode.ILSpy.Tests.Analyzers.TestCases.Main.MainAssembly.UsesInt32");
Assert.That(method, Is.Not.Null);
Assert.That(!method.MetadataToken.IsNil);
}
}
}

BIN
ILSpy.Tests/BitmapContainer.resources

Binary file not shown.

127
ILSpy.Tests/CommandLineArgumentsTests.cs

@ -1,127 +0,0 @@ @@ -1,127 +0,0 @@
using System;
using AwesomeAssertions;
using ICSharpCode.ILSpy.AppEnv;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests
{
[TestFixture]
public class CommandLineArgumentsTests
{
[Test]
public void VerifyEmptyArgumentsArray()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { });
cmdLineArgs.AssembliesToLoad.Should().BeEmpty();
cmdLineArgs.SingleInstance.Should().BeNull();
cmdLineArgs.NavigateTo.Should().BeNull();
cmdLineArgs.Search.Should().BeNull();
cmdLineArgs.Language.Should().BeNull();
cmdLineArgs.NoActivate.Should().BeFalse();
cmdLineArgs.ConfigFile.Should().BeNull();
}
[Test]
public void VerifyHelpOption()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--help" });
cmdLineArgs.ArgumentsParser.IsShowingInformation.Should().BeTrue();
}
[Test]
public void VerifyForceNewInstanceOption()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--newinstance" });
cmdLineArgs.SingleInstance.Should().NotBeNull();
cmdLineArgs.SingleInstance.Value.Should().BeFalse();
}
[Test]
public void VerifyNavigateToOption()
{
const string navigateTo = "MyNamespace.MyClass";
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--navigateto", navigateTo });
cmdLineArgs.NavigateTo.Should().Be(navigateTo);
}
[Test]
public void VerifyNavigateToOption_NoneTest_Matching_VSAddin()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--navigateto:none" });
cmdLineArgs.NavigateTo.Should().Be("none");
}
[Test]
public void VerifyCaseSensitivityOfOptionsDoesntThrow()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--navigateTo:none" });
cmdLineArgs.ArgumentsParser.RemainingArguments.Count.Should().Be(1);
}
[Test]
public void VerifySearchOption()
{
const string searchWord = "TestContainers";
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--search", searchWord });
cmdLineArgs.Search.Should().Be(searchWord);
}
[Test]
public void VerifyLanguageOption()
{
const string language = "csharp";
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--language", language });
cmdLineArgs.Language.Should().Be(language);
}
[Test]
public void VerifyConfigOption()
{
const string configFile = "myilspyoptions.xml";
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--config", configFile });
cmdLineArgs.ConfigFile.Should().Be(configFile);
}
[Test]
public void VerifyNoActivateOption()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { "--noactivate" });
cmdLineArgs.NoActivate.Should().BeTrue();
}
[Test]
public void MultipleAssembliesAsArguments()
{
var cmdLineArgs = CommandLineArguments.Create(new string[] { "assembly1", "assembly2", "assembly3" });
cmdLineArgs.AssembliesToLoad.Count.Should().Be(3);
}
[Test]
public void PassAtFileArguments()
{
string filepath = System.IO.Path.GetTempFileName();
System.IO.File.WriteAllText(filepath, "assembly1\r\nassembly2\r\nassembly3\r\n--newinstance\r\n--noactivate");
var cmdLineArgs = CommandLineArguments.Create(new string[] { $"@{filepath}" });
try
{
System.IO.File.Delete(filepath);
}
catch (Exception)
{
}
cmdLineArgs.SingleInstance.Should().NotBeNull();
cmdLineArgs.SingleInstance.Value.Should().BeFalse();
cmdLineArgs.NoActivate.Should().BeTrue();
cmdLineArgs.AssembliesToLoad.Count.Should().Be(3);
}
}
}

91
ILSpy.Tests/ILSpy.Tests.csproj

@ -1,91 +0,0 @@ @@ -1,91 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsWindowsX64 Condition="$([MSBuild]::IsOsPlatform('Windows')) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == X64">true</IsWindowsX64>
<IsWindowsARM64 Condition="$([MSBuild]::IsOsPlatform('Windows')) And $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) == ARM64">true</IsWindowsARM64>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net11.0-windows</TargetFramework>
<IsPackable>false</IsPackable>
<RuntimeIdentifier Condition="$(IsWindowsX64) == true">win-x64</RuntimeIdentifier>
<RuntimeIdentifier Condition="$(IsWindowsARM64) == true">win-arm64</RuntimeIdentifier>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoWarn>$(NoWarn);1701;1702;1705,67,169,1058,728,1720,649,168,251</NoWarn>
<EnableDefaultItems>false</EnableDefaultItems>
<RootNamespace>ICSharpCode.ILSpy.Tests</RootNamespace>
<AutoGenerateBindingRedirects>True</AutoGenerateBindingRedirects>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NET46;ROSLYN;CS60;CS70</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="Analyzers\ExportAnalyzerAttributeTests.cs" />
<Compile Include="Analyzers\AnalyzerScopeTests.cs" />
<Compile Include="Analyzers\MemberImplementsInterfaceAnalyzerTests.cs" />
<Compile Include="Analyzers\MethodUsesAnalyzerTests.cs" />
<Compile Include="Analyzers\TestCases\MainAssembly.cs" />
<Compile Include="Analyzers\TypeUsedByAnalyzerTests.cs" />
<Compile Include="CommandLineArgumentsTests.cs" />
<Compile Include="ResourceReaderWriterTests.cs" />
<Compile Include="UpdateServiceTests.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Test.resources" />
<EmbeddedResource Include="IconContainer.resx">
<Generator>ResXCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="BitmapContainer.resources" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DiffLib" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="NSubstitute.Analyzers.CSharp">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Collections.Immutable" />
<PackageReference Include="System.Reflection.Metadata" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="coverlet.MTP" />
<PackageReference Include="NUnit" />
<PackageReference Include="AwesomeAssertions" />
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport" />
<PackageReference Include="Microsoft.Testing.Extensions.VSTestBridge" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj" />
</ItemGroup>
</Project>

BIN
ILSpy.Tests/Icon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

124
ILSpy.Tests/IconContainer.resx

@ -1,124 +0,0 @@ @@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

232
ILSpy.Tests/ResourceReaderWriterTests.cs

@ -1,232 +0,0 @@ @@ -1,232 +0,0 @@
// Copyright (c) 2023 Siegfried Pammer
//
// 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.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using ICSharpCode.Decompiler.Util;
using NUnit.Framework;
using NUnit.Framework.Internal;
using TomsToolbox.Essentials;
namespace ICSharpCode.ILSpy.Tests
{
[TestFixture]
public class ResourceReaderWriterTests
{
const string winFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
const string msCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
const string drawingAssemblyName = ", System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
[Serializable]
public class SerializableClass
{
public string Name { get; set; }
public int Age { get; set; }
}
static readonly object[][] testWriteCases = {
new object[] { "Decimal", 1.0m, "1.0", "System.Decimal" + msCorLibAssemblyName },
new object[] { "TimeSpan", TimeSpan.FromSeconds(42), "00:00:42", "System.TimeSpan" + msCorLibAssemblyName },
new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture), "06/18/2023 21:36:30", "System.DateTime" + msCorLibAssemblyName },
};
static readonly object[][] testReadCases = {
new object[] { "Decimal", 1.0m },
new object[] { "TimeSpan", TimeSpan.FromSeconds(42) },
new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture) },
};
static Stream GetResource(string fileName)
{
return typeof(ResourceReaderWriterTests).Assembly.GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + "." + fileName);
}
static MemoryStream ProduceResourcesTestFile<T>(string name, T value)
{
var ms = new MemoryStream();
var writer = new System.Resources.ResourceWriter(ms);
writer.AddResource(name, value);
writer.Generate();
ms.Position = 0;
return ms;
}
static XElement ProduceResXTest<T>(string name, T value)
{
using var ms = new MemoryStream();
var writer = new Decompiler.Util.ResXResourceWriter(ms);
writer.AddResource(name, value);
writer.Generate();
ms.Position = 0;
var doc = XDocument.Load(ms);
return doc.XPathSelectElement(".//data");
}
[TestCase("Null", null)]
[TestCase("String", "Hello World!")]
[TestCase("Char", 'A')]
[TestCase("Bool", true)]
[TestCase("Bool", false)]
[TestCase("Byte", (byte)1)]
[TestCase("SByte", (sbyte)-1)]
[TestCase("Int16", (short)1)]
[TestCase("UInt16", (ushort)1)]
[TestCase("Int32", 1)]
[TestCase("UInt32", (uint)1)]
[TestCase("Int64", (long)1)]
[TestCase("UInt64", (ulong)1)]
[TestCase("Single", 1.0f)]
[TestCase("Double", 1.0d)]
[TestCase("Bytes", new byte[] { 42, 43, 44 })]
[TestCaseSource(nameof(testReadCases))]
public void Read(string name, object value)
{
using var testFile = ProduceResourcesTestFile(name, value);
using var reader = new ResourcesFile(testFile);
var items = reader.ToArray();
Assert.That(items.Length, Is.EqualTo(1));
Assert.That(items[0].Key, Is.EqualTo(name));
Assert.That(items[0].Value, Is.EqualTo(value));
}
[TestCase("Null", null, null, "System.Resources.ResXNullRef" + winFormsAssemblyName)]
[TestCase("String", "Hello World!", "Hello World!", null)]
[TestCase("Bool", true, "True", "System.Boolean" + msCorLibAssemblyName)]
[TestCase("Bool", false, "False", "System.Boolean" + msCorLibAssemblyName)]
[TestCase("Char", 'A', "A", "System.Char" + msCorLibAssemblyName)]
[TestCase("Byte", (byte)1, "1", "System.Byte" + msCorLibAssemblyName)]
[TestCase("SByte", (sbyte)-1, "-1", "System.SByte" + msCorLibAssemblyName)]
[TestCase("Int16", (short)1, "1", "System.Int16" + msCorLibAssemblyName)]
[TestCase("UInt16", (ushort)1, "1", "System.UInt16" + msCorLibAssemblyName)]
[TestCase("Int32", 1, "1", "System.Int32" + msCorLibAssemblyName)]
[TestCase("UInt32", (uint)1, "1", "System.UInt32" + msCorLibAssemblyName)]
[TestCase("Int64", (long)1, "1", "System.Int64" + msCorLibAssemblyName)]
[TestCase("UInt64", (ulong)1, "1", "System.UInt64" + msCorLibAssemblyName)]
[TestCase("Single", 1.0f, "1", "System.Single" + msCorLibAssemblyName)]
[TestCase("Double", 1.0d, "1", "System.Double" + msCorLibAssemblyName)]
[TestCaseSource(nameof(testWriteCases))]
public void Write(string name, object value, string serializedValue, string typeName)
{
var element = ProduceResXTest(name, value);
Assert.That(element.Attribute("name")?.Value, Is.EqualTo(name));
if (typeName != null)
{
Assert.That(element.Attribute("type")?.Value, Is.EqualTo(typeName));
}
var v = element.Element("value");
Assert.That(v, Is.Not.Null);
Assert.That(v.IsEmpty ? serializedValue == null : v.Value == serializedValue);
}
[Test]
public void ResXSerializableClassIsRejected()
{
Assert.Throws<NotSupportedException>(
() => ProduceResXTest("Serial", new SerializableClass { Name = "Hugo", Age = 42 })
);
}
[Test]
public void BitmapIsResourceSerializedObject()
{
Stream stream = typeof(ResourceReaderWriterTests).Assembly
.GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + ".Test.resources");
using var reader = new ResourcesFile(stream);
var items = reader.ToArray();
Assert.That(items.Length, Is.EqualTo(3));
var item = items.FirstOrDefault(i => i.Key == "Bitmap");
Assert.That(item.Key, Is.Not.Null);
Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>());
var rso = (ResourceSerializedObject)item.Value;
Assert.That(rso.TypeName, Is.Null);
}
[Test]
public void ByteArrayIsSupported()
{
Stream stream = typeof(ResourceReaderWriterTests).Assembly
.GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + ".Test.resources");
using var reader = new ResourcesFile(stream);
var items = reader.ToArray();
Assert.That(items.Length, Is.EqualTo(3));
var item = items.FirstOrDefault(i => i.Key == "Byte[]");
Assert.That(item.Key, Is.Not.Null);
Assert.That(item.Value, Is.InstanceOf<byte[]>());
byte[] array = (byte[])item.Value;
Assert.That(array.Length, Is.EqualTo(3));
Assert.That(array[0], Is.EqualTo(42));
Assert.That(array[1], Is.EqualTo(43));
Assert.That(array[2], Is.EqualTo(44));
}
[Test]
public void MemoryStreamIsSupported()
{
Stream stream = typeof(ResourceReaderWriterTests).Assembly
.GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + ".Test.resources");
using var reader = new ResourcesFile(stream);
var items = reader.ToArray();
Assert.That(items.Length, Is.EqualTo(3));
var item = items.FirstOrDefault(i => i.Key == "MemoryStream");
Assert.That(item.Key, Is.Not.Null);
Assert.That(item.Value, Is.InstanceOf<MemoryStream>());
}
[Test]
public void IconDataCanBeDeserializedFromResX()
{
// Uses new serialization format
var resourcesStream = GetResource("IconContainer.resources");
var reader = new ResourcesFile(resourcesStream);
var item = reader.Single();
Assert.That(item.Key, Is.EqualTo("Icon"));
Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>());
var rso = (ResourceSerializedObject)item.Value;
var xml = ProduceResXTest("Icon", rso);
Assert.That(xml.GetAttribute("name"), Is.EqualTo("Icon"));
Assert.That(xml.GetAttribute("type"), Is.EqualTo("System.Drawing.Icon" + drawingAssemblyName));
Assert.That(xml.GetAttribute("mimetype"), Is.EqualTo("application/x-microsoft.net.object.bytearray.base64"));
var base64Icon = xml.Element("value").Value;
using var memory = new MemoryStream(Convert.FromBase64String(base64Icon));
new Icon(memory);
}
[Test]
public void BitmapDataCanBeDeserializedFromResX()
{
// Uses old serialization format
var resourcesStream = GetResource("BitmapContainer.resources");
var reader = new ResourcesFile(resourcesStream);
var item = reader.Single(x => x.Key == "Image1");
Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>());
var rso = (ResourceSerializedObject)item.Value;
var xml = ProduceResXTest("Image1", rso);
Assert.That(xml.GetAttribute("name"), Is.EqualTo("Image1"));
Assert.That(xml.GetAttribute("type"), Is.Null);
Assert.That(xml.GetAttribute("mimetype"), Is.EqualTo("application/x-microsoft.net.object.binary.base64"));
}
}
}

BIN
ILSpy.Tests/Test.resources

Binary file not shown.

109
ILSpy.Tests/UpdateServiceTests.cs

@ -1,109 +0,0 @@ @@ -1,109 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using AwesomeAssertions;
using ICSharpCode.ILSpy.Updates;
using NUnit.Framework;
namespace ICSharpCode.ILSpy.Tests;
[TestFixture]
public class UpdateServiceTests
{
[Test]
public async Task GetLatestVersionAsync_UsesReleaseTag_WhenReleaseTagIsPresent()
{
const string xml = """
<updateInfo>
<band id="stable">
<latestVersion>10.0.0.0</latestVersion>
<releaseTag>v10.0</releaseTag>
<downloadUrl>https://example.com/ignored.zip</downloadUrl>
</band>
</updateInfo>
""";
using var client = new HttpClient(new StubHttpMessageHandler(xml));
var result = await UpdateService.GetLatestVersionAsync(client, new Uri("https://example.com/updates.xml"));
result.Version.Should().Be(new Version(10, 0, 0, 0));
result.DownloadUrl.Should().Be("https://github.com/icsharpcode/ILSpy/releases/tag/v10.0");
}
[Test]
public async Task GetLatestVersionAsync_ReturnsNullDownloadUrl_WhenReleaseTagContainsPathTraversalAttempt()
{
const string xml = """
<updateInfo>
<band id="stable">
<latestVersion>10.0.0.0</latestVersion>
<releaseTag>../malicious</releaseTag>
<downloadUrl>https://example.com/ignored.zip</downloadUrl>
</band>
</updateInfo>
""";
using var client = new HttpClient(new StubHttpMessageHandler(xml));
var result = await UpdateService.GetLatestVersionAsync(client, new Uri("https://example.com/updates.xml"));
result.Version.Should().Be(new Version(10, 0, 0, 0));
result.DownloadUrl.Should().BeNull();
}
[Test]
public async Task GetLatestVersionAsync_UsesDownloadUrl_WhenReleaseTagIsMissing()
{
const string xml = """
<updateInfo>
<band id="stable">
<latestVersion>10.0.0.0</latestVersion>
<downloadUrl>https://github.com/icsharpcode/ILSpy/releases/tag/v10.0</downloadUrl>
</band>
</updateInfo>
""";
using var client = new HttpClient(new StubHttpMessageHandler(xml));
var result = await UpdateService.GetLatestVersionAsync(client, new Uri("https://example.com/updates.xml"));
result.Version.Should().Be(new Version(10, 0, 0, 0));
result.DownloadUrl.Should().Be("https://github.com/icsharpcode/ILSpy/releases/tag/v10.0");
}
[Test]
public async Task GetLatestVersionAsync_UsesDownloadUrl_ButFailsBecauseBaseUrlDoesntMatch()
{
const string xml = """
<updateInfo>
<band id="stable">
<latestVersion>10.0.0.0</latestVersion>
<downloadUrl>https://example.com/ilspy.zip</downloadUrl>
</band>
</updateInfo>
""";
using var client = new HttpClient(new StubHttpMessageHandler(xml));
var result = await UpdateService.GetLatestVersionAsync(client, new Uri("https://example.com/updates.xml"));
result.Version.Should().Be(new Version(10, 0, 0, 0));
result.DownloadUrl.Should().BeNull();
}
sealed class StubHttpMessageHandler(string responseContent) : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) {
Content = new StringContent(responseContent)
});
}
}
}

39
ILSpy.sln

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.4.11620.152 stable
VisualStudioVersion = 18.4.11620.152
MinimumVisualStudioVersion = 15.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E72-4000-B5AD-3A7B485A0896}"
ProjectSection(SolutionItems) = preProject
@ -9,22 +10,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E7 @@ -9,22 +10,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{F45DB999-7E7
doc\IntPtr.txt = doc\IntPtr.txt
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy", "ILSpy\ILSpy.csproj", "{1E85EFF9-E370-4683-83E4-8A3D063FF791}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler.Tests", "ICSharpCode.Decompiler.Tests\ICSharpCode.Decompiler.Tests.csproj", "{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestPlugin", "TestPlugin\TestPlugin.csproj", "{F32EBCC8-0E53-4421-867E-05B3D6E10C70}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.BamlDecompiler", "ILSpy.BamlDecompiler\ILSpy.BamlDecompiler.csproj", "{A6BAD2BA-76BA-461C-8B6D-418607591247}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.BamlDecompiler.Tests", "ILSpy.BamlDecompiler.Tests\ILSpy.BamlDecompiler.Tests.csproj", "{1169E6D1-1899-43D4-A500-07CE4235B388}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.Tests", "ILSpy.Tests\ILSpy.Tests.csproj", "{B51C6636-B8D1-4200-9869-08F2689DE6C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILSpy.ReadyToRun", "ILSpy.ReadyToRun\ILSpy.ReadyToRun.csproj", "{0313F581-C63B-43BB-AA9B-07615DABD8A3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.ILSpyCmd", "ICSharpCode.ILSpyCmd\ICSharpCode.ILSpyCmd.csproj", "{743B439A-E7AD-4A0A-BAB6-222E1EA83C6D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ICSharpCode.Decompiler.PowerShell", "ICSharpCode.Decompiler.PowerShell\ICSharpCode.Decompiler.PowerShell.csproj", "{50060E0C-FA25-41F4-B72F-8490324EC9F0}"
@ -50,10 +39,6 @@ Global @@ -50,10 +39,6 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1E85EFF9-E370-4683-83E4-8A3D063FF791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1E85EFF9-E370-4683-83E4-8A3D063FF791}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E85EFF9-E370-4683-83E4-8A3D063FF791}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E85EFF9-E370-4683-83E4-8A3D063FF791}.Release|Any CPU.Build.0 = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -62,26 +47,6 @@ Global @@ -62,26 +47,6 @@ Global
{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Release|Any CPU.Build.0 = Release|Any CPU
{F32EBCC8-0E53-4421-867E-05B3D6E10C70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F32EBCC8-0E53-4421-867E-05B3D6E10C70}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F32EBCC8-0E53-4421-867E-05B3D6E10C70}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F32EBCC8-0E53-4421-867E-05B3D6E10C70}.Release|Any CPU.Build.0 = Release|Any CPU
{A6BAD2BA-76BA-461C-8B6D-418607591247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A6BAD2BA-76BA-461C-8B6D-418607591247}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A6BAD2BA-76BA-461C-8B6D-418607591247}.Release|Any CPU.Build.0 = Release|Any CPU
{1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1169E6D1-1899-43D4-A500-07CE4235B388}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1169E6D1-1899-43D4-A500-07CE4235B388}.Release|Any CPU.Build.0 = Release|Any CPU
{B51C6636-B8D1-4200-9869-08F2689DE6C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B51C6636-B8D1-4200-9869-08F2689DE6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B51C6636-B8D1-4200-9869-08F2689DE6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B51C6636-B8D1-4200-9869-08F2689DE6C2}.Release|Any CPU.Build.0 = Release|Any CPU
{0313F581-C63B-43BB-AA9B-07615DABD8A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0313F581-C63B-43BB-AA9B-07615DABD8A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0313F581-C63B-43BB-AA9B-07615DABD8A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0313F581-C63B-43BB-AA9B-07615DABD8A3}.Release|Any CPU.Build.0 = Release|Any CPU
{743B439A-E7AD-4A0A-BAB6-222E1EA83C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{743B439A-E7AD-4A0A-BAB6-222E1EA83C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{743B439A-E7AD-4A0A-BAB6-222E1EA83C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU

241
ILSpy/AboutPage.cs

@ -1,241 +0,0 @@ @@ -1,241 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Composition;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Navigation;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpy.Updates;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)]
[Shared]
public sealed class AboutPage : SimpleCommand
{
readonly SettingsService settingsService;
readonly IEnumerable<IAboutPageAddition> aboutPageAdditions;
public AboutPage(SettingsService settingsService, IEnumerable<IAboutPageAddition> aboutPageAdditions)
{
this.settingsService = settingsService;
this.aboutPageAdditions = aboutPageAdditions;
MessageBus<ShowAboutPageEventArgs>.Subscribers += (_, e) => ShowAboutPage(e.TabPage);
}
public override void Execute(object parameter)
{
MessageBus.Send(this, new NavigateToEventArgs(new RequestNavigateEventArgs(new Uri("resource://aboutpage"), null), inNewTabPage: true));
}
private void ShowAboutPage(TabPageModel tabPage)
{
tabPage.ShowTextView(Display);
}
private void Display(DecompilerTextView textView)
{
AvalonEditTextOutput output = new AvalonEditTextOutput() {
Title = Resources.About,
EnableHyperlinks = true
};
output.WriteLine(Resources.ILSpyVersion + DecompilerVersionInfo.FullVersionWithCommitHash);
string prodVersion = GetDotnetProductVersion();
output.WriteLine(Resources.NETFrameworkVersion + prodVersion);
output.AddUIElement(
delegate {
var stackPanel = new StackPanel {
HorizontalAlignment = HorizontalAlignment.Center,
Orientation = Orientation.Horizontal
};
if (UpdateService.LatestAvailableVersion == null)
{
AddUpdateCheckButton(stackPanel, textView);
}
else
{
// we already retrieved the latest version sometime earlier
ShowAvailableVersion(UpdateService.LatestAvailableVersion, stackPanel);
}
var checkBox = new CheckBox {
Margin = new Thickness(4),
Content = Resources.AutomaticallyCheckUpdatesEveryWeek
};
var settings = settingsService.GetSettings<UpdateSettings>();
checkBox.SetBinding(ToggleButton.IsCheckedProperty, new Binding("AutomaticUpdateCheckEnabled") { Source = settings });
return new StackPanel {
Margin = new Thickness(0, 4, 0, 0),
Cursor = Cursors.Arrow,
Children = { stackPanel, checkBox }
};
});
output.WriteLine();
foreach (var plugin in aboutPageAdditions)
plugin.Write(output);
output.WriteLine();
output.Address = new Uri("resource://AboutPage");
using (Stream s = typeof(AboutPage).Assembly.GetManifestResourceStream(typeof(AboutPage), Resources.ILSpyAboutPageTxt))
{
using (StreamReader r = new StreamReader(s))
{
while (r.ReadLine() is { } line)
{
output.WriteLine(line);
}
}
}
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("MIT License", "resource:license.txt"));
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("third-party notices", "resource:third-party-notices.txt"));
textView.ShowText(output);
}
private static string GetDotnetProductVersion()
{
// In case of AOT .Location is null, we need a fallback for that
string assemblyLocation = typeof(Uri).Assembly.Location;
if (!String.IsNullOrWhiteSpace(assemblyLocation))
{
return System.Diagnostics.FileVersionInfo.GetVersionInfo(assemblyLocation).ProductVersion;
}
else
{
var version = typeof(Object).Assembly.GetName().Version;
if (version != null)
{
return version.ToString();
}
}
return "UNKNOWN";
}
sealed class MyLinkElementGenerator : LinkElementGenerator
{
readonly Uri uri;
public MyLinkElementGenerator(string matchText, string url) : base(new Regex(Regex.Escape(matchText)))
{
this.uri = new Uri(url);
this.RequireControlModifierForClick = false;
}
protected override Uri GetUriFromMatch(Match match)
{
return uri;
}
}
static void AddUpdateCheckButton(StackPanel stackPanel, DecompilerTextView textView)
{
Button button = ThemeManager.Current.CreateButton();
button.Content = Resources.CheckUpdates;
button.Cursor = Cursors.Arrow;
stackPanel.Children.Add(button);
button.Click += async delegate {
button.Content = Resources.Checking;
button.IsEnabled = false;
try
{
AvailableVersionInfo vInfo = await UpdateService.GetLatestVersionAsync();
stackPanel.Children.Clear();
ShowAvailableVersion(vInfo, stackPanel);
}
catch (Exception ex)
{
AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput();
exceptionOutput.WriteLine(ex.ToString());
textView.ShowText(exceptionOutput);
}
};
}
static void ShowAvailableVersion(AvailableVersionInfo availableVersion, StackPanel stackPanel)
{
if (AppUpdateService.CurrentVersion == availableVersion.Version)
{
stackPanel.Children.Add(
new Image {
Width = 16, Height = 16,
#if CROSS_PLATFORM
Source = Images.LoadImage(Images.OK),
#else
Source = Images.OK,
#endif
Margin = new Thickness(4, 0, 4, 0)
});
stackPanel.Children.Add(
new TextBlock {
Text = Resources.UsingLatestRelease,
VerticalAlignment = VerticalAlignment.Bottom
});
}
else if (AppUpdateService.CurrentVersion < availableVersion.Version)
{
stackPanel.Children.Add(
new TextBlock {
Text = string.Format(Resources.VersionAvailable, availableVersion.Version),
Margin = new Thickness(0, 0, 8, 0),
VerticalAlignment = VerticalAlignment.Bottom
});
if (availableVersion.DownloadUrl != null)
{
Button button = ThemeManager.Current.CreateButton();
button.Content = Resources.Download;
button.Cursor = Cursors.Arrow;
button.Click += delegate {
GlobalUtils.OpenLink(availableVersion.DownloadUrl);
};
stackPanel.Children.Add(button);
}
}
else
{
stackPanel.Children.Add(new TextBlock { Text = Resources.UsingNightlyBuildNewerThanLatestRelease });
}
}
}
/// <summary>
/// Interface that allows plugins to extend the about page.
/// </summary>
public interface IAboutPageAddition
{
void Write(ISmartTextOutput textOutput);
}
}

93
ILSpy/Analyzers/AnalyzeCommand.cs

@ -1,93 +0,0 @@ @@ -1,93 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportContextMenuEntry(Header = nameof(Resources.Analyze), Icon = "Images/Search", Category = nameof(Resources.Analyze), InputGestureText = "Ctrl+R", Order = 100)]
[Shared]
internal sealed class AnalyzeContextMenuCommand(AnalyzerTreeViewModel analyzerTreeView) : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot))
return false;
if (context.SelectedTreeNodes == null)
return context.Reference != null && IsValidReference(context.Reference.Reference);
return context.SelectedTreeNodes.All(n => n is IMemberTreeNode);
}
public bool IsEnabled(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
{
return context.Reference is { Reference: IEntity };
}
return context.SelectedTreeNodes
.OfType<IMemberTreeNode>()
.All(node => IsValidReference(node.Member));
}
static bool IsValidReference(object reference)
{
return reference is IEntity and not IField { IsConst: true };
}
public void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes != null)
{
foreach (var node in context.SelectedTreeNodes.OfType<IMemberTreeNode>().ToArray())
{
analyzerTreeView.Analyze(node.Member);
}
}
else if (context.Reference is { Reference: IEntity entity })
{
analyzerTreeView.Analyze(entity);
}
}
}
[Export]
[Shared]
public sealed class AnalyzeCommand(AssemblyTreeModel assemblyTreeModel, AnalyzerTreeViewModel analyzerTreeViewModel) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
return assemblyTreeModel.SelectedNodes.All(n => n is IMemberTreeNode);
}
public override void Execute(object parameter)
{
foreach (var node in assemblyTreeModel.SelectedNodes.OfType<IMemberTreeNode>())
{
analyzerTreeViewModel.Analyze(node.Member);
}
}
}
}

78
ILSpy/Analyzers/AnalyzerEntityTreeNode.cs

@ -1,78 +0,0 @@ @@ -1,78 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView;
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// Base class for entity nodes.
/// </summary>
public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode
{
public abstract IEntity? Member { get; }
public IEntity? SourceMember { get; protected set; }
public override void ActivateItem(IPlatformRoutedEventArgs e)
{
e.Handled = true;
if (this.Member == null || this.Member.MetadataToken.IsNil)
{
MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy");
return;
}
var module = this.Member.ParentModule?.MetadataFile;
Debug.Assert(module != null);
MessageBus.Send(this, new NavigateToReferenceEventArgs(new EntityReference(module, this.Member.MetadataToken), this.SourceMember));
}
public override object? ToolTip => Member?.ParentModule?.MetadataFile?.FileName;
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
if (Member == null)
{
return true;
}
foreach (LoadedAssembly asm in removedAssemblies)
{
if (this.Member.ParentModule!.MetadataFile == asm.GetMetadataFileOrNull())
return false; // remove this node
}
this.Children.RemoveAll(
delegate (SharpTreeNode n) {
return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
});
return true;
}
}
}

59
ILSpy/Analyzers/AnalyzerRootNode.cs

@ -1,59 +0,0 @@ @@ -1,59 +0,0 @@
// Copyright (c) 2024 Tom Englert 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.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView;
namespace ICSharpCode.ILSpy.Analyzers;
public sealed class AnalyzerRootNode : AnalyzerTreeNode
{
public AnalyzerRootNode()
{
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed(sender, e);
}
void CurrentAssemblyList_Changed(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
this.Children.Clear();
}
else
{
var removedAssemblies = e.OldItems?.Cast<LoadedAssembly>().ToArray() ?? [];
var addedAssemblies = e.NewItems?.Cast<LoadedAssembly>().ToArray() ?? [];
HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
}
}
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
this.Children.RemoveAll(
delegate (SharpTreeNode n) {
AnalyzerTreeNode an = n as AnalyzerTreeNode;
return an == null || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
});
return true;
}
}

136
ILSpy/Analyzers/AnalyzerSearchTreeNode.cs

@ -1,136 +0,0 @@ @@ -1,136 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers.TreeNodes;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Analyzers;
namespace ICSharpCode.ILSpy.Analyzers
{
class AnalyzerSearchTreeNode : AnalyzerTreeNode
{
private readonly ThreadingSupport threading = new ThreadingSupport();
readonly ISymbol symbol;
readonly IAnalyzer analyzer;
readonly string analyzerHeader;
public AnalyzerSearchTreeNode(ISymbol symbol, IAnalyzer analyzer, string analyzerHeader)
{
this.symbol = symbol;
this.analyzer = analyzer ?? throw new ArgumentNullException(nameof(analyzer));
this.LazyLoading = true;
this.analyzerHeader = analyzerHeader;
}
public override object Text => analyzerHeader
+ (Children.Count > 0 && !threading.IsRunning ? " (" + Children.Count + " in " + threading.EllapsedMilliseconds + "ms)" : "");
public override object Icon => Images.Search;
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected IEnumerable<AnalyzerTreeNode> FetchChildren(CancellationToken ct)
{
if (symbol is IEntity)
{
var context = new AnalyzerContext {
CancellationToken = ct,
Language = Language,
AssemblyList = AssemblyList
};
var results = analyzer.Analyze(symbol, context).Select(SymbolTreeNodeFactory);
if (context.SortResults)
{
results = results.OrderBy(tn => tn.Text?.ToString(), NaturalStringComparer.Instance);
}
return results;
}
else
{
throw new NotSupportedException("Currently symbols that are not entities are not supported!");
}
}
AnalyzerTreeNode SymbolTreeNodeFactory(ISymbol resultSymbol)
{
if (resultSymbol == null)
{
throw new ArgumentNullException(nameof(resultSymbol));
}
switch (resultSymbol)
{
case IModule module:
return new AnalyzedModuleTreeNode(module, (IEntity)this.symbol);
case ITypeDefinition td:
return new AnalyzedTypeTreeNode(td, (IEntity)this.symbol);
case IField fd:
return new AnalyzedFieldTreeNode(fd, (IEntity)this.symbol);
case IMethod md:
return new AnalyzedMethodTreeNode(md, (IEntity)this.symbol);
case IProperty pd:
return new AnalyzedPropertyTreeNode(pd, (IEntity)this.symbol);
case IEvent ed:
return new AnalyzedEventTreeNode(ed, (IEntity)this.symbol);
default:
throw new ArgumentOutOfRangeException(nameof(resultSymbol), $"Symbol {resultSymbol.GetType().FullName} is not supported.");
}
}
protected override void OnIsVisibleChanged()
{
base.OnIsVisibleChanged();
if (!this.IsVisible && threading.IsRunning)
{
this.LazyLoading = true;
threading.Cancel();
this.Children.Clear();
RaisePropertyChanged(nameof(Text));
}
}
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
// only cancel a running analysis if user has manually added/removed assemblies
bool manualAdd = false;
foreach (var asm in addedAssemblies)
{
if (!asm.IsAutoLoaded)
manualAdd = true;
}
if (removedAssemblies.Count > 0 || manualAdd)
{
this.LazyLoading = true;
threading.Cancel();
this.Children.Clear();
RaisePropertyChanged(nameof(Text));
}
return true;
}
}
}

62
ILSpy/Analyzers/AnalyzerTreeNode.cs

@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Linq;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.Analyzers;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.Analyzers
{
public abstract class AnalyzerTreeNode : SharpTreeNode
{
protected static Language Language => App.ExportProvider.GetExportedValue<LanguageService>().Language;
protected static AssemblyList AssemblyList => App.ExportProvider.GetExportedValue<AssemblyList>();
public override bool CanDelete()
{
return Parent is { IsRoot: true };
}
public override void DeleteCore()
{
Parent.Children.Remove(this);
}
public override void Delete()
{
DeleteCore();
}
public static ICollection<IExport<IAnalyzer, IAnalyzerMetadata>> Analyzers => App.ExportProvider
.GetExports<IAnalyzer, IAnalyzerMetadata>("Analyzer")
.OrderBy(item => item.Metadata?.Order)
.ToArray();
/// <summary>
/// Handles changes to the assembly list.
/// </summary>
public abstract bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies);
}
}

22
ILSpy/Analyzers/AnalyzerTreeView.xaml

@ -1,22 +0,0 @@ @@ -1,22 +0,0 @@
<treeView:SharpTreeView
x:Class="ICSharpCode.ILSpy.Analyzers.AnalyzerTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:toms="urn:TomsToolbox"
xmlns:treeView="clr-namespace:ICSharpCode.ILSpy.Controls.TreeView"
xmlns:analyzers="clr-namespace:ICSharpCode.ILSpy.Analyzers"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance analyzers:AnalyzerTreeViewModel}"
ShowRoot="False"
BorderThickness="0"
Root="{Binding Root}"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems, Mode=TwoWay}"
SelectionChanged="AnalyzerTreeView_OnSelectionChanged">
<UIElement.InputBindings>
<KeyBinding Key="R" Modifiers="Control" Command="{Binding AnalyzeCommand}" />
</UIElement.InputBindings>
</treeView:SharpTreeView>

50
ILSpy/Analyzers/AnalyzerTreeView.xaml.cs

@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Windows.Controls;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Wpf.Composition.AttributedModel;
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// Interaction logic for AnalyzerTreeView.xaml
/// </summary>
[DataTemplate(typeof(AnalyzerTreeViewModel))]
[NonShared]
[Export]
public partial class AnalyzerTreeView
{
public AnalyzerTreeView()
{
InitializeComponent();
ContextMenuProvider.Add(this);
}
private void AnalyzerTreeView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (SelectedItem is SharpTreeNode sharpTreeNode)
{
FocusNode(sharpTreeNode);
}
}
}
}

134
ILSpy/Analyzers/AnalyzerTreeViewModel.cs

@ -1,134 +0,0 @@ @@ -1,134 +0,0 @@
// Copyright (c) 2024 Tom Englert 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.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers.TreeNodes;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Wpf;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportToolPane]
[Shared]
[Export]
public class AnalyzerTreeViewModel : ToolPaneModel
{
public const string PaneContentId = "analyzerPane";
public AnalyzerTreeViewModel(AssemblyTreeModel assemblyTreeModel)
{
ContentId = PaneContentId;
Title = Properties.Resources.Analyze;
ShortcutKey = new(Key.R, ModifierKeys.Control);
AssociatedCommand = new AnalyzeCommand(assemblyTreeModel, this);
}
public AnalyzerRootNode Root { get; } = new();
public ICommand AnalyzeCommand => new DelegateCommand(AnalyzeSelected);
private SharpTreeNode[] selectedItems = [];
public SharpTreeNode[] SelectedItems {
get => selectedItems ?? [];
set {
if (SelectedItems.SequenceEqual(value))
return;
selectedItems = value;
OnPropertyChanged();
}
}
private void AnalyzeSelected()
{
foreach (var node in SelectedItems.OfType<IMemberTreeNode>())
{
Analyze(node.Member);
}
}
void AddOrSelect(AnalyzerTreeNode node)
{
Show();
AnalyzerTreeNode target = default;
if (node is AnalyzerEntityTreeNode { Member: { } member })
{
target = this.Root.Children.OfType<AnalyzerEntityTreeNode>().FirstOrDefault(item => item.Member == member);
}
if (target == null)
{
this.Root.Children.Add(node);
target = node;
}
target.IsExpanded = true;
this.SelectedItems = [target];
}
public void Analyze(IEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
if (entity.MetadataToken.IsNil)
{
MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy");
return;
}
switch (entity)
{
case ITypeDefinition td:
AddOrSelect(new AnalyzedTypeTreeNode(td, null));
break;
case IField fd:
if (!fd.IsConst)
AddOrSelect(new AnalyzedFieldTreeNode(fd, null));
break;
case IMethod md:
AddOrSelect(new AnalyzedMethodTreeNode(md, null));
break;
case IProperty pd:
AddOrSelect(new AnalyzedPropertyTreeNode(pd, null));
break;
case IEvent ed:
AddOrSelect(new AnalyzedEventTreeNode(ed, null));
break;
default:
throw new ArgumentOutOfRangeException(nameof(entity), $@"Entity {entity.GetType().FullName} is not supported.");
}
}
}
}

58
ILSpy/Analyzers/CopyAnalysisResultsContextMenuEntry.cs

@ -1,58 +0,0 @@ @@ -1,58 +0,0 @@
// Copyright (c) 2022 Siegfried Pammer
//
// 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.Composition;
using System.Linq;
using System.Text;
using System.Windows;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportContextMenuEntry(Header = "Copy results", Category = "Analyze", Order = 200)]
[Shared]
internal sealed class CopyAnalysisResultsContextMenuEntry : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n is AnalyzerSearchTreeNode))
return true;
return false;
}
public bool IsEnabled(TextViewContext context)
{
return true;
}
public void Execute(TextViewContext context)
{
StringBuilder sb = new StringBuilder();
if (context.SelectedTreeNodes != null)
{
foreach (var node in context.SelectedTreeNodes)
{
foreach (var item in node.Children)
{
sb.AppendLine(item.Text.ToString());
}
}
}
Clipboard.SetText(sb.ToString());
}
}
}

51
ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs

@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
// Copyright (c) 2013 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.Composition;
using System.Linq;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete", Category = "Analyze", Order = 200)]
[Shared]
internal sealed class RemoveAnalyzeContextMenuEntry : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot))
return true;
return false;
}
public bool IsEnabled(TextViewContext context)
{
return true;
}
public void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes != null)
{
foreach (var node in context.SelectedTreeNodes)
{
node.Parent.Children.Remove(node);
}
}
}
}
}

40
ILSpy/Analyzers/TreeNodes/AnalyzedAccessorTreeNode.cs

@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
// Copyright (c) 2011 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 ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
class AnalyzedAccessorTreeNode : AnalyzedMethodTreeNode
{
readonly string name;
public AnalyzedAccessorTreeNode(IMethod analyzedMethod, IEntity source, string name)
: base(analyzedMethod, source)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new System.ArgumentException("name must be a non-empty string", nameof(name));
}
this.name = name;
}
public override object Text => name;
}
}

85
ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs

@ -1,85 +0,0 @@ @@ -1,85 +0,0 @@
// Copyright (c) 2011 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.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
internal sealed class AnalyzedEventTreeNode : AnalyzerEntityTreeNode
{
readonly IEvent analyzedEvent;
readonly string prefix;
public AnalyzedEventTreeNode(IEvent analyzedEvent, IEntity? source, string prefix = "")
{
this.analyzedEvent = analyzedEvent ?? throw new ArgumentNullException(nameof(analyzedEvent));
this.prefix = prefix;
this.LazyLoading = true;
this.SourceMember = source;
}
public override IEntity Member => analyzedEvent;
public override object Icon => EventTreeNode.GetIcon(analyzedEvent);
// TODO: This way of formatting is not suitable for events which explicitly implement interfaces.
public override object Text => prefix + Language.EntityToString(analyzedEvent, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames);
protected override void LoadChildren()
{
if (analyzedEvent.CanAdd)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.AddAccessor, this.SourceMember, "add"));
if (analyzedEvent.CanRemove)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedEvent.RemoveAccessor, this.SourceMember, "remove"));
if (TryFindBackingField(analyzedEvent, out var backingField))
this.Children.Add(new AnalyzedFieldTreeNode(backingField, this.SourceMember));
foreach (var lazy in Analyzers)
{
var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedEvent))
{
this.Children.Add(new AnalyzerSearchTreeNode(analyzedEvent, analyzer, lazy.Metadata?.Header));
}
}
}
bool TryFindBackingField(IEvent analyzedEvent, [NotNullWhen(true)] out IField? backingField)
{
backingField = null;
foreach (var field in analyzedEvent.DeclaringTypeDefinition?.GetFields(options: GetMemberOptions.IgnoreInheritedMembers) ?? [])
{
if (field.Name == analyzedEvent.Name && field.Accessibility == Decompiler.TypeSystem.Accessibility.Private)
{
backingField = field;
return true;
}
}
return false;
}
}
}

60
ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs

@ -1,60 +0,0 @@ @@ -1,60 +0,0 @@
// Copyright (c) 2011 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.Diagnostics;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
class AnalyzedFieldTreeNode : AnalyzerEntityTreeNode
{
readonly IField analyzedField;
public AnalyzedFieldTreeNode(IField analyzedField, IEntity? source)
{
this.analyzedField = analyzedField ?? throw new ArgumentNullException(nameof(analyzedField));
this.SourceMember = source;
this.LazyLoading = true;
}
public override object Icon => FieldTreeNode.GetIcon(analyzedField);
public override object Text => Language.EntityToString(analyzedField, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames);
protected override void LoadChildren()
{
foreach (var lazy in Analyzers)
{
var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedField))
{
this.Children.Add(new AnalyzerSearchTreeNode(analyzedField, analyzer, lazy.Metadata?.Header));
}
}
}
public override IEntity Member => analyzedField;
}
}

62
ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs

@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
// Copyright (c) 2011 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.Diagnostics;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode
{
readonly IMethod analyzedMethod;
readonly string prefix;
public AnalyzedMethodTreeNode(IMethod analyzedMethod, IEntity? source, string prefix = "")
{
this.analyzedMethod = analyzedMethod ?? throw new ArgumentNullException(nameof(analyzedMethod));
this.SourceMember = source;
this.prefix = prefix;
this.LazyLoading = true;
}
public override object Icon => MethodTreeNode.GetIcon(analyzedMethod);
public override object Text => prefix + Language.EntityToString(analyzedMethod, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames);
protected override void LoadChildren()
{
foreach (var lazy in Analyzers)
{
var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedMethod))
{
this.Children.Add(new AnalyzerSearchTreeNode(analyzedMethod, analyzer, lazy.Metadata!.Header));
}
}
}
public override IEntity Member => analyzedMethod;
}
}

94
ILSpy/Analyzers/TreeNodes/AnalyzedModuleTreeNode.cs

@ -1,94 +0,0 @@ @@ -1,94 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView;
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
internal class AnalyzedModuleTreeNode : AnalyzerEntityTreeNode
{
readonly IModule analyzedModule;
public AnalyzedModuleTreeNode(IModule analyzedModule, IEntity? source)
{
this.analyzedModule = analyzedModule ?? throw new ArgumentNullException(nameof(analyzedModule));
this.SourceMember = source;
this.LazyLoading = true;
}
public override object Icon => Images.Assembly;
public override object Text => analyzedModule.AssemblyName;
public override object? ToolTip => analyzedModule.MetadataFile?.FileName;
protected override void LoadChildren()
{
foreach (var lazy in Analyzers)
{
var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedModule))
{
this.Children.Add(new AnalyzerSearchTreeNode(analyzedModule, analyzer, lazy.Metadata!.Header));
}
}
}
public override void ActivateItem(IPlatformRoutedEventArgs e)
{
e.Handled = true;
if (analyzedModule.MetadataFile == null)
{
MessageBox.Show(Properties.Resources.CannotAnalyzeMissingRef, "ILSpy");
return;
}
MessageBus.Send(this, new NavigateToReferenceEventArgs(analyzedModule.MetadataFile));
}
public override IEntity? Member => null;
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
if (analyzedModule == null)
{
return true;
}
foreach (LoadedAssembly asm in removedAssemblies)
{
if (this.analyzedModule.MetadataFile == asm.GetMetadataFileOrNull())
return false; // remove this node
}
this.Children.RemoveAll(
delegate (SharpTreeNode n) {
return n is not AnalyzerTreeNode an || !an.HandleAssemblyListChanged(removedAssemblies, addedAssemblies);
});
return true;
}
}
}

68
ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs

@ -1,68 +0,0 @@ @@ -1,68 +0,0 @@
// Copyright (c) 2011 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.Diagnostics;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
sealed class AnalyzedPropertyTreeNode : AnalyzerEntityTreeNode
{
readonly IProperty analyzedProperty;
readonly string prefix;
public AnalyzedPropertyTreeNode(IProperty analyzedProperty, IEntity? source, string prefix = "")
{
this.analyzedProperty = analyzedProperty ?? throw new ArgumentNullException(nameof(analyzedProperty));
this.prefix = prefix;
this.LazyLoading = true;
this.SourceMember = source;
}
public override object Icon => PropertyTreeNode.GetIcon(analyzedProperty);
// TODO: This way of formatting is not suitable for properties which explicitly implement interfaces.
public override object Text => prefix + Language.EntityToString(analyzedProperty, ConversionFlags.ShowDeclaringType | ConversionFlags.UseFullyQualifiedEntityNames);
protected override void LoadChildren()
{
if (analyzedProperty.CanGet)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Getter, this.SourceMember, "get"));
if (analyzedProperty.CanSet)
this.Children.Add(new AnalyzedAccessorTreeNode(analyzedProperty.Setter, this.SourceMember, "set"));
foreach (var lazy in Analyzers)
{
var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedProperty))
{
this.Children.Add(new AnalyzerSearchTreeNode(analyzedProperty, analyzer, lazy.Metadata!.Header));
}
}
}
public override IEntity Member => analyzedProperty;
}
}

59
ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs

@ -1,59 +0,0 @@ @@ -1,59 +0,0 @@
// Copyright (c) 2011 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.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
#nullable enable
namespace ICSharpCode.ILSpy.Analyzers.TreeNodes
{
internal class AnalyzedTypeTreeNode : AnalyzerEntityTreeNode
{
readonly ITypeDefinition analyzedType;
public AnalyzedTypeTreeNode(ITypeDefinition analyzedType, IEntity? source)
{
this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType));
this.SourceMember = source;
this.LazyLoading = true;
}
public override object Icon => TypeTreeNode.GetIcon(analyzedType);
public override object Text => Language.TypeToString(analyzedType);
protected override void LoadChildren()
{
foreach (var lazy in Analyzers)
{
var analyzer = lazy.Value;
Debug.Assert(analyzer != null);
if (analyzer.Show(analyzedType))
{
this.Children.Add(new AnalyzerSearchTreeNode(analyzedType, analyzer, lazy.Metadata!.Header));
}
}
}
public override IEntity Member => analyzedType;
}
}

39
ILSpy/App.xaml

@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
<Application x:Class="ICSharpCode.ILSpy.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:styles="urn:TomsToolbox.Wpf.Styles"
xmlns:toms="urn:TomsToolbox"
xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
xmlns:composition="urn:TomsToolbox.Composition"
xmlns:util="clr-namespace:ICSharpCode.ILSpy.Util">
<Application.Resources>
<Style x:Key="DialogWindow" TargetType="{x:Type Window}">
<Setter Property="ShowInTaskbar" Value="False" />
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
<Setter Property="toms:StyleBindings.Behaviors">
<Setter.Value>
<toms:BehaviorCollection>
<themes:WindowStyleManagerBehavior />
</toms:BehaviorCollection>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static styles:ResourceKeys.ButtonStyle}}">
<Setter Property="MinWidth" Value="73" />
<Setter Property="Padding" Value="9,1,9,1" />
</Style>
<Style TargetType="ScrollViewer">
<Setter Property="toms:StyleBindings.Behaviors">
<Setter.Value>
<toms:BehaviorCollection>
<toms:AdvancedScrollWheelBehavior UseScrollingAnimation="{Binding Path=(util:SettingsService.DisplaySettings).EnableSmoothScrolling, Source={composition:Import util:SettingsService}}"/>
</toms:BehaviorCollection>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>

277
ILSpy/App.xaml.cs

@ -1,277 +0,0 @@ @@ -1,277 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpyX.Analyzers;
using Medo.Application;
using TomsToolbox.Wpf.Styles;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Composition;
using TomsToolbox.Wpf.Composition;
using ICSharpCode.ILSpy.Themes;
using System.Globalization;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using TomsToolbox.Composition.MicrosoftExtensions;
using TomsToolbox.Essentials;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
internal static CommandLineArguments CommandLineArguments;
internal static readonly IList<ExceptionData> StartupExceptions = new List<ExceptionData>();
public static IExportProvider ExportProvider { get; private set; }
internal record ExceptionData(Exception Exception)
{
public string PluginName { get; init; }
}
public App()
{
var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
CommandLineArguments = CommandLineArguments.Create(cmdArgs);
// This is only a temporary, read only handle to the settings service to access the AllowMultipleInstances setting before DI is initialized.
// At runtime, you must use the service via DI!
var settingsService = new SettingsService();
bool forceSingleInstance = (CommandLineArguments.SingleInstance ?? true)
&& !settingsService.MiscSettings.AllowMultipleInstances;
if (forceSingleInstance)
{
SingleInstance.Attach(); // will auto-exit for second instance
SingleInstance.NewInstanceDetected += SingleInstance_NewInstanceDetected;
}
InitializeComponent();
if (!InitializeDependencyInjection(settingsService))
{
// There is something completely wrong with DI, probably some service registration is missing => nothing we can do to recover, so stop and shut down.
Exit += (_, _) => MessageBox.Show(StartupExceptions.FormatExceptions(), "Sorry we crashed!", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
Shutdown(1);
return;
}
if (!Debugger.IsAttached)
{
AppDomain.CurrentDomain.UnhandledException += ShowErrorBox;
Dispatcher.CurrentDispatcher.UnhandledException += Dispatcher_UnhandledException;
}
TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException;
SharpTreeNode.SetImagesProvider(new WpfWindowsTreeNodeImagesProvider());
Resources.RegisterDefaultStyles();
// Register the export provider so that it can be accessed from WPF/XAML components.
ExportProviderLocator.Register(ExportProvider);
// Add data templates registered via MEF.
Resources.MergedDictionaries.Add(DataTemplateManager.CreateDynamicDataTemplates(ExportProvider));
var sessionSettings = settingsService.SessionSettings;
ThemeManager.Current.Theme = sessionSettings.Theme;
if (!string.IsNullOrEmpty(sessionSettings.CurrentCulture))
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new(sessionSettings.CurrentCulture);
}
ILSpyTraceListener.Install();
if (CommandLineArguments.ArgumentsParser.IsShowingInformation)
{
MessageBox.Show(CommandLineArguments.ArgumentsParser.GetHelpText(), "ILSpy Command Line Arguments");
}
if (CommandLineArguments.ArgumentsParser.RemainingArguments.Any())
{
string unknownArguments = string.Join(", ", CommandLineArguments.ArgumentsParser.RemainingArguments);
MessageBox.Show(unknownArguments, "ILSpy Unknown Command Line Arguments Passed");
}
settingsService.AssemblyListManager.CreateDefaultAssemblyLists();
}
public new static App Current => (App)Application.Current;
public new MainWindow MainWindow {
get => (MainWindow)base.MainWindow;
private set => base.MainWindow = value;
}
private static void SingleInstance_NewInstanceDetected(object sender, NewInstanceEventArgs e) => ExportProvider.GetExportedValue<AssemblyTreeModel>().HandleSingleInstanceCommandLineArguments(e.Args).HandleExceptions();
static Assembly ResolvePluginDependencies(AssemblyLoadContext context, AssemblyName assemblyName)
{
var rootPath = Path.GetDirectoryName(typeof(App).Assembly.Location);
var assemblyFileName = Path.Combine(rootPath, assemblyName.Name + ".dll");
if (!File.Exists(assemblyFileName))
return null;
return context.LoadFromAssemblyPath(assemblyFileName);
}
private bool InitializeDependencyInjection(SettingsService settingsService)
{
// Add custom logic for resolution of dependencies.
// This necessary because the AssemblyLoadContext.LoadFromAssemblyPath and related methods,
// do not automatically load dependencies.
AssemblyLoadContext.Default.Resolving += ResolvePluginDependencies;
try
{
var services = new ServiceCollection();
var pluginDir = Path.GetDirectoryName(typeof(App).Module.FullyQualifiedName);
if (pluginDir != null)
{
foreach (var plugin in Directory.GetFiles(pluginDir, "*.Plugin.dll"))
{
var name = Path.GetFileNameWithoutExtension(plugin);
try
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(plugin);
services.BindExports(assembly);
}
catch (Exception ex)
{
// Cannot show MessageBox here, because WPF would crash with a XamlParseException
// Remember and show exceptions in text output, once MainWindow is properly initialized
StartupExceptions.Add(new(ex) { PluginName = name });
}
}
}
// Add the built-in parts: First, from ILSpyX
services.BindExports(typeof(IAnalyzer).Assembly);
// Then from ILSpy itself
services.BindExports(Assembly.GetExecutingAssembly());
// Add the settings service
services.AddSingleton(settingsService);
// Add the export provider
services.AddSingleton(_ => ExportProvider);
// Add the docking manager
services.AddSingleton(serviceProvider => serviceProvider.GetService<MainWindow>().DockManager);
services.AddTransient(serviceProvider => serviceProvider.GetService<AssemblyTreeModel>().AssemblyList);
var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
ExportProvider = new ExportProviderAdapter(serviceProvider);
Exit += (_, _) => serviceProvider.Dispose();
return true;
}
catch (Exception ex)
{
if (ex is AggregateException aggregate)
StartupExceptions.AddRange(aggregate.InnerExceptions.Select(item => new ExceptionData(ex)));
else
StartupExceptions.Add(new(ex));
return false;
}
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow = ExportProvider.GetExportedValue<MainWindow>();
MainWindow.Show();
}
void DotNet40_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
// On .NET 4.0, an unobserved exception in a task terminates the process unless we mark it as observed
e.SetObserved();
}
#region Exception Handling
static void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
UnhandledException(e.Exception);
e.Handled = true;
}
static void ShowErrorBox(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
if (ex != null)
{
UnhandledException(ex);
}
}
[ThreadStatic]
static bool showingError;
internal static void UnhandledException(Exception exception)
{
Debug.WriteLine(exception.ToString());
for (Exception ex = exception; ex != null; ex = ex.InnerException)
{
ReflectionTypeLoadException rtle = ex as ReflectionTypeLoadException;
if (rtle != null && rtle.LoaderExceptions.Length > 0)
{
exception = rtle.LoaderExceptions[0];
Debug.WriteLine(exception.ToString());
break;
}
}
if (showingError)
{
// Ignore re-entrant calls
// We run the risk of opening an infinite number of exception dialogs.
return;
}
showingError = true;
try
{
MessageBox.Show(exception.ToString(), "Sorry, we crashed");
}
finally
{
showingError = false;
}
}
#endregion
}
}

9
ILSpy/AppEnv/AppEnvironment.cs

@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
using System.Runtime.InteropServices;
namespace ICSharpCode.ILSpy.AppEnv
{
public static class AppEnvironment
{
public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
}
}

119
ILSpy/AppEnv/CommandLineArguments.cs

@ -1,119 +0,0 @@ @@ -1,119 +0,0 @@
// Copyright (c) 2011 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 McMaster.Extensions.CommandLineUtils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.ILSpy.AppEnv
{
public sealed class CommandLineArguments
{
// see /doc/Command Line.txt for details
public List<string> AssembliesToLoad = new List<string>();
public bool? SingleInstance;
public string NavigateTo;
public string Search;
public string Language;
public bool NoActivate;
public string ConfigFile;
public CommandLineApplication ArgumentsParser { get; }
private CommandLineArguments(CommandLineApplication app)
{
ArgumentsParser = app;
}
public static CommandLineArguments Create(IEnumerable<string> arguments)
{
var app = new CommandLineApplication() {
// https://natemcmaster.github.io/CommandLineUtils/docs/response-file-parsing.html?tabs=using-attributes
ResponseFileHandling = ResponseFileHandling.ParseArgsAsLineSeparated,
// Note: options are case-sensitive (!), and, default behavior would be UnrecognizedArgumentHandling.Throw on Parse()
UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.CollectAndContinue
};
app.HelpOption();
var instance = new CommandLineArguments(app);
try
{
var oForceNewInstance = app.Option("--newinstance",
"Start a new instance of ILSpy even if the user configuration is set to single-instance",
CommandOptionType.NoValue);
var oNavigateTo = app.Option<string>("-n|--navigateto <TYPENAME>",
"Navigates to the member specified by the given ID string.\r\nThe member is searched for only in the assemblies specified on the command line.\r\nExample: 'ILSpy ILSpy.exe --navigateto T:ICSharpCode.ILSpy.CommandLineArguments'",
CommandOptionType.SingleValue);
oNavigateTo.DefaultValue = null;
var oSearch = app.Option<string>("-s|--search <SEARCHTERM>",
"Search for t:TypeName, m:Member or c:Constant; use exact match (=term), 'should not contain' (-term) or 'must contain' (+term); use /reg(ular)?Ex(pressions)?/ or both - t:/Type(Name)?/...",
CommandOptionType.SingleValue);
oSearch.DefaultValue = null;
var oLanguage = app.Option<string>("-l|--language <LANGUAGEIDENTIFIER>",
"Selects the specified language.\r\nExample: 'ILSpy --language:C#' or 'ILSpy --language IL'",
CommandOptionType.SingleValue);
oLanguage.DefaultValue = null;
var oConfig = app.Option<string>("-c|--config <CONFIGFILENAME>",
"Provide a specific configuration file.\r\nExample: 'ILSpy --config myconfig.xml'",
CommandOptionType.SingleValue);
oConfig.DefaultValue = null;
var oNoActivate = app.Option("--noactivate",
"Do not activate the existing ILSpy instance. This option has no effect if a new ILSpy instance is being started.",
CommandOptionType.NoValue);
// https://natemcmaster.github.io/CommandLineUtils/docs/arguments.html#variable-numbers-of-arguments
// To enable this, MultipleValues must be set to true, and the argument must be the last one specified.
var files = app.Argument("Assemblies", "Assemblies to load", multipleValues: true);
app.Parse(arguments.ToArray());
if (oForceNewInstance.HasValue())
instance.SingleInstance = false;
instance.NavigateTo = oNavigateTo.ParsedValue;
instance.Search = oSearch.ParsedValue;
instance.Language = oLanguage.ParsedValue;
instance.ConfigFile = oConfig.ParsedValue;
if (oNoActivate.HasValue())
instance.NoActivate = true;
foreach (var assembly in files.Values)
{
if (!string.IsNullOrWhiteSpace(assembly))
instance.AssembliesToLoad.Add(assembly);
}
}
catch (Exception)
{
// Intentionally ignore exceptions if any, this is only added to always have an exception-free startup
}
return instance;
}
}
}

241
ILSpy/AppEnv/CommandLineTools.cs

@ -1,241 +0,0 @@ @@ -1,241 +0,0 @@
// Copyright (c) 2024 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.Collections.Generic;
using System.IO;
using System.Text;
namespace ICSharpCode.ILSpy.AppEnv
{
public class CommandLineTools
{
/// <summary>
/// Decodes a command line into an array of arguments according to the CommandLineToArgvW rules.
/// </summary>
/// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static string[] CommandLineToArgumentArray(string commandLine)
{
if (string.IsNullOrEmpty(commandLine))
return Array.Empty<string>();
var results = new List<string>();
ParseArgv.ParseArgumentsIntoList(commandLine, results);
return results.ToArray();
}
static readonly char[] charsNeedingQuoting = { ' ', '\t', '\n', '\v', '"' };
/// <summary>
/// Escapes a set of arguments according to the CommandLineToArgvW rules.
/// </summary>
/// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static string ArgumentArrayToCommandLine(params string[] arguments)
{
if (arguments == null)
return null;
StringBuilder b = new StringBuilder();
for (int i = 0; i < arguments.Length; i++)
{
if (i > 0)
b.Append(' ');
AppendArgument(b, arguments[i]);
}
return b.ToString();
}
static void AppendArgument(StringBuilder b, string arg)
{
if (arg == null)
{
return;
}
if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0)
{
b.Append(arg);
}
else
{
b.Append('"');
for (int j = 0; ; j++)
{
int backslashCount = 0;
while (j < arg.Length && arg[j] == '\\')
{
backslashCount++;
j++;
}
if (j == arg.Length)
{
b.Append('\\', backslashCount * 2);
break;
}
else if (arg[j] == '"')
{
b.Append('\\', backslashCount * 2 + 1);
b.Append('"');
}
else
{
b.Append('\\', backslashCount);
b.Append(arg[j]);
}
}
b.Append('"');
}
}
public static string FullyQualifyPath(string argument)
{
// Fully qualify the paths before passing them to another process,
// because that process might use a different current directory.
if (string.IsNullOrEmpty(argument) || argument[0] == '-')
return argument;
try
{
if (argument.StartsWith("@"))
{
return "@" + FullyQualifyPath(argument.Substring(1));
}
return Path.Combine(Environment.CurrentDirectory, argument);
}
catch (ArgumentException)
{
return argument;
}
}
}
// Source: https://github.com/dotnet/runtime/blob/bc9fc5a774d96f95abe0ea5c90fac48b38ed2e67/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L574-L606
// Minor adaptations in GetNextArgument (ValueStringBuilder replaced), otherwise kept as identical as possible
public static class ParseArgv
{
/// <summary>Parses a command-line argument string into a list of arguments.</summary>
/// <param name="arguments">The argument string.</param>
/// <param name="results">The list into which the component arguments should be stored.</param>
/// <remarks>
/// This follows the rules outlined in "Parsing C++ Command-Line Arguments" at
/// https://msdn.microsoft.com/en-us/library/17w5ykft.aspx.
/// </remarks>
public static void ParseArgumentsIntoList(string arguments, List<string> results)
{
// Iterate through all of the characters in the argument string.
for (int i = 0; i < arguments.Length; i++)
{
while (i < arguments.Length && (arguments[i] == ' ' || arguments[i] == '\t'))
i++;
if (i == arguments.Length)
break;
results.Add(GetNextArgument(arguments, ref i));
}
}
private static string GetNextArgument(string arguments, ref int i)
{
var currentArgument = new StringBuilder();
bool inQuotes = false;
while (i < arguments.Length)
{
// From the current position, iterate through contiguous backslashes.
int backslashCount = 0;
while (i < arguments.Length && arguments[i] == '\\')
{
i++;
backslashCount++;
}
if (backslashCount > 0)
{
if (i >= arguments.Length || arguments[i] != '"')
{
// Backslashes not followed by a double quote:
// they should all be treated as literal backslashes.
currentArgument.Append('\\', backslashCount);
}
else
{
// Backslashes followed by a double quote:
// - Output a literal slash for each complete pair of slashes
// - If one remains, use it to make the subsequent quote a literal.
currentArgument.Append('\\', backslashCount / 2);
if (backslashCount % 2 != 0)
{
currentArgument.Append('"');
i++;
}
}
continue;
}
char c = arguments[i];
// If this is a double quote, track whether we're inside of quotes or not.
// Anything within quotes will be treated as a single argument, even if
// it contains spaces.
if (c == '"')
{
if (inQuotes && i < arguments.Length - 1 && arguments[i + 1] == '"')
{
// Two consecutive double quotes inside an inQuotes region should result in a literal double quote
// (the parser is left in the inQuotes region).
// This behavior is not part of the spec of code:ParseArgumentsIntoList, but is compatible with CRT
// and .NET Framework.
currentArgument.Append('"');
i++;
}
else
{
inQuotes = !inQuotes;
}
i++;
continue;
}
// If this is a space/tab and we're not in quotes, we're done with the current
// argument, it should be added to the results and then reset for the next one.
if ((c == ' ' || c == '\t') && !inQuotes)
{
break;
}
// Nothing special; add the character to the current argument.
currentArgument.Append(c);
i++;
}
return currentArgument.ToString();
}
}
}

286
ILSpy/AppEnv/SingleInstance.cs

@ -1,286 +0,0 @@ @@ -1,286 +0,0 @@
// Source: https://github.com/medo64/Medo/blob/main/src/Medo/Application/SingleInstance.cs
/* Josip Medved <jmedved@jmedved.com> * www.medo64.com * MIT License */
//2022-12-01: Compatible with .NET 6 and 7
//2012-11-24: Suppressing bogus CA5122 warning (http://connect.microsoft.com/VisualStudio/feedback/details/729254/bogus-ca5122-warning-about-p-invoke-declarations-should-not-be-safe-critical)
//2010-10-07: Added IsOtherInstanceRunning method
//2008-11-14: Reworked code to use SafeHandle
//2008-04-11: Cleaned code to match FxCop 1.36 beta 2 (SpecifyMarshalingForPInvokeStringArguments, NestedTypesShouldNotBeVisible)
//2008-04-10: NewInstanceEventArgs is not nested class anymore
//2008-01-26: AutoExit parameter changed to NoAutoExit
//2008-01-08: Main method is now called Attach
//2008-01-06: System.Environment.Exit returns E_ABORT (0x80004004)
//2008-01-03: Added Resources
//2007-12-29: New version
#nullable enable
namespace Medo.Application;
using System;
using System.Diagnostics;
using System.IO.Pipes;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using ICSharpCode.ILSpy.AppEnv;
/// <summary>
/// Handles detection and communication of programs multiple instances.
/// This class is thread safe.
/// </summary>
public static class SingleInstance
{
private static Mutex? _mtxFirstInstance;
private static Thread? _thread;
private static readonly object _syncRoot = new();
/// <summary>
/// Returns true if this application is not already started.
/// Another instance is contacted via named pipe.
/// </summary>
/// <exception cref="InvalidOperationException">API call failed.</exception>
public static bool Attach()
{
return Attach(false);
}
private static string[] GetILSpyCommandLineArgs()
{
// Note: NO Skip(1) here because .Args property on SingleInstanceArguments does this for us
return Environment.GetCommandLineArgs().AsEnumerable()
.Select(CommandLineTools.FullyQualifyPath)
.ToArray();
}
/// <summary>
/// Returns true if this application is not already started.
/// Another instance is contacted via named pipe.
/// </summary>
/// <param name="noAutoExit">If true, application will exit after informing another instance.</param>
/// <exception cref="InvalidOperationException">API call failed.</exception>
public static bool Attach(bool noAutoExit)
{
lock (_syncRoot)
{
var isFirstInstance = false;
try
{
_mtxFirstInstance = new Mutex(initiallyOwned: true, @"Global\" + MutexName, out isFirstInstance);
if (isFirstInstance == false)
{ //we need to contact previous instance
var contentObject = new SingleInstanceArguments() {
CommandLine = Environment.CommandLine,
CommandLineArgs = GetILSpyCommandLineArgs(),
};
var contentBytes = JsonSerializer.SerializeToUtf8Bytes(contentObject);
using var clientPipe = new NamedPipeClientStream(".",
MutexName,
PipeDirection.Out,
PipeOptions.CurrentUserOnly | PipeOptions.WriteThrough);
clientPipe.Connect();
clientPipe.Write(contentBytes, 0, contentBytes.Length);
}
else
{ //there is no application already running.
_thread = new Thread(Run) {
Name = typeof(SingleInstance).FullName,
IsBackground = true
};
_thread.Start();
}
}
catch (Exception ex)
{
Trace.TraceWarning(ex.Message + " {Medo.Application.SingleInstance}");
}
if ((isFirstInstance == false) && (noAutoExit == false))
{
Trace.TraceInformation("Exit due to another instance running." + " [" + nameof(SingleInstance) + "]");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Environment.Exit(unchecked((int)0x80004004)); // E_ABORT(0x80004004)
}
else
{
Environment.Exit(114); // EALREADY(114)
}
}
return isFirstInstance;
}
}
private static string? _mutexName;
private static string MutexName {
get {
lock (_syncRoot)
{
if (_mutexName == null)
{
var assembly = Assembly.GetEntryAssembly();
var sbMutextName = new StringBuilder();
var assName = assembly?.GetName().Name;
if (assName != null)
{
sbMutextName.Append(assName, 0, Math.Min(assName.Length, 31));
sbMutextName.Append('.');
}
var sbHash = new StringBuilder();
sbHash.AppendLine(Environment.MachineName);
sbHash.AppendLine(Environment.UserName);
if (assembly != null)
{
sbHash.AppendLine(assembly.FullName);
sbHash.AppendLine(assembly.Location);
}
else
{
var args = Environment.GetCommandLineArgs();
if (args.Length > 0)
{ sbHash.AppendLine(args[0]); }
}
foreach (var b in SHA256.HashData(Encoding.UTF8.GetBytes(sbHash.ToString())))
{
if (sbMutextName.Length == 63)
{ sbMutextName.AppendFormat("{0:X1}", b >> 4); } // just take the first nubble
if (sbMutextName.Length == 64)
{ break; }
sbMutextName.AppendFormat("{0:X2}", b);
}
_mutexName = sbMutextName.ToString();
}
return _mutexName;
}
}
}
/// <summary>
/// Gets whether there is another instance running.
/// It temporary creates mutex.
/// </summary>
public static bool IsOtherInstanceRunning {
get {
lock (_syncRoot)
{
if (_mtxFirstInstance != null)
{
return false; //no other instance is running
}
else
{
var tempInstance = new Mutex(true, MutexName, out var isFirstInstance);
tempInstance.Close();
return (isFirstInstance == false);
}
}
}
}
/// <summary>
/// Occurs in first instance when new instance is detected.
/// </summary>
public static event EventHandler<NewInstanceEventArgs>? NewInstanceDetected;
/// <summary>
/// Thread function.
/// </summary>
private static void Run()
{
using var serverPipe = new NamedPipeServerStream(MutexName,
PipeDirection.In,
maxNumberOfServerInstances: 1,
PipeTransmissionMode.Byte,
PipeOptions.CurrentUserOnly | PipeOptions.WriteThrough);
while (_mtxFirstInstance != null)
{
try
{
if (!serverPipe.IsConnected)
{ serverPipe.WaitForConnection(); }
var contentObject = JsonSerializer.Deserialize<SingleInstanceArguments>(serverPipe);
serverPipe.Disconnect();
if (contentObject != null)
{
NewInstanceDetected?.Invoke(null,
new NewInstanceEventArgs(
contentObject.CommandLine,
contentObject.CommandLineArgs));
}
}
catch (Exception ex)
{
Trace.TraceWarning(ex.Message + " [" + nameof(SingleInstance) + "]");
Thread.Sleep(100);
}
}
}
[Serializable]
private sealed record SingleInstanceArguments
{ // just a storage
[JsonInclude]
public required string CommandLine;
[JsonInclude]
public required string[] CommandLineArgs;
}
}
/// <summary>
/// Arguments for newly detected application instance.
/// </summary>
public sealed class NewInstanceEventArgs : EventArgs
{
/// <summary>
/// Creates new instance.
/// </summary>
/// <param name="commandLine">Command line.</param>
/// <param name="commandLineArgs">String array containing the command line arguments in the same format as Environment.GetCommandLineArgs.</param>
internal NewInstanceEventArgs(string commandLine, string[] commandLineArgs)
{
CommandLine = commandLine;
_commandLineArgs = new string[commandLineArgs.Length];
Array.Copy(commandLineArgs, _commandLineArgs, _commandLineArgs.Length);
}
/// <summary>
/// Gets the command line.
/// </summary>
public string CommandLine { get; }
private readonly string[] _commandLineArgs;
/// <summary>
/// Returns a string array containing the command line arguments.
/// </summary>
public string[] GetCommandLineArgs()
{
var argCopy = new string[_commandLineArgs.Length];
Array.Copy(_commandLineArgs, argCopy, argCopy.Length);
return argCopy;
}
/// <summary>
/// Gets a string array containing the command line arguments without the name of exectuable.
/// </summary>
public string[] Args {
get {
var argCopy = new string[_commandLineArgs.Length - 1];
Array.Copy(_commandLineArgs, 1, argCopy, 0, argCopy.Length);
return argCopy;
}
}
}

55
ILSpy/AssemblyTree/AssemblyListPane.xaml

@ -1,55 +0,0 @@ @@ -1,55 +0,0 @@
<treeView:SharpTreeView x:Class="ICSharpCode.ILSpy.AssemblyTree.AssemblyListPane"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:treeView="clr-namespace:ICSharpCode.ILSpy.Controls.TreeView"
xmlns:treeNodes="clr-namespace:ICSharpCode.ILSpy.TreeNodes"
xmlns:assemblyTree="clr-namespace:ICSharpCode.ILSpy.AssemblyTree"
xmlns:toms="urn:TomsToolbox"
xmlns:viewModels="clr-namespace:ICSharpCode.ILSpy.ViewModels"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance assemblyTree:AssemblyTreeModel}"
AutomationProperties.Name="Assemblies and Classes"
ShowRoot="False"
AllowDropOrder="True"
AllowDrop="True"
BorderThickness="0" Visibility="Visible"
Root="{Binding Root}"
toms:MultiSelectorExtensions.SelectionBinding="{Binding SelectedItems, Mode=TwoWay}"
viewModels:Pane.IsActive="{Binding IsActive}">
<treeView:SharpTreeView.ItemContainerStyle>
<Style TargetType="treeView:SharpTreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type treeView:SharpTreeViewItem}"
d:DataContext="{d:DesignInstance treeNodes:ILSpyTreeNode}">
<Border Background="Transparent">
<Border Background="{TemplateBinding Background}">
<treeView:SharpTreeNodeView x:Name="nodeView" HorizontalAlignment="Left" />
</Border>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsAutoLoaded}" Value="True">
<Setter Property="Foreground" Value="SteelBlue" />
</DataTrigger>
<DataTrigger Binding="{Binding IsPublicAPI}" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</DataTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="nodeView" Property="TextBackground"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter TargetName="nodeView" Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="nodeView" Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</treeView:SharpTreeView.ItemContainerStyle>
</treeView:SharpTreeView>

86
ILSpy/AssemblyTree/AssemblyListPane.xaml.cs

@ -1,86 +0,0 @@ @@ -1,86 +0,0 @@
// Copyright (c) 2024 Tom Englert 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.Composition;
using System.Windows;
using System.Windows.Threading;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Wpf;
using TomsToolbox.Wpf.Composition.AttributedModel;
namespace ICSharpCode.ILSpy.AssemblyTree
{
/// <summary>
/// Interaction logic for AssemblyListPane.xaml
/// </summary>
[DataTemplate(typeof(AssemblyTreeModel))]
[NonShared]
public partial class AssemblyListPane
{
public AssemblyListPane()
{
InitializeComponent();
ContextMenuProvider.Add(this);
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == DataContextProperty)
{
if (e.NewValue is not AssemblyTreeModel model)
return;
model.SetActiveView(this);
// If there is already a selected item in the model, we need to scroll it into view, so it can be selected in the UI.
var selected = model.SelectedItem;
if (selected != null)
{
this.BeginInvoke(DispatcherPriority.Background, () => {
ScrollIntoView(selected);
this.SelectedItem = selected;
});
}
}
else if (e.Property == Pane.IsActiveProperty)
{
if (!true.Equals(e.NewValue))
return;
if (SelectedItem is SharpTreeNode selectedItem)
{
// defer focusing, so it does not interfere with selection via mouse click
this.BeginInvoke(() => {
if (this.SelectedItem == selectedItem)
FocusNode(selectedItem);
});
}
else
{
Focus();
}
}
}
}
}

1111
ILSpy/AssemblyTree/AssemblyTreeModel.cs

File diff suppressed because it is too large Load Diff

83
ILSpy/AssemblyTree/AssemblyTreeModel.wpf.cs

@ -1,83 +0,0 @@ @@ -1,83 +0,0 @@
// 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.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Navigation;
using System.Windows.Threading;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpyX;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.AssemblyTree
{
public partial class AssemblyTreeModel
{
public AssemblyTreeModel(SettingsService settingsService, LanguageService languageService, IExportProvider exportProvider)
{
this.settingsService = settingsService;
this.languageService = languageService;
this.exportProvider = exportProvider;
Title = Resources.Assemblies;
ContentId = PaneContentId;
IsCloseable = false;
ShortcutKey = new KeyGesture(Key.F6);
MessageBus<NavigateToReferenceEventArgs>.Subscribers += JumpToReference;
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
MessageBus<ApplySessionSettingsEventArgs>.Subscribers += ApplySessionSettings;
MessageBus<ActiveTabPageChangedEventArgs>.Subscribers += ActiveTabPageChanged;
MessageBus<TabPagesCollectionChangedEventArgs>.Subscribers += (_, e) => history.RemoveAll(s => !DockWorkspace.TabPages.Contains(s.TabPage));
MessageBus<ResetLayoutEventArgs>.Subscribers += ResetLayout;
MessageBus<NavigateToEventArgs>.Subscribers += (_, e) => NavigateTo(e.Request, e.InNewTabPage);
MessageBus<MainWindowLoadedEventArgs>.Subscribers += (_, _) => {
Initialize();
Show();
};
EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler((_, e) => NavigateTo(e)));
refreshThrottle = new(DispatcherPriority.Background, RefreshInternal);
AssemblyList = settingsService.CreateEmptyAssemblyList();
}
private static void LoadInitialAssemblies(AssemblyList assemblyList)
{
// Called when loading an empty assembly list; so that
// the user can see something initially.
System.Reflection.Assembly[] initialAssemblies = {
typeof(object).Assembly,
typeof(Uri).Assembly,
typeof(System.Linq.Enumerable).Assembly,
typeof(System.Xml.XmlDocument).Assembly,
typeof(System.Windows.Markup.MarkupExtension).Assembly,
typeof(System.Windows.Rect).Assembly,
typeof(System.Windows.UIElement).Assembly,
typeof(System.Windows.FrameworkElement).Assembly
};
foreach (System.Reflection.Assembly asm in initialAssemblies)
assemblyList.OpenAssembly(asm.Location);
}
}
}

168
ILSpy/AvalonEdit/ITextMarker.cs

@ -1,168 +0,0 @@ @@ -1,168 +0,0 @@
// Copyright (c) 2014 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.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace ICSharpCode.ILSpy.AvalonEdit
{
/// <summary>
/// Represents a text marker.
/// </summary>
public interface ITextMarker
{
/// <summary>
/// Gets the start offset of the marked text region.
/// </summary>
int StartOffset { get; }
/// <summary>
/// Gets the end offset of the marked text region.
/// </summary>
int EndOffset { get; }
/// <summary>
/// Gets the length of the marked region.
/// </summary>
int Length { get; }
/// <summary>
/// Deletes the text marker.
/// </summary>
void Delete();
/// <summary>
/// Gets whether the text marker was deleted.
/// </summary>
bool IsDeleted { get; }
/// <summary>
/// Event that occurs when the text marker is deleted.
/// </summary>
event EventHandler Deleted;
/// <summary>
/// Gets/Sets the background color.
/// </summary>
Color? BackgroundColor { get; set; }
/// <summary>
/// Gets/Sets the foreground color.
/// </summary>
Color? ForegroundColor { get; set; }
/// <summary>
/// Gets/Sets the font weight.
/// </summary>
FontWeight? FontWeight { get; set; }
/// <summary>
/// Gets/Sets the font style.
/// </summary>
FontStyle? FontStyle { get; set; }
/// <summary>
/// Gets/Sets the type of the marker. Use TextMarkerType.None for normal markers.
/// </summary>
TextMarkerTypes MarkerTypes { get; set; }
/// <summary>
/// Gets/Sets the color of the marker.
/// </summary>
Color MarkerColor { get; set; }
/// <summary>
/// Gets/Sets an object with additional data for this text marker.
/// </summary>
object Tag { get; set; }
/// <summary>
/// Gets/Sets an object that will be displayed as tooltip in the text editor.
/// </summary>
object ToolTip { get; set; }
}
[Flags]
public enum TextMarkerTypes
{
/// <summary>
/// Use no marker
/// </summary>
None = 0x0000,
/// <summary>
/// Use squiggly underline marker
/// </summary>
SquigglyUnderline = 0x001,
/// <summary>
/// Normal underline.
/// </summary>
NormalUnderline = 0x002,
/// <summary>
/// Dotted underline.
/// </summary>
DottedUnderline = 0x004,
/// <summary>
/// Horizontal line in the scroll bar.
/// </summary>
LineInScrollBar = 0x0100,
/// <summary>
/// Small triangle in the scroll bar, pointing to the right.
/// </summary>
ScrollBarRightTriangle = 0x0400,
/// <summary>
/// Small triangle in the scroll bar, pointing to the left.
/// </summary>
ScrollBarLeftTriangle = 0x0800,
/// <summary>
/// Small circle in the scroll bar.
/// </summary>
CircleInScrollBar = 0x1000
}
public interface ITextMarkerService
{
/// <summary>
/// Creates a new text marker. The text marker will be invisible at first,
/// you need to set one of the Color properties to make it visible.
/// </summary>
ITextMarker Create(int startOffset, int length);
/// <summary>
/// Gets the list of text markers.
/// </summary>
IEnumerable<ITextMarker> TextMarkers { get; }
/// <summary>
/// Removes the specified text marker.
/// </summary>
void Remove(ITextMarker marker);
/// <summary>
/// Removes all text markers that match the condition.
/// </summary>
void RemoveAll(Predicate<ITextMarker> predicate);
/// <summary>
/// Finds all text markers at the specified offset.
/// </summary>
IEnumerable<ITextMarker> GetMarkersAtOffset(int offset);
}
}

372
ILSpy/AvalonEdit/TextMarkerService.cs

@ -1,372 +0,0 @@ @@ -1,372 +0,0 @@
// Copyright (c) 2014 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.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
namespace ICSharpCode.ILSpy.AvalonEdit
{
using TextView = ICSharpCode.AvalonEdit.Rendering.TextView;
/// <summary>
/// Handles the text markers for a code editor.
/// </summary>
sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
{
TextSegmentCollection<TextMarker> markers;
TextView textView;
public TextMarkerService(TextView textView)
{
if (textView == null)
throw new ArgumentNullException(nameof(textView));
this.textView = textView;
textView.DocumentChanged += OnDocumentChanged;
OnDocumentChanged(null, null);
}
void OnDocumentChanged(object sender, EventArgs e)
{
if (textView.Document != null)
markers = new TextSegmentCollection<TextMarker>(textView.Document);
else
markers = null;
}
#region ITextMarkerService
public ITextMarker Create(int startOffset, int length)
{
if (markers == null)
throw new InvalidOperationException("Cannot create a marker when not attached to a document");
int textLength = textView.Document.TextLength;
if (startOffset < 0 || startOffset > textLength)
throw new ArgumentOutOfRangeException(nameof(startOffset), startOffset, "Value must be between 0 and " + textLength);
if (length < 0 || startOffset + length > textLength)
throw new ArgumentOutOfRangeException(nameof(length), length, "length must not be negative and startOffset+length must not be after the end of the document");
TextMarker m = new TextMarker(this, startOffset, length);
markers.Add(m);
// no need to mark segment for redraw: the text marker is invisible until a property is set
return m;
}
public IEnumerable<ITextMarker> GetMarkersAtOffset(int offset)
{
if (markers == null)
return Enumerable.Empty<ITextMarker>();
else
return markers.FindSegmentsContaining(offset);
}
public IEnumerable<ITextMarker> TextMarkers {
get { return markers ?? Enumerable.Empty<ITextMarker>(); }
}
public void RemoveAll(Predicate<ITextMarker> predicate)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));
if (markers != null)
{
foreach (TextMarker m in markers.ToArray())
{
if (predicate(m))
Remove(m);
}
}
}
public void Remove(ITextMarker marker)
{
if (marker == null)
throw new ArgumentNullException(nameof(marker));
TextMarker m = marker as TextMarker;
if (markers != null && markers.Remove(m))
{
Redraw(m);
m.OnDeleted();
}
}
/// <summary>
/// Redraws the specified text segment.
/// </summary>
internal void Redraw(ISegment segment)
{
textView.Redraw(segment, DispatcherPriority.Normal);
RedrawRequested?.Invoke(this, EventArgs.Empty);
}
public event EventHandler RedrawRequested;
#endregion
#region DocumentColorizingTransformer
protected override void ColorizeLine(DocumentLine line)
{
if (markers == null)
return;
int lineStart = line.Offset;
int lineEnd = lineStart + line.Length;
foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length))
{
Brush foregroundBrush = null;
if (marker.ForegroundColor != null)
{
foregroundBrush = new SolidColorBrush(marker.ForegroundColor.Value);
foregroundBrush.Freeze();
}
ChangeLinePart(
Math.Max(marker.StartOffset, lineStart),
Math.Min(marker.EndOffset, lineEnd),
element => {
if (foregroundBrush != null)
{
element.TextRunProperties.SetForegroundBrush(foregroundBrush);
}
Typeface tf = element.TextRunProperties.Typeface;
element.TextRunProperties.SetTypeface(new Typeface(
tf.FontFamily,
marker.FontStyle ?? tf.Style,
marker.FontWeight ?? tf.Weight,
tf.Stretch
));
}
);
}
}
#endregion
#region IBackgroundRenderer
public KnownLayer Layer {
get {
// draw behind selection
return KnownLayer.Selection;
}
}
public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingContext drawingContext)
{
if (textView == null)
throw new ArgumentNullException(nameof(textView));
if (drawingContext == null)
throw new ArgumentNullException(nameof(drawingContext));
if (markers == null || !textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart))
{
if (marker.BackgroundColor != null)
{
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.CornerRadius = 3;
geoBuilder.AddSegment(textView, marker);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null)
{
Color color = marker.BackgroundColor.Value;
SolidColorBrush brush = new SolidColorBrush(color);
brush.Freeze();
drawingContext.DrawGeometry(brush, null, geometry);
}
}
var underlineMarkerTypes = TextMarkerTypes.SquigglyUnderline | TextMarkerTypes.NormalUnderline | TextMarkerTypes.DottedUnderline;
if ((marker.MarkerTypes & underlineMarkerTypes) != 0)
{
foreach (Rect r in BackgroundGeometryBuilder.GetRectsForSegment(textView, marker))
{
Point startPoint = r.BottomLeft;
Point endPoint = r.BottomRight;
Brush usedBrush = new SolidColorBrush(marker.MarkerColor);
usedBrush.Freeze();
if ((marker.MarkerTypes & TextMarkerTypes.SquigglyUnderline) != 0)
{
double offset = 2.5;
int count = Math.Max((int)((endPoint.X - startPoint.X) / offset) + 1, 4);
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext ctx = geometry.Open())
{
ctx.BeginFigure(startPoint, false, false);
ctx.PolyLineTo(CreatePoints(startPoint, endPoint, offset, count).ToArray(), true, false);
}
geometry.Freeze();
Pen usedPen = new Pen(usedBrush, 1);
usedPen.Freeze();
drawingContext.DrawGeometry(Brushes.Transparent, usedPen, geometry);
}
if ((marker.MarkerTypes & TextMarkerTypes.NormalUnderline) != 0)
{
Pen usedPen = new Pen(usedBrush, 1);
usedPen.Freeze();
drawingContext.DrawLine(usedPen, startPoint, endPoint);
}
if ((marker.MarkerTypes & TextMarkerTypes.DottedUnderline) != 0)
{
Pen usedPen = new Pen(usedBrush, 1);
usedPen.DashStyle = DashStyles.Dot;
usedPen.Freeze();
drawingContext.DrawLine(usedPen, startPoint, endPoint);
}
}
}
}
}
IEnumerable<Point> CreatePoints(Point start, Point end, double offset, int count)
{
for (int i = 0; i < count; i++)
yield return new Point(start.X + i * offset, start.Y - ((i + 1) % 2 == 0 ? offset : 0));
}
#endregion
}
sealed class TextMarker : TextSegment, ITextMarker
{
readonly TextMarkerService service;
public TextMarker(TextMarkerService service, int startOffset, int length)
{
if (service == null)
throw new ArgumentNullException(nameof(service));
this.service = service;
this.StartOffset = startOffset;
this.Length = length;
this.markerTypes = TextMarkerTypes.None;
}
public event EventHandler Deleted;
public bool IsDeleted {
get { return !this.IsConnectedToCollection; }
}
public void Delete()
{
service.Remove(this);
}
internal void OnDeleted()
{
Deleted?.Invoke(this, EventArgs.Empty);
}
void Redraw()
{
service.Redraw(this);
}
Color? backgroundColor;
public Color? BackgroundColor {
get { return backgroundColor; }
set {
if (backgroundColor != value)
{
backgroundColor = value;
Redraw();
}
}
}
Color? foregroundColor;
public Color? ForegroundColor {
get { return foregroundColor; }
set {
if (foregroundColor != value)
{
foregroundColor = value;
Redraw();
}
}
}
FontWeight? fontWeight;
public FontWeight? FontWeight {
get { return fontWeight; }
set {
if (fontWeight != value)
{
fontWeight = value;
Redraw();
}
}
}
FontStyle? fontStyle;
public FontStyle? FontStyle {
get { return fontStyle; }
set {
if (fontStyle != value)
{
fontStyle = value;
Redraw();
}
}
}
public object Tag { get; set; }
TextMarkerTypes markerTypes;
public TextMarkerTypes MarkerTypes {
get { return markerTypes; }
set {
if (markerTypes != value)
{
markerTypes = value;
Redraw();
}
}
}
Color markerColor;
public Color MarkerColor {
get { return markerColor; }
set {
if (markerColor != value)
{
markerColor = value;
Redraw();
}
}
}
public object ToolTip { get; set; }
}
}

65
ILSpy/Commands/BrowseBackCommand.cs

@ -1,65 +0,0 @@ @@ -1,65 +0,0 @@
// Copyright (c) 2011 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.Collections;
using System.Composition;
using System.Linq;
using System.Windows.Input;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportToolbarCommand(ToolTip = nameof(Resources.Back), ToolbarIcon = "Images/Back", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 0)]
[Shared]
sealed class BrowseBackCommand : CommandWrapper, IProvideParameterList
{
readonly AssemblyTreeModel assemblyTreeModel;
public BrowseBackCommand(AssemblyTreeModel assemblyTreeModel)
: base(NavigationCommands.BrowseBack)
{
this.assemblyTreeModel = assemblyTreeModel;
}
protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
base.OnCanExecute(sender, e);
e.Handled = true;
e.CanExecute = assemblyTreeModel.CanNavigateBack;
}
protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
if (assemblyTreeModel.CanNavigateBack)
{
e.Handled = true;
assemblyTreeModel.NavigateHistory(false, e.Parameter as NavigationState);
}
}
public IEnumerable ParameterList => assemblyTreeModel.GetNavigateHistory(false).Reverse();
public object GetParameterText(object parameter)
{
return (parameter as NavigationState)?.NavigationText;
}
}
}

65
ILSpy/Commands/BrowseForwardCommand.cs

@ -1,65 +0,0 @@ @@ -1,65 +0,0 @@
// Copyright (c) 2011 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.Collections;
using System.Composition;
using System.Linq;
using System.Windows.Input;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportToolbarCommand(ToolTip = nameof(Resources.Forward), ToolbarIcon = "Images/Forward", ToolbarCategory = nameof(Resources.Navigation), ToolbarOrder = 1)]
[Shared]
sealed class BrowseForwardCommand : CommandWrapper, IProvideParameterList
{
private readonly AssemblyTreeModel assemblyTreeModel;
public BrowseForwardCommand(AssemblyTreeModel assemblyTreeModel)
: base(NavigationCommands.BrowseForward)
{
this.assemblyTreeModel = assemblyTreeModel;
}
protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
base.OnCanExecute(sender, e);
e.Handled = true;
e.CanExecute = assemblyTreeModel.CanNavigateForward;
}
protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
if (assemblyTreeModel.CanNavigateForward)
{
e.Handled = true;
assemblyTreeModel.NavigateHistory(true, e.Parameter as NavigationState);
}
}
public IEnumerable ParameterList => assemblyTreeModel.GetNavigateHistory(true).Reverse();
public object GetParameterText(object parameter)
{
return (parameter as NavigationState)?.NavigationText;
}
}
}

34
ILSpy/Commands/CheckForUpdatesCommand.cs

@ -1,34 +0,0 @@ @@ -1,34 +0,0 @@
// Copyright (c) 2011 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.Composition;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._Help), Header = nameof(Resources._CheckUpdates), MenuOrder = 5000)]
[Shared]
sealed class CheckForUpdatesCommand : SimpleCommand
{
public override void Execute(object parameter)
{
MessageBus.Send(this, new CheckIfUpdateAvailableEventArgs(notify: true));
}
}
}

66
ILSpy/Commands/CommandWrapper.cs

@ -1,66 +0,0 @@ @@ -1,66 +0,0 @@
// Copyright (c) 2011 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.Windows;
using System.Windows.Input;
namespace ICSharpCode.ILSpy
{
abstract class CommandWrapper : ICommand
{
private readonly ICommand wrappedCommand;
protected CommandWrapper(ICommand wrappedCommand)
{
this.wrappedCommand = wrappedCommand;
Application.Current.MainWindow?.CommandBindings.Add(new CommandBinding(wrappedCommand, OnExecute, OnCanExecute));
}
public static ICommand Unwrap(ICommand command)
{
if (command is CommandWrapper w)
return w.wrappedCommand;
return command;
}
public event EventHandler CanExecuteChanged {
add { wrappedCommand.CanExecuteChanged += value; }
remove { wrappedCommand.CanExecuteChanged -= value; }
}
public void Execute(object parameter)
{
wrappedCommand.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return wrappedCommand.CanExecute(parameter);
}
protected abstract void OnExecute(object sender, ExecutedRoutedEventArgs e);
protected virtual void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
}

53
ILSpy/Commands/CompareContextMenuEntry.cs

@ -1,53 +0,0 @@ @@ -1,53 +0,0 @@
// Copyright (c) 2025 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.Composition;
using System.Threading.Tasks;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpy.Views;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = "Compare...", Order = 9999)]
[Shared]
internal sealed class CompareContextMenuEntry(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
var left = ((AssemblyTreeNode)context.SelectedTreeNodes[0]).LoadedAssembly;
var right = ((AssemblyTreeNode)context.SelectedTreeNodes[1]).LoadedAssembly;
var tabPage = dockWorkspace.AddTabPage();
CompareViewModel.Show(tabPage, left, right, assemblyTreeModel);
}
public bool IsEnabled(TextViewContext context)
{
return true;
}
public bool IsVisible(TextViewContext context)
{
return context.SelectedTreeNodes is [AssemblyTreeNode { LoadedAssembly.IsLoadedAsValidAssembly: true }, AssemblyTreeNode { LoadedAssembly.IsLoadedAsValidAssembly: true }];
}
}
}

50
ILSpy/Commands/CopyFullyQualifiedNameContextMenuEntry.cs

@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Windows;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = nameof(Resources.CopyName), Icon = "images/Copy", Order = 9999)]
[Shared]
public class CopyFullyQualifiedNameContextMenuEntry : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
return GetMemberNodeFromContext(context) != null;
}
public bool IsEnabled(TextViewContext context) => true;
public void Execute(TextViewContext context)
{
var member = GetMemberNodeFromContext(context)?.Member;
if (member == null)
return;
Clipboard.SetText(member.ReflectionName);
}
private IMemberTreeNode GetMemberNodeFromContext(TextViewContext context)
{
return context.SelectedTreeNodes?.Length == 1 ? context.SelectedTreeNodes[0] as IMemberTreeNode : null;
}
}
}

124
ILSpy/Commands/CreateDiagramContextMenuEntry.cs

@ -1,124 +0,0 @@ @@ -1,124 +0,0 @@
// Copyright (c) 2024 Christoph Wille 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.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpyX.MermaidDiagrammer;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy.TextView
{
[ExportContextMenuEntry(Header = nameof(Resources._CreateDiagram), Category = nameof(Resources.Save), Icon = "Images/Save")]
[Shared]
sealed class CreateDiagramContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly;
if (assembly == null)
return;
var selectedPath = SelectDestinationFolder();
if (string.IsNullOrEmpty(selectedPath))
return;
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new() {
EnableHyperlinks = true
};
Stopwatch stopwatch = Stopwatch.StartNew();
try
{
var command = new GenerateHtmlDiagrammer {
Assembly = assembly.FileName,
OutputFolder = selectedPath
};
command.Run();
}
catch (OperationCanceledException)
{
output.WriteLine();
output.WriteLine(Resources.GenerationWasCancelled);
throw;
}
stopwatch.Stop();
output.WriteLine(Resources.GenerationCompleteInSeconds, stopwatch.Elapsed.TotalSeconds.ToString("F1"));
output.WriteLine();
output.WriteLine("Learn more: " + "https://github.com/icsharpcode/ILSpy/wiki/Diagramming#tips-for-using-the-html-diagrammer");
output.WriteLine();
var diagramHtml = Path.Combine(selectedPath, "index.html");
output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItem(diagramHtml); });
output.WriteLine();
return output;
}, ct), Properties.Resources.CreatingDiagram).Then(dockWorkspace.ShowText).HandleExceptions();
return;
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context)
{
return context.SelectedTreeNodes?.Length == 1
&& context.SelectedTreeNodes?.FirstOrDefault() is AssemblyTreeNode tn
&& tn.LoadedAssembly.IsLoadedAsValidAssembly;
}
static string SelectDestinationFolder()
{
OpenFolderDialog dialog = new();
dialog.Multiselect = false;
dialog.Title = "Select target folder";
if (dialog.ShowDialog() != true)
{
return null;
}
string selectedPath = Path.GetDirectoryName(dialog.FolderName);
bool directoryNotEmpty;
try
{
directoryNotEmpty = 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.",
"Target directory not accessible",
MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
return dialog.FolderName;
}
}
}

122
ILSpy/Commands/DecompileAllCommand.cs

@ -1,122 +0,0 @@ @@ -1,122 +0,0 @@
// Copyright (c) 2011 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.
#if DEBUG
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
[Shared]
sealed class DecompileAllCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
return System.IO.Directory.Exists("c:\\temp\\decompiled");
}
public override void Execute(object parameter)
{
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(
Partitioner.Create(assemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate (LoadedAssembly asm) {
if (!asm.HasLoadError)
{
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;
using (var writer = new System.IO.StreamWriter("c:\\temp\\decompiled\\" + asm.ShortName + ".cs"))
{
try
{
var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
options.CancellationToken = ct;
options.FullDecompilation = true;
new CSharpLanguage().DecompileAssembly(asm, new PlainTextOutput(writer), options);
}
catch (Exception ex)
{
writer.WriteLine(ex.ToString());
exception = ex;
}
}
lock (output)
{
output.Write(asm.ShortName + " - " + w.Elapsed);
if (exception != null)
{
output.Write(" - ");
output.Write(exception.GetType().Name);
}
output.WriteLine();
}
}
});
return output;
}, ct)).Then(dockWorkspace.ShowText).HandleExceptions();
}
}
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile100x), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)]
[Shared]
sealed class Decompile100TimesCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) : SimpleCommand
{
public override void Execute(object parameter)
{
const int numRuns = 100;
var language = languageService.Language;
var nodes = assemblyTreeModel.SelectedNodes.ToArray();
var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
options.CancellationToken = ct;
Stopwatch w = Stopwatch.StartNew();
for (int i = 0; i < numRuns; ++i)
{
foreach (var node in nodes)
{
node.Decompile(language, new PlainTextOutput(), options);
}
}
w.Stop();
AvalonEditTextOutput output = new AvalonEditTextOutput();
double msPerRun = w.Elapsed.TotalMilliseconds / numRuns;
output.Write($"Average time: {msPerRun.ToString("f1")}ms\n");
return output;
}, ct)).Then(output => dockWorkspace.ShowText(output)).HandleExceptions();
}
}
}
#endif

72
ILSpy/Commands/DecompileCommand.cs

@ -1,72 +0,0 @@ @@ -1,72 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy.Commands
{
[ExportContextMenuEntry(Header = nameof(Resources.Decompile), Order = 10)]
[Shared]
class DecompileCommand : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return context.Reference?.Reference is IEntity;
return context.SelectedTreeNodes.Length == 1 && context.SelectedTreeNodes.All(n => n is IMemberTreeNode);
}
public bool IsEnabled(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return context.Reference?.Reference is IEntity;
foreach (IMemberTreeNode node in context.SelectedTreeNodes)
{
if (!IsValidReference(node.Member))
return false;
}
return true;
}
bool IsValidReference(object reference)
{
return reference is IEntity;
}
public void Execute(TextViewContext context)
{
IEntity selection = null;
if (context.SelectedTreeNodes?[0] is IMemberTreeNode node)
{
selection = node.Member;
}
else if (context.Reference?.Reference is IEntity entity)
{
selection = entity;
}
if (selection != null)
MessageBus.Send(this, new NavigateToReferenceEventArgs(selection));
}
}
}

96
ILSpy/Commands/DecompileInNewViewCommand.cs

@ -1,96 +0,0 @@ @@ -1,96 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Composition;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using TomsToolbox.Essentials;
namespace ICSharpCode.ILSpy.Commands
{
[ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), InputGestureText = "MMB", Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)]
[Shared]
internal sealed class DecompileInNewViewCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
return context.SelectedTreeNodes != null || context.Reference?.Reference is IEntity;
}
public bool IsEnabled(TextViewContext context)
{
return GetNodes(context).Any();
}
public void Execute(TextViewContext context)
{
DecompileNodes(GetNodes(context).ToArray());
}
IEnumerable<ILSpyTreeNode> GetNodes(TextViewContext context)
{
if (context.SelectedTreeNodes != null)
{
if (context.TreeView.DataContext != assemblyTreeModel)
{
return context.SelectedTreeNodes.OfType<IMemberTreeNode>().Select(FindTreeNode).ExceptNullItems();
}
else
{
return context.SelectedTreeNodes.OfType<ILSpyTreeNode>();
}
}
else if (context.Reference?.Reference is IEntity entity)
{
if (assemblyTreeModel.FindTreeNode(entity) is { } node)
{
return new[] { node };
}
}
return Array.Empty<ILSpyTreeNode>();
ILSpyTreeNode FindTreeNode(IMemberTreeNode node)
{
if (node is ILSpyTreeNode ilspyNode)
return ilspyNode;
return assemblyTreeModel.FindTreeNode(node.Member);
}
}
void DecompileNodes(ILSpyTreeNode[] nodes)
{
if (nodes.Length == 0)
return;
dockWorkspace.ActiveTabPage = dockWorkspace.AddTabPage();
if (assemblyTreeModel.SelectedItems.SequenceEqual(nodes))
assemblyTreeModel.DecompileSelectedNodes();
else
assemblyTreeModel.SelectNodes(nodes);
}
}
}

73
ILSpy/Commands/DelegateCommand.cs

@ -1,73 +0,0 @@ @@ -1,73 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace ICSharpCode.ILSpy.Commands
{
public class DelegateCommand : ICommand
{
private readonly Action action;
private readonly Func<bool> canExecute;
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action action)
: this(action, () => true)
{
}
public DelegateCommand(Action action, Func<bool> canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute();
}
public void Execute(object parameter)
{
action();
}
}
public class DelegateCommand<T> : ICommand
{
private readonly Action<T> action;
private readonly Func<T, bool> canExecute;
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public DelegateCommand(Action<T> action)
: this(action, _ => true)
{
}
public DelegateCommand(Action<T> action, Func<T, bool> canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute((T)parameter);
}
public void Execute(object parameter)
{
action((T)parameter);
}
}
}

89
ILSpy/Commands/DisassembleAllCommand.cs

@ -1,89 +0,0 @@ @@ -1,89 +0,0 @@
// Copyright (c) 2011 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.
#if DEBUG
using System;
using System.Collections.Concurrent;
using System.Composition;
using System.Diagnostics;
using System.Threading.Tasks;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
[Shared]
sealed class DisassembleAllCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
return System.IO.Directory.Exists("c:\\temp\\disassembled");
}
public override void Execute(object parameter)
{
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new();
Parallel.ForEach(
Partitioner.Create(assemblyTreeModel.AssemblyList.GetAssemblies(), loadBalance: true),
new() { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
asm => {
if (!asm.HasLoadError)
{
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;
using (var writer = new System.IO.StreamWriter("c:\\temp\\disassembled\\" + asm.Text.Replace("(", "").Replace(")", "").Replace(' ', '_') + ".il"))
{
try
{
var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
options.FullDecompilation = true;
options.CancellationToken = ct;
new ILLanguage(dockWorkspace).DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), options);
}
catch (Exception ex)
{
writer.WriteLine(ex.ToString());
exception = ex;
}
}
lock (output)
{
output.Write(asm.ShortName + " - " + w.Elapsed);
if (exception != null)
{
output.Write(" - ");
output.Write(exception.GetType().Name);
}
output.WriteLine();
}
}
});
return output;
}, ct)).Then(dockWorkspace.ShowText).HandleExceptions();
}
}
}
#endif

33
ILSpy/Commands/ExitCommand.cs

@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
// Copyright (c) 2011 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.Composition;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.E_xit), MenuOrder = 99999, MenuCategory = nameof(Resources.Exit))]
[Shared]
sealed class ExitCommand(MainWindow mainWindow) : SimpleCommand
{
public override void Execute(object parameter)
{
mainWindow.Close();
}
}
}

113
ILSpy/Commands/ExportCommandAttribute.cs

@ -1,113 +0,0 @@ @@ -1,113 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Windows.Input;
namespace ICSharpCode.ILSpy
{
#region Toolbar
public interface IToolbarCommandMetadata
{
string ToolbarIcon { get; }
string ToolTip { get; }
string ToolbarCategory { get; }
object Tag { get; }
double ToolbarOrder { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportToolbarCommandAttribute : ExportAttribute, IToolbarCommandMetadata
{
public ExportToolbarCommandAttribute()
: base("ToolbarCommand", typeof(ICommand))
{
}
public string ToolTip { get; set; }
public string ToolbarIcon { get; set; }
public string ToolbarCategory { get; set; }
public double ToolbarOrder { get; set; }
public object Tag { get; set; }
}
#endregion
#region Main Menu
public interface IMainMenuCommandMetadata
{
string MenuID { get; }
string MenuIcon { get; }
string Header { get; }
string ParentMenuID { get; }
string MenuCategory { get; }
string InputGestureText { get; }
bool IsEnabled { get; }
double MenuOrder { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportMainMenuCommandAttribute : ExportAttribute, IMainMenuCommandMetadata
{
public ExportMainMenuCommandAttribute()
: base("MainMenuCommand", typeof(ICommand))
{
}
/// <summary>
/// Gets/Sets the ID of this menu item. Menu entries are not required to have an ID,
/// however, setting it allows to declare nested menu structures.
/// The built-in menus have the IDs "_File", "_View", "_Window" and "_Help".
/// Plugin authors are advised to use GUIDs as identifiers to prevent conflicts.
/// <para/>
/// NOTE: Defining cycles (for example by accidentally setting <see cref="MenuID"/> equal to <see cref="ParentMenuID"/>)
/// will lead to a stack-overflow and crash of ILSpy at startup.
/// </summary>
public string MenuID { get; set; }
public string MenuIcon { get; set; }
public string Header { get; set; }
/// <summary>
/// Gets/Sets the parent of this menu item. All menu items sharing the same parent will be displayed as sub-menu items.
/// If this property is set to <see langword="null"/>, the menu item is displayed in the top-level menu.
/// The built-in menus have the IDs "_File", "_View", "_Window" and "_Help".
/// <para/>
/// NOTE: Defining cycles (for example by accidentally setting <see cref="MenuID"/> equal to <see cref="ParentMenuID"/>)
/// will lead to a stack-overflow and crash of ILSpy at startup.
/// </summary>
public string ParentMenuID { get; set; }
public string MenuCategory { get; set; }
public string InputGestureText { get; set; }
public bool IsEnabled { get; set; } = true;
public double MenuOrder { get; set; }
}
#endregion
#region Tool Panes
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class ExportToolPaneAttribute : ExportAttribute
{
public ExportToolPaneAttribute()
: base("ToolPane", typeof(ViewModels.ToolPaneModel))
{
}
}
#endregion
}

237
ILSpy/Commands/ExtractPackageEntryContextMenuEntry.cs

@ -1,237 +0,0 @@ @@ -1,237 +0,0 @@
// Copyright (c) 2021 Siegfried Pammer
//
// 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.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView;
using Microsoft.Win32;
using static ICSharpCode.ILSpyX.LoadedPackage;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = nameof(Resources.ExtractPackageEntry), Category = nameof(Resources.Save), Icon = "Images/Save")]
[Shared]
sealed class ExtractPackageEntryContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
var selectedNodes = Array.FindAll(context.SelectedTreeNodes, IsBundleItem);
// Get root assembly to infer the initial directory for the save dialog.
var bundleNode = selectedNodes.FirstOrDefault()?.Ancestors().OfType<AssemblyTreeNode>()
.FirstOrDefault(asm => asm.PackageEntry == null);
if (bundleNode == null)
return;
if (selectedNodes is [AssemblyTreeNode { PackageEntry: { } assembly }])
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(assembly.Name));
dlg.Filter = ".NET assemblies|*.dll;*.exe;*.winmd" + Resources.AllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName);
if (dlg.ShowDialog() == true)
Save(dockWorkspace, selectedNodes, dlg.FileName, true);
}
else if (selectedNodes is [ResourceTreeNode { Resource: { } resource }])
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(WholeProjectDecompiler.SanitizeFileName(resource.Name));
dlg.Filter = Resources.AllFiles[1..];
dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName);
if (dlg.ShowDialog() == true)
Save(dockWorkspace, selectedNodes, dlg.FileName, true);
}
else
{
OpenFolderDialog dlg = new OpenFolderDialog();
dlg.InitialDirectory = Path.GetDirectoryName(bundleNode.LoadedAssembly.FileName);
if (dlg.ShowDialog() != true)
return;
string folderName = dlg.FolderName;
if (Directory.EnumerateFileSystemEntries(folderName).Any())
{
var result = MessageBox.Show(
Resources.AssemblySaveCodeDirectoryNotEmpty,
Resources.AssemblySaveCodeDirectoryNotEmptyTitle,
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (result == MessageBoxResult.No)
return;
}
Save(dockWorkspace, selectedNodes, folderName, false);
}
}
internal static void Save(DockWorkspace dockWorkspace, ICollection<SharpTreeNode> nodes, string path, bool isFile)
{
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Stopwatch stopwatch = Stopwatch.StartNew();
Dictionary<string, int> fileNameCounts = new Dictionary<string, int>(Platform.FileNameComparer);
foreach (var (entry, fileName) in CollectAllFiles(nodes))
{
string actualFileName = WholeProjectDecompiler.SanitizeFileName(fileName);
while (fileNameCounts.TryGetValue(actualFileName, out int index))
{
index++;
fileNameCounts[actualFileName] = index;
actualFileName = Path.ChangeExtension(actualFileName, index + Path.GetExtension(actualFileName));
}
if (!fileNameCounts.ContainsKey(actualFileName))
{
fileNameCounts[actualFileName] = 1;
}
SaveEntry(output, entry, Path.Combine(path, actualFileName));
}
stopwatch.Stop();
output.WriteLine(Resources.GenerationCompleteInSeconds, stopwatch.Elapsed.TotalSeconds.ToString("F1"));
output.WriteLine();
// If we have written files, open explorer and select them grouped by folder; otherwise fall back to opening containing folder.
if (isFile && File.Exists(path))
output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItem(path); });
else
output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolder(path); });
output.WriteLine();
return output;
}, ct)).Then(dockWorkspace.ShowText).HandleExceptions();
static IEnumerable<(PackageEntry Entry, string TargetFileName)> CollectAllFiles(ICollection<SharpTreeNode> nodes)
{
foreach (var node in nodes)
{
if (node is AssemblyTreeNode { PackageEntry: { } assembly })
{
yield return (assembly, Path.GetFileName(assembly.FullName));
}
else if (node is ResourceTreeNode { Resource: PackageEntry { } resource })
{
yield return (resource, Path.GetFileName(resource.FullName));
}
else if (node is AssemblyTreeNode { PackageKind: not null } asm)
{
var package = asm.LoadedAssembly.GetLoadResultAsync().GetAwaiter().GetResult().Package;
foreach (var entry in package.Entries)
{
yield return (entry, entry.FullName);
}
}
else if (node is PackageFolderTreeNode folder)
{
int prefixLength = 0;
PackageFolder current = folder.Folder;
if (nodes.Count > 1)
current = current.Parent;
while (current != null)
{
prefixLength += current.Name.Length + 1;
current = current.Parent;
}
if (prefixLength > 0)
prefixLength--;
foreach (var item in TreeTraversal.PreOrder(folder.Folder, f => f.Folders).SelectMany(f => f.Entries))
{
yield return (item, item.FullName.Substring(prefixLength));
}
}
}
}
}
static void SaveEntry(ITextOutput output, PackageEntry entry, string targetFileName)
{
output.Write(entry.Name + ": ");
using Stream stream = entry.TryOpenStream();
if (stream == null)
{
output.WriteLine("Could not open stream!");
return;
}
Directory.CreateDirectory(Path.GetDirectoryName(targetFileName));
stream.Position = 0;
using FileStream fileStream = new FileStream(targetFileName, FileMode.OpenOrCreate);
stream.CopyTo(fileStream);
output.WriteLine("Written to " + targetFileName);
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context) => context.SelectedTreeNodes?.Any(IsBundleItem) == true;
static bool IsBundleItem(SharpTreeNode node)
{
if (node is AssemblyTreeNode { PackageEntry: { } } or PackageFolderTreeNode)
return true;
if (node is ResourceTreeNode { Resource: PackageEntry { } resource } && resource.PackageQualifiedFileName.StartsWith("bundle://"))
return true;
return false;
}
}
[ExportContextMenuEntry(Header = nameof(Resources.ExtractAllPackageEntries), Category = nameof(Resources.Save), Icon = "Images/Save")]
[Shared]
sealed class ExtractAllPackageEntriesContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes is not [AssemblyTreeNode { PackageEntry: null } asm])
return;
OpenFolderDialog dlg = new OpenFolderDialog();
dlg.InitialDirectory = Path.GetDirectoryName(asm.LoadedAssembly.FileName);
if (dlg.ShowDialog() != true)
return;
string folderName = dlg.FolderName;
if (Directory.EnumerateFileSystemEntries(folderName).Any())
{
var result = MessageBox.Show(
Resources.AssemblySaveCodeDirectoryNotEmpty,
Resources.AssemblySaveCodeDirectoryNotEmptyTitle,
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (result == MessageBoxResult.No)
return;
}
ExtractPackageEntryContextMenuEntry.Save(dockWorkspace, [asm], folderName, false);
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context)
{
return context.SelectedTreeNodes is [AssemblyTreeNode { PackageEntry: null, PackageKind: PackageKind.Bundle }];
}
}
}

210
ILSpy/Commands/GeneratePdbContextMenuEntry.cs

@ -1,210 +0,0 @@ @@ -1,210 +0,0 @@
// Copyright (c) 2018 Siegfried Pammer
//
// 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.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = nameof(Resources.GeneratePortable))]
[Shared]
class GeneratePdbContextMenuEntry(LanguageService languageService, DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
var selectedNodes = context.SelectedTreeNodes?.OfType<AssemblyTreeNode>().ToArray();
if (selectedNodes == null || selectedNodes.Length == 0)
return;
GeneratePdbForAssemblies(selectedNodes.Select(n => n.LoadedAssembly), languageService, dockWorkspace);
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context)
{
var selectedNodes = context.SelectedTreeNodes;
return selectedNodes?.Any() == true
&& selectedNodes.All(n => n is AssemblyTreeNode asm && asm.LoadedAssembly.IsLoadedAsValidAssembly);
}
internal static void GeneratePdbForAssemblies(IEnumerable<LoadedAssembly> assemblies, LanguageService languageService, DockWorkspace dockWorkspace)
{
var assemblyArray = assemblies?.Where(a => a != null).ToArray() ?? [];
if (assemblyArray == null || assemblyArray.Length == 0)
return;
// Ensure at least one assembly supports PDB generation
var supported = new Dictionary<LoadedAssembly, PEFile>();
var unsupported = new List<LoadedAssembly>();
foreach (var a in assemblyArray)
{
try
{
var file = a.GetMetadataFileOrNull() as PEFile;
if (PortablePdbWriter.HasCodeViewDebugDirectoryEntry(file))
supported.Add(a, file);
else
unsupported.Add(a);
}
catch
{
unsupported.Add(a);
}
}
if (supported.Count == 0)
{
// none can be generated
string msg = string.Format(Resources.CannotCreatePDBFile, ":" + Environment.NewLine +
string.Join(Environment.NewLine, unsupported.Select(u => Path.GetFileName(u.FileName)))
+ Environment.NewLine);
MessageBox.Show(msg);
return;
}
// Ask for target folder
var dlg = new OpenFolderDialog();
dlg.Title = Resources.SelectPDBOutputFolder;
if (dlg.ShowDialog() != true || string.IsNullOrWhiteSpace(dlg.FolderName))
return;
string targetFolder = dlg.FolderName;
DecompilationOptions options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Stopwatch totalWatch = Stopwatch.StartNew();
options.CancellationToken = ct;
int total = assemblyArray.Length;
int processed = 0;
foreach (var assembly in assemblyArray)
{
// only process supported assemblies
if (!supported.TryGetValue(assembly, out var file))
{
output.WriteLine(string.Format(Resources.CannotCreatePDBFile, Path.GetFileName(assembly.FileName)));
processed++;
if (options.Progress != null)
{
options.Progress.Report(new DecompilationProgress {
Title = string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName)),
TotalUnits = total,
UnitsCompleted = processed
});
}
continue;
}
string fileName = Path.Combine(targetFolder, WholeProjectDecompiler.CleanUpFileName(assembly.ShortName, ".pdb"));
try
{
using (FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(options.DecompilerSettings.AutoLoadAssemblyReferences), options.DecompilerSettings);
decompiler.CancellationToken = ct;
PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream, progress: options.Progress, currentProgressTitle: string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName)));
}
output.WriteLine(string.Format(Resources.GeneratedPDBFile, fileName));
}
catch (OperationCanceledException)
{
output.WriteLine();
output.WriteLine(Resources.GenerationWasCancelled);
throw;
}
catch (Exception ex)
{
output.WriteLine(string.Format(Resources.GenerationFailedForAssembly, assembly.FileName, ex.Message));
}
processed++;
if (options.Progress != null)
{
options.Progress.Report(new DecompilationProgress {
Title = string.Format(Resources.GeneratingPortablePDB, Path.GetFileName(assembly.FileName)),
TotalUnits = total,
UnitsCompleted = processed
});
}
}
totalWatch.Stop();
output.WriteLine();
output.WriteLine(Resources.GenerationCompleteInSeconds, totalWatch.Elapsed.TotalSeconds.ToString("F1"));
output.WriteLine();
// Select all generated pdb files in explorer
var generatedFiles = assemblyArray
.Select(a => Path.Combine(targetFolder, WholeProjectDecompiler.CleanUpFileName(a.ShortName, ".pdb")))
.Where(File.Exists)
.ToList();
if (generatedFiles.Any())
{
output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolderAndSelectItems(generatedFiles); });
}
else
{
output.AddButton(null, Resources.OpenExplorer, delegate { ShellHelper.OpenFolder(targetFolder); });
}
output.WriteLine();
return output;
}, ct)).Then(dockWorkspace.ShowText).HandleExceptions();
}
}
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.GeneratePortable), MenuCategory = nameof(Resources.Save))]
[Shared]
class GeneratePdbMainMenuEntry(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
return assemblyTreeModel.SelectedNodes?.Any() == true
&& assemblyTreeModel.SelectedNodes?.All(n => n is AssemblyTreeNode tn && !tn.LoadedAssembly.HasLoadError) == true;
}
public override void Execute(object parameter)
{
var selectedNodes = assemblyTreeModel.SelectedNodes?.OfType<AssemblyTreeNode>().ToArray();
if (selectedNodes == null || selectedNodes.Length == 0)
return;
GeneratePdbContextMenuEntry.GeneratePdbForAssemblies(selectedNodes.Select(n => n.LoadedAssembly), languageService, dockWorkspace);
}
}
}

30
ILSpy/Commands/IProtocolHandler.cs

@ -1,30 +0,0 @@ @@ -1,30 +0,0 @@
// Copyright (c) 2018 Siegfried Pammer
//
// 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.Reflection.Metadata;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy
{
public interface IProtocolHandler
{
ILSpyTreeNode Resolve(string protocol, MetadataFile module, Handle handle, out bool newTabPage);
}
}

46
ILSpy/Commands/ManageAssemblyListsCommand.cs

@ -1,46 +0,0 @@ @@ -1,46 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Windows;
using System.Windows.Data;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ManageAssembly_Lists), MenuIcon = "Images/AssemblyList", MenuCategory = nameof(Resources.Open), MenuOrder = 1.7)]
[Shared]
sealed class ManageAssemblyListsCommand(SettingsService settingsService) : SimpleCommand, IProvideParameterBinding
{
public override void Execute(object parameter)
{
ManageAssemblyListsDialog dlg = new(settingsService) {
Owner = parameter as Window
};
dlg.ShowDialog();
}
public Binding ParameterBinding => new() {
RelativeSource = new(RelativeSourceMode.FindAncestor) {
AncestorType = typeof(Window)
}
};
}
}

57
ILSpy/Commands/OpenCommand.cs

@ -1,57 +0,0 @@ @@ -1,57 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Windows.Input;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy
{
[ExportToolbarCommand(ToolTip = nameof(Resources.Open), ToolbarIcon = "Images/Open", ToolbarCategory = nameof(Resources.Open), ToolbarOrder = 0)]
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._Open), MenuIcon = "Images/Open", MenuCategory = nameof(Resources.Open), MenuOrder = 0)]
[Shared]
sealed class OpenCommand : CommandWrapper
{
private readonly AssemblyTreeModel assemblyTreeModel;
public OpenCommand(AssemblyTreeModel assemblyTreeModel)
: base(ApplicationCommands.Open)
{
this.assemblyTreeModel = assemblyTreeModel;
}
protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
OpenFileDialog dlg = new OpenFileDialog {
Filter = ".NET assemblies|*.dll;*.exe;*.winmd;*.wasm|Nuget Packages (*.nupkg)|*.nupkg|Portable Program Database (*.pdb)|*.pdb|All files|*.*",
Multiselect = true,
RestoreDirectory = true
};
if (dlg.ShowDialog() == true)
{
assemblyTreeModel.OpenFiles(dlg.FileNames);
}
}
}
}

48
ILSpy/Commands/OpenFromGacCommand.cs

@ -1,48 +0,0 @@ @@ -1,48 +0,0 @@
// Copyright (c) 2011 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.Composition;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.OpenFrom_GAC), MenuIcon = "Images/AssemblyListGAC", MenuCategory = nameof(Resources.Open), MenuOrder = 1)]
[Shared]
sealed class OpenFromGacCommand(AssemblyTreeModel assemblyTreeModel, MainWindow mainWindow) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
return AppEnvironment.IsWindows;
}
public override void Execute(object parameter)
{
OpenFromGacDialog dlg = new() {
Owner = mainWindow
};
if (dlg.ShowDialog() == true)
{
assemblyTreeModel.OpenFiles(dlg.SelectedFileNames);
}
}
}
}

97
ILSpy/Commands/Pdb2XmlCommand.cs

@ -1,97 +0,0 @@ @@ -1,97 +0,0 @@
// Copyright (c) 2011 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.
#if DEBUG && WINDOWS
using System.Collections.Generic;
using System.Composition;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.DiaSymReader.Tools;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDumpPDBAsXML), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)]
[Shared]
sealed class Pdb2XmlCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
var selectedNodes = assemblyTreeModel.SelectedNodes;
return selectedNodes?.Any() == true
&& selectedNodes.All(n => n is AssemblyTreeNode asm && !asm.LoadedAssembly.HasLoadError);
}
public override void Execute(object parameter)
{
Execute(assemblyTreeModel.SelectedNodes.OfType<AssemblyTreeNode>(), dockWorkspace);
}
internal static void Execute(IEnumerable<AssemblyTreeNode> nodes, DockWorkspace dockWorkspace)
{
var highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml");
var options = PdbToXmlOptions.IncludeEmbeddedSources | PdbToXmlOptions.IncludeMethodSpans | PdbToXmlOptions.IncludeTokens;
dockWorkspace.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
var writer = new TextOutputWriter(output);
foreach (var node in nodes)
{
string pdbFileName = Path.ChangeExtension(node.LoadedAssembly.FileName, ".pdb");
if (!File.Exists(pdbFileName))
continue;
using (var pdbStream = File.OpenRead(pdbFileName))
using (var peStream = File.OpenRead(node.LoadedAssembly.FileName))
PdbToXmlConverter.ToXml(writer, pdbStream, peStream, options);
}
return output;
}, ct)).Then(output => dockWorkspace.ShowNodes(output, null, highlighting)).HandleExceptions();
}
}
[ExportContextMenuEntry(Header = nameof(Resources.DEBUGDumpPDBAsXML))]
[Shared]
class Pdb2XmlCommandContextMenuEntry(DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
Pdb2XmlCommand.Execute(context.SelectedTreeNodes.OfType<AssemblyTreeNode>(), dockWorkspace);
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context)
{
var selectedNodes = context.SelectedTreeNodes;
return selectedNodes?.Any() == true
&& selectedNodes.All(n => n is AssemblyTreeNode asm && asm.LoadedAssembly.IsLoadedAsValidAssembly);
}
}
}
#endif

45
ILSpy/Commands/RefreshCommand.cs

@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Windows.Input;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportToolbarCommand(ToolTip = nameof(Resources.RefreshCommand_ReloadAssemblies), ToolbarIcon = "Images/Refresh", ToolbarCategory = nameof(Resources.Open), ToolbarOrder = 2)]
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._Reload), MenuIcon = "Images/Refresh", MenuCategory = nameof(Resources.Open), MenuOrder = 2)]
[Shared]
sealed class RefreshCommand : CommandWrapper
{
private readonly AssemblyTreeModel assemblyTreeModel;
public RefreshCommand(AssemblyTreeModel assemblyTreeModel)
: base(NavigationCommands.Refresh)
{
this.assemblyTreeModel = assemblyTreeModel;
}
protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
assemblyTreeModel.Refresh();
}
}
}

70
ILSpy/Commands/RemoveAssembliesWithLoadErrors.cs

@ -1,70 +0,0 @@ @@ -1,70 +0,0 @@
// Copyright (c) 2018 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.Composition;
using System.Linq;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._RemoveAssembliesWithLoadErrors), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)]
[Shared]
class RemoveAssembliesWithLoadErrors(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
{
public override bool CanExecute(object parameter)
{
return assemblyTreeModel.AssemblyList.GetAssemblies().Any(l => l.HasLoadError);
}
public override void Execute(object parameter)
{
foreach (var assembly in assemblyTreeModel.AssemblyList.GetAssemblies())
{
if (!assembly.HasLoadError)
continue;
var node = assemblyTreeModel.FindAssemblyNode(assembly);
if (node != null && node.CanDelete())
node.Delete();
}
}
}
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.ClearAssemblyList), MenuCategory = nameof(Resources.Remove), MenuOrder = 2.6)]
[Shared]
class ClearAssemblyList : SimpleCommand
{
private readonly AssemblyTreeModel assemblyTreeModel;
public ClearAssemblyList(AssemblyTreeModel assemblyTreeModel)
{
this.assemblyTreeModel = assemblyTreeModel;
}
public override bool CanExecute(object parameter)
{
return assemblyTreeModel.AssemblyList.Count > 0;
}
public override void Execute(object parameter)
{
assemblyTreeModel.AssemblyList.Clear();
}
}
}

140
ILSpy/Commands/SaveCodeContextMenuEntry.cs

@ -1,140 +0,0 @@ @@ -1,140 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Composition;
using System.IO;
using System.Linq;
using System.Windows;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
using ICSharpCode.ILSpyX.TreeView;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy.TextView
{
[ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/Save")]
[Shared]
sealed class SaveCodeContextMenuEntry(LanguageService languageService, DockWorkspace dockWorkspace) : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
Execute(context.SelectedTreeNodes, languageService, dockWorkspace);
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context)
{
return CanExecute(context.SelectedTreeNodes);
}
public static bool CanExecute(IReadOnlyList<SharpTreeNode> selectedNodes)
{
if (selectedNodes == null || selectedNodes.Any(n => !(n is ILSpyTreeNode)))
return false;
return selectedNodes.Count == 1
|| (selectedNodes.Count > 1 && (selectedNodes.All(n => n is AssemblyTreeNode) || selectedNodes.All(n => n is IMemberTreeNode)));
}
public static void Execute(IReadOnlyList<SharpTreeNode> selectedNodes, LanguageService languageService, DockWorkspace dockWorkspace)
{
var currentLanguage = languageService.Language;
var tabPage = dockWorkspace.ActiveTabPage;
tabPage.ShowTextView(textView => {
if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection)
{
// if there's only one treenode selected
// we will invoke the custom Save logic
if (singleSelection.Save(tabPage))
return;
}
else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode { LoadedAssembly.IsLoadedAsValidAssembly: true }))
{
var selectedPath = SelectSolutionFile();
if (!string.IsNullOrEmpty(selectedPath))
{
var assemblies = selectedNodes.OfType<AssemblyTreeNode>()
.Select(n => n.LoadedAssembly)
.Where(a => a.IsLoadedAsValidAssembly).ToList();
SolutionWriter.CreateSolution(tabPage, textView, selectedPath, currentLanguage, assemblies);
}
return;
}
// Fallback: if nobody was able to handle the request, use default behavior.
// try to save all nodes to disk.
var options = dockWorkspace.ActiveTabPage.CreateDecompilationOptions();
options.FullDecompilation = true;
textView.SaveToDisk(currentLanguage, selectedNodes.OfType<ILSpyTreeNode>(), 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>
static string SelectSolutionFile()
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = "Solution.sln";
dlg.Filter = Resources.VisualStudioSolutionFileSlnAllFiles;
if (dlg.ShowDialog() != true)
{
return null;
}
string selectedPath = Path.GetDirectoryName(dlg.FileName);
bool directoryNotEmpty;
try
{
directoryNotEmpty = 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);
return null;
}
if (directoryNotEmpty)
{
var result = MessageBox.Show(
Resources.AssemblySaveCodeDirectoryNotEmpty,
Resources.AssemblySaveCodeDirectoryNotEmptyTitle,
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (result == MessageBoxResult.No)
return null; // -> abort
}
return dlg.FileName;
}
}
}

45
ILSpy/Commands/SaveCommand.cs

@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
// Copyright (c) 2011 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.Composition;
using System.Linq;
using System.Windows.Input;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources._SaveCode), MenuIcon = "Images/Save", MenuCategory = nameof(Resources.Save), MenuOrder = 0)]
[Shared]
sealed class SaveCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) : CommandWrapper(ApplicationCommands.Save)
{
protected override void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.Handled = true;
e.CanExecute = SaveCodeContextMenuEntry.CanExecute(assemblyTreeModel.SelectedNodes.ToList());
}
protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
{
SaveCodeContextMenuEntry.Execute(assemblyTreeModel.SelectedNodes.ToList(), languageService, dockWorkspace);
}
}
}

96
ILSpy/Commands/ScopeSearchToAssembly.cs

@ -1,96 +0,0 @@ @@ -1,96 +0,0 @@
// Copyright (c) 2021 Siegfried Pammer
//
// 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
using System;
using System.Composition;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = nameof(Resources.ScopeSearchToThisAssembly), Category = nameof(Resources.Analyze), Order = 9999)]
[Shared]
public class ScopeSearchToAssembly : IContextMenuEntry
{
private readonly SearchPaneModel searchPane;
public ScopeSearchToAssembly(SearchPaneModel searchPane)
{
this.searchPane = searchPane;
}
public void Execute(TextViewContext context)
{
// asmName cannot be null here, because Execute is only called if IsEnabled/IsVisible return true.
string asmName = GetAssembly(context)!;
string searchTerm = searchPane.SearchTerm;
string[] args = CommandLineTools.CommandLineToArgumentArray(searchTerm);
bool replaced = false;
for (int i = 0; i < args.Length; i++)
{
if (args[i].StartsWith("inassembly:", StringComparison.OrdinalIgnoreCase))
{
args[i] = "inassembly:" + asmName;
replaced = true;
break;
}
}
if (!replaced)
{
searchTerm += " inassembly:" + asmName;
}
else
{
searchTerm = CommandLineTools.ArgumentArrayToCommandLine(args);
}
searchPane.SearchTerm = searchTerm;
}
public bool IsEnabled(TextViewContext context)
{
return GetAssembly(context) != null;
}
public bool IsVisible(TextViewContext context)
{
return GetAssembly(context) != null;
}
string? GetAssembly(TextViewContext context)
{
if (context.Reference?.Reference is IEntity entity)
return entity.ParentModule?.AssemblyName;
if (context.SelectedTreeNodes?.Length != 1)
return null;
switch (context.SelectedTreeNodes[0])
{
case AssemblyTreeNode tn:
return tn.LoadedAssembly.ShortName;
case IMemberTreeNode member:
return member.Member?.ParentModule?.AssemblyName;
default:
return null;
}
}
}
}

94
ILSpy/Commands/ScopeSearchToNamespace.cs

@ -1,94 +0,0 @@ @@ -1,94 +0,0 @@
// Copyright (c) 2021 Siegfried Pammer
//
// 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.Composition;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = nameof(Resources.ScopeSearchToThisNamespace), Category = nameof(Resources.Analyze), Order = 9999)]
[Shared]
public class ScopeSearchToNamespace : IContextMenuEntry
{
private readonly SearchPaneModel searchPane;
public ScopeSearchToNamespace(SearchPaneModel searchPane)
{
this.searchPane = searchPane;
}
public void Execute(TextViewContext context)
{
string ns = GetNamespace(context);
string searchTerm = searchPane.SearchTerm;
string[] args = CommandLineTools.CommandLineToArgumentArray(searchTerm);
bool replaced = false;
for (int i = 0; i < args.Length; i++)
{
if (args[i].StartsWith("innamespace:", StringComparison.OrdinalIgnoreCase))
{
args[i] = "innamespace:" + ns;
replaced = true;
break;
}
}
if (!replaced)
{
searchTerm += " innamespace:" + ns;
}
else
{
searchTerm = CommandLineTools.ArgumentArrayToCommandLine(args);
}
searchPane.SearchTerm = searchTerm;
}
public bool IsEnabled(TextViewContext context)
{
return GetNamespace(context) != null;
}
public bool IsVisible(TextViewContext context)
{
return GetNamespace(context) != null;
}
string GetNamespace(TextViewContext context)
{
if (context.Reference?.Reference is IEntity entity)
return entity.Namespace;
if (context.SelectedTreeNodes?.Length != 1)
return null;
switch (context.SelectedTreeNodes[0])
{
case NamespaceTreeNode tn:
return tn.Name;
case IMemberTreeNode member:
return member.Member.Namespace;
default:
return null;
}
}
}
}

141
ILSpy/Commands/SearchMsdnContextMenuEntry.cs

@ -1,141 +0,0 @@ @@ -1,141 +0,0 @@
// Copyright (c) 2011 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.Linq;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy
{
using System.Composition;
using ICSharpCode.Decompiler.TypeSystem;
[ExportContextMenuEntry(Header = nameof(Resources.SearchMSDN), Icon = "images/SearchMsdn", Order = 9999)]
[Shared]
internal sealed class SearchMsdnContextMenuEntry : IContextMenuEntry
{
private static string msdnAddress = "https://docs.microsoft.com/dotnet/api/{0}";
public bool IsVisible(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return false;
return context.SelectedTreeNodes.All(
n => n is NamespaceTreeNode
|| n is TypeTreeNode
|| n is EventTreeNode
|| n is FieldTreeNode
|| n is PropertyTreeNode
|| n is MethodTreeNode);
}
public bool IsEnabled(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return false;
foreach (var node in context.SelectedTreeNodes)
{
if (node is TypeTreeNode typeNode && !typeNode.IsPublicAPI)
return false;
if (node is EventTreeNode eventNode && (!eventNode.IsPublicAPI || !IsAccessible(eventNode.EventDefinition)))
return false;
if (node is FieldTreeNode fieldNode && (!fieldNode.IsPublicAPI || !IsAccessible(fieldNode.FieldDefinition) || IsDelegateOrEnumMember(fieldNode.FieldDefinition)))
return false;
if (node is PropertyTreeNode propertyNode && (!propertyNode.IsPublicAPI || !IsAccessible(propertyNode.PropertyDefinition)))
return false;
if (node is MethodTreeNode methodNode && (!methodNode.IsPublicAPI || !IsAccessible(methodNode.MethodDefinition) || IsDelegateOrEnumMember(methodNode.MethodDefinition)))
return false;
if (node is NamespaceTreeNode namespaceNode && string.IsNullOrEmpty(namespaceNode.Name))
return false;
}
return true;
}
bool IsAccessible(IEntity entity)
{
if (entity.DeclaringTypeDefinition == null)
return false;
switch (entity.DeclaringTypeDefinition.Accessibility)
{
case Accessibility.Public:
case Accessibility.Protected:
case Accessibility.ProtectedOrInternal:
return true;
default:
return false;
}
}
bool IsDelegateOrEnumMember(IMember member)
{
if (member.DeclaringTypeDefinition == null)
return false;
switch (member.DeclaringTypeDefinition.Kind)
{
case TypeKind.Delegate:
case TypeKind.Enum:
return true;
default:
return false;
}
}
public void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes != null)
{
foreach (ILSpyTreeNode node in context.SelectedTreeNodes)
{
SearchMsdn(node);
}
}
}
public static void SearchMsdn(ILSpyTreeNode node)
{
var address = string.Empty;
if (node is NamespaceTreeNode namespaceNode)
{
address = string.Format(msdnAddress, namespaceNode.Name);
}
else if (node is IMemberTreeNode memberNode)
{
var member = memberNode.Member;
var memberName = member.ReflectionName.Replace('`', '-').Replace('+', '.');
if (memberName.EndsWith("..ctor", System.StringComparison.Ordinal))
memberName = memberName.Substring(0, memberName.Length - 5) + "-ctor";
address = string.Format(msdnAddress, memberName);
}
address = address.ToLower();
if (!string.IsNullOrEmpty(address))
GlobalUtils.OpenLink(address);
}
}
}

67
ILSpy/Commands/SelectPdbContextMenuEntry.cs

@ -1,67 +0,0 @@ @@ -1,67 +0,0 @@
// Copyright (c) 2018 Siegfried Pammer
//
// 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.Composition;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy
{
[ExportContextMenuEntry(Header = nameof(Resources.SelectPDB))]
[Shared]
class SelectPdbContextMenuEntry(AssemblyTreeModel assemblyTreeModel) : IContextMenuEntry
{
public async void Execute(TextViewContext context)
{
var assembly = (context.SelectedTreeNodes?.FirstOrDefault() as AssemblyTreeNode)?.LoadedAssembly;
if (assembly == null)
return;
OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = WholeProjectDecompiler.CleanUpFileName(assembly.ShortName, ".pdb");
dlg.Filter = Resources.PortablePDBPdbAllFiles;
dlg.InitialDirectory = Path.GetDirectoryName(assembly.FileName);
if (dlg.ShowDialog() != true)
return;
using (context.TreeView.LockUpdates())
{
await assembly.LoadDebugInfo(dlg.FileName);
}
var node = (AssemblyTreeNode)assemblyTreeModel.FindNodeByPath(new[] { assembly.FileName }, true);
node.UpdateToolTip();
assemblyTreeModel.SelectNode(node);
assemblyTreeModel.RefreshDecompiledView();
}
public bool IsEnabled(TextViewContext context) => true;
public bool IsVisible(TextViewContext context)
{
return context.SelectedTreeNodes?.Length == 1
&& context.SelectedTreeNodes?.FirstOrDefault() is AssemblyTreeNode asm
&& asm.LoadedAssembly.IsLoadedAsValidAssembly;
}
}
}

18
ILSpy/Commands/SetThemeCommand.cs

@ -1,18 +0,0 @@ @@ -1,18 +0,0 @@
using System.Composition;
namespace ICSharpCode.ILSpy.Commands
{
[Export]
[Shared]
public class SetThemeCommand(SettingsService settingsService) : SimpleCommand
{
public override void Execute(object parameter)
{
if (parameter is string theme)
{
settingsService.SessionSettings.Theme = theme;
}
}
}
}

77
ILSpy/Commands/ShowCFGContextMenuEntry.cs

@ -1,77 +0,0 @@ @@ -1,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Composition;
using System.Windows;
using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.ControlFlow;
namespace ICSharpCode.ILSpy.Commands
{
#if DEBUG
[ExportContextMenuEntry(Header = "DEBUG -- Show CFG")]
[Shared]
internal class ShowCFGContextMenuEntry : IContextMenuEntry
{
public void Execute(TextViewContext context)
{
try
{
var container = (BlockContainer)context.Reference.Reference;
var cfg = new ControlFlowGraph(container);
ExportGraph(cfg.Nodes).Show();
}
catch (Exception ex)
{
MessageBox.Show("Error generating CFG - requires GraphViz dot.exe in PATH" + Environment.NewLine + Environment.NewLine + ex.ToString());
}
}
public bool IsEnabled(TextViewContext context)
{
return context.Reference?.Reference is BlockContainer;
}
public bool IsVisible(TextViewContext context)
{
return context.Reference?.Reference is BlockContainer;
}
internal static GraphVizGraph ExportGraph(IReadOnlyList<ControlFlowNode> nodes, Func<ControlFlowNode, string> labelFunc = null)
{
if (labelFunc == null)
{
labelFunc = node => {
var block = node.UserData as Block;
return block != null ? block.Label : node.UserData?.ToString();
};
}
GraphVizGraph g = new GraphVizGraph();
GraphVizNode[] n = new GraphVizNode[nodes.Count];
for (int i = 0; i < n.Length; i++)
{
n[i] = new GraphVizNode(nodes[i].UserIndex);
n[i].shape = "box";
n[i].label = labelFunc(nodes[i]);
g.AddNode(n[i]);
}
foreach (var source in nodes)
{
foreach (var target in source.Successors)
{
g.AddEdge(new GraphVizEdge(source.UserIndex, target.UserIndex));
}
if (source.ImmediateDominator != null)
{
g.AddEdge(
new GraphVizEdge(source.ImmediateDominator.UserIndex, source.UserIndex) {
color = "green"
});
}
}
return g;
}
}
#endif
}

25
ILSpy/Commands/ShowPane.cs

@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy.Commands
{
class ToolPaneCommand(string contentId, DockWorkspace dockWorkspace) : SimpleCommand
{
public override void Execute(object parameter)
{
dockWorkspace.ShowToolPane(contentId);
}
}
class TabPageCommand(TabPageModel model, DockWorkspace dockWorkspace) : SimpleCommand
{
public override void Execute(object parameter)
{
// ensure the tab control is focused before setting the active tab page, else the tab will not be focused
dockWorkspace.ActiveTabPage?.Focus();
// reset first, else clicking on the already active tab will not focus the tab and the menu checkmark will not be updated
dockWorkspace.ActiveTabPage = null;
dockWorkspace.ActiveTabPage = model;
}
}
}

51
ILSpy/Commands/SimpleCommand.cs

@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
// Copyright (c) 2011 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.Collections;
using System.Windows.Data;
using System.Windows.Input;
namespace ICSharpCode.ILSpy
{
public abstract class SimpleCommand : ICommand
{
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public abstract void Execute(object parameter);
public virtual bool CanExecute(object parameter)
{
return true;
}
}
public interface IProvideParameterBinding
{
Binding ParameterBinding { get; }
}
public interface IProvideParameterList
{
IEnumerable ParameterList { get; }
object GetParameterText(object parameter);
}
}

52
ILSpy/Commands/SortAssemblyListCommand.cs

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Composition;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpyX.TreeView;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources.SortAssembly_listName), MenuIcon = "Images/Sort", MenuCategory = nameof(Resources.View))]
[ExportToolbarCommand(ToolTip = nameof(Resources.SortAssemblyListName), ToolbarIcon = "Images/Sort", ToolbarCategory = nameof(Resources.View))]
[Shared]
sealed class SortAssemblyListCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
{
public override void Execute(object parameter)
{
assemblyTreeModel.SortAssemblyList();
}
}
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._View), Header = nameof(Resources._CollapseTreeNodes), MenuIcon = "Images/CollapseAll", MenuCategory = nameof(Resources.View))]
[ExportToolbarCommand(ToolTip = nameof(Resources.CollapseTreeNodes), ToolbarIcon = "Images/CollapseAll", ToolbarCategory = nameof(Resources.View))]
[Shared]
sealed class CollapseAllCommand(AssemblyTreeModel assemblyTreeModel) : SimpleCommand
{
public override void Execute(object parameter)
{
assemblyTreeModel.CollapseAll();
}
}
}

375
ILSpy/ContextMenuEntry.cs

@ -1,375 +0,0 @@ @@ -1,375 +0,0 @@
// Copyright (c) 2011 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.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using ICSharpCode.AvalonEdit;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpyX.Search;
using ICSharpCode.ILSpy.Controls.TreeView;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Composition;
using TomsToolbox.Essentials;
using TomsToolbox.Wpf.Composition;
namespace ICSharpCode.ILSpy
{
public interface IContextMenuEntry
{
bool IsVisible(TextViewContext context);
bool IsEnabled(TextViewContext context);
void Execute(TextViewContext context);
}
public class TextViewContext
{
/// <summary>
/// Returns the selected nodes in the tree view.
/// Returns null, if context menu does not belong to a tree view.
/// </summary>
public SharpTreeNode[] SelectedTreeNodes { get; private set; }
/// <summary>
/// Returns the tree view the context menu is assigned to.
/// Returns null, if context menu is not assigned to a tree view.
/// </summary>
public SharpTreeView TreeView { get; private set; }
/// <summary>
/// Returns the text view the context menu is assigned to.
/// Returns null, if context menu is not assigned to a text view.
/// </summary>
public DecompilerTextView TextView { get; private set; }
/// <summary>
/// Returns the list box the context menu is assigned to.
/// Returns null, if context menu is not assigned to a list box.
/// </summary>
public ListBox ListBox { get; private set; }
/// <summary>
/// Returns the data grid the context menu is assigned to.
/// Returns null, if context menu is not assigned to a data grid.
/// </summary>
public DataGrid DataGrid { get; private set; }
/// <summary>
/// Returns the reference the mouse cursor is currently hovering above.
/// Returns null, if there was no reference found.
/// </summary>
public ReferenceSegment Reference { get; private set; }
/// <summary>
/// Returns the position in TextView the mouse cursor is currently hovering above.
/// Returns null, if TextView returns null;
/// </summary>
public TextViewPosition? Position { get; private set; }
/// <summary>
/// Returns the original source of the context menu event.
/// </summary>
public DependencyObject OriginalSource { get; private set; }
public static TextViewContext Create(ContextMenuEventArgs eventArgs, SharpTreeView treeView = null, DecompilerTextView textView = null, ListBox listBox = null, DataGrid dataGrid = null)
{
ReferenceSegment reference;
if (textView is not null)
{
reference = textView.GetReferenceSegmentAtMousePosition();
}
else
{
reference = (listBox?.SelectedItem ?? dataGrid?.SelectedItem) switch {
SearchResult searchResult => new() { Reference = searchResult.Reference },
TreeNodes.IMemberTreeNode treeNode => new() { Reference = treeNode.Member }, { } value => new() { Reference = value },
_ => null
};
}
var position = textView?.GetPositionFromMousePosition();
var selectedTreeNodes = treeView?.GetTopLevelSelection().ToArray();
return new() {
ListBox = listBox,
DataGrid = dataGrid,
TreeView = treeView,
TextView = textView,
SelectedTreeNodes = selectedTreeNodes,
Reference = reference,
Position = position,
OriginalSource = eventArgs.OriginalSource as DependencyObject
};
}
}
public interface IContextMenuEntryMetadata
{
string MenuID { get; }
string ParentMenuID { get; }
string Icon { get; }
string Header { get; }
string Category { get; }
string InputGestureText { get; }
double Order { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ExportContextMenuEntryAttribute : ExportAttribute, IContextMenuEntryMetadata
{
public ExportContextMenuEntryAttribute()
: base(typeof(IContextMenuEntry))
{
// entries default to end of menu unless given specific order position
Order = double.MaxValue;
}
/// <summary>
/// Gets/Sets the ID of this menu item. Menu entries are not required to have an ID,
/// however, setting it allows to declare nested menu structures.
/// Plugin authors are advised to use GUIDs as identifiers to prevent conflicts.
/// <para/>
/// NOTE: Defining cycles (for example by accidentally setting <see cref="MenuID"/> equal to <see cref="ParentMenuID"/>)
/// will lead to a stack-overflow and crash of ILSpy at startup.
/// </summary>
public string MenuID { get; set; }
/// <summary>
/// Gets/Sets the parent of this menu item. All menu items sharing the same parent will be displayed as sub-menu items.
/// If this property is set to <see langword="null"/>, the menu item is displayed in the top-level menu.
/// <para/>
/// NOTE: Defining cycles (for example by accidentally setting <see cref="MenuID"/> equal to <see cref="ParentMenuID"/>)
/// will lead to a stack-overflow and crash of ILSpy at startup.
/// </summary>
public string ParentMenuID { get; set; }
public string Icon { get; set; }
public string Header { get; set; }
public string Category { get; set; }
public string InputGestureText { get; set; }
public double Order { get; set; }
}
internal class ContextMenuProvider
{
private static readonly WeakEventSource<EventArgs> ContextMenuClosedEventSource = new();
public static event EventHandler<EventArgs> ContextMenuClosed {
add => ContextMenuClosedEventSource.Subscribe(value);
remove => ContextMenuClosedEventSource.Unsubscribe(value);
}
/// <summary>
/// Enables extensible context menu support for the specified tree view.
/// </summary>
public static void Add(SharpTreeView treeView)
{
var provider = new ContextMenuProvider(treeView);
treeView.ContextMenuOpening += provider.treeView_ContextMenuOpening;
// Context menu is shown only when the ContextMenu property is not null before the
// ContextMenuOpening event handler is called.
treeView.ContextMenu = new ContextMenu();
}
public static void Add(DecompilerTextView textView)
{
var provider = new ContextMenuProvider(textView);
textView.ContextMenuOpening += provider.textView_ContextMenuOpening;
// Context menu is shown only when the ContextMenu property is not null before the
// ContextMenuOpening event handler is called.
textView.ContextMenu = new ContextMenu();
}
public static void Add(ListBox listBox)
{
var provider = new ContextMenuProvider(listBox);
listBox.ContextMenuOpening += provider.listBox_ContextMenuOpening;
listBox.ContextMenu = new ContextMenu();
}
public static void Add(DataGrid dataGrid)
{
var provider = new ContextMenuProvider(dataGrid);
dataGrid.ContextMenuOpening += provider.dataGrid_ContextMenuOpening;
dataGrid.ContextMenu = new ContextMenu();
}
readonly Control control;
readonly SharpTreeView treeView;
readonly DecompilerTextView textView;
readonly ListBox listBox;
readonly DataGrid dataGrid;
readonly IExport<IContextMenuEntry, IContextMenuEntryMetadata>[] entries;
private ContextMenuProvider(Control control)
{
entries = control.GetExportProvider().GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().ToArray();
this.control = control;
}
ContextMenuProvider(DecompilerTextView textView)
: this((Control)textView)
{
this.textView = textView ?? throw new ArgumentNullException(nameof(textView));
}
ContextMenuProvider(SharpTreeView treeView)
: this((Control)treeView)
{
this.treeView = treeView ?? throw new ArgumentNullException(nameof(treeView));
}
ContextMenuProvider(ListBox listBox)
: this((Control)listBox)
{
this.listBox = listBox ?? throw new ArgumentNullException(nameof(listBox));
}
ContextMenuProvider(DataGrid dataGrid)
: this((Control)dataGrid)
{
this.dataGrid = dataGrid ?? throw new ArgumentNullException(nameof(dataGrid));
}
void treeView_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var context = TextViewContext.Create(e, treeView: treeView);
if (context.SelectedTreeNodes.Length == 0)
{
e.Handled = true; // don't show the menu
return;
}
if (ShowContextMenu(context, out var menu))
treeView.ContextMenu = menu;
else
// hide the context menu.
e.Handled = true;
}
void textView_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var context = TextViewContext.Create(e, textView: textView);
if (ShowContextMenu(context, out var menu))
textView.ContextMenu = menu;
else
// hide the context menu.
e.Handled = true;
}
void listBox_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var context = TextViewContext.Create(e, listBox: listBox);
if (ShowContextMenu(context, out var menu))
listBox.ContextMenu = menu;
else
// hide the context menu.
e.Handled = true;
}
void dataGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
var context = TextViewContext.Create(e, dataGrid: dataGrid);
if (ShowContextMenu(context, out var menu))
dataGrid.ContextMenu = menu;
else
// hide the context menu.
e.Handled = true;
}
bool ShowContextMenu(TextViewContext context, out ContextMenu menu)
{
// Closing event is raised on the control where mouse is clicked, not on the control that opened the menu, so we hook on the global window event.
var window = Window.GetWindow(control)!;
window.ContextMenuClosing += ContextMenu_Closing;
void ContextMenu_Closing(object sender, EventArgs e)
{
window.ContextMenuClosing -= ContextMenu_Closing;
ContextMenuClosedEventSource.Raise(this, EventArgs.Empty);
}
menu = new ContextMenu();
var menuGroups = new Dictionary<string, IExport<IContextMenuEntry, IContextMenuEntryMetadata>[]>();
IExport<IContextMenuEntry, IContextMenuEntryMetadata>[] topLevelGroup = null;
foreach (var group in entries.OrderBy(c => c.Metadata.Order).GroupBy(c => c.Metadata.ParentMenuID))
{
if (group.Key == null)
{
topLevelGroup = group.ToArray();
}
else
{
menuGroups.Add(group.Key, group.ToArray());
}
}
BuildMenu(topLevelGroup ?? Array.Empty<IExport<IContextMenuEntry, IContextMenuEntryMetadata>>(), menu.Items);
return menu.Items.Count > 0;
void BuildMenu(IExport<IContextMenuEntry, IContextMenuEntryMetadata>[] menuGroup, ItemCollection parent)
{
foreach (var category in menuGroup.GroupBy(c => c.Metadata.Category))
{
var needSeparatorForCategory = parent.Count > 0;
foreach (var entryPair in category)
{
var entry = entryPair.Value;
if (entry.IsVisible(context))
{
if (needSeparatorForCategory)
{
parent.Add(new Separator());
needSeparatorForCategory = false;
}
var menuItem = new MenuItem();
menuItem.Header = ResourceHelper.GetString(entryPair.Metadata.Header);
menuItem.InputGestureText = entryPair.Metadata.InputGestureText;
if (!string.IsNullOrEmpty(entryPair.Metadata.Icon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(entryPair.Value, entryPair.Metadata.Icon)
};
}
if (entryPair.Value.IsEnabled(context))
{
menuItem.Click += delegate { entry.Execute(context); };
}
else
menuItem.IsEnabled = false;
parent.Add(menuItem);
if (entryPair.Metadata.MenuID != null && menuGroups.TryGetValue(entryPair.Metadata.MenuID, out var group))
{
BuildMenu(group, menuItem.Items);
}
}
}
}
}
}
}
}

209
ILSpy/Controls/CollapsiblePanel.cs

@ -1,209 +0,0 @@ @@ -1,209 +0,0 @@
// Copyright (c) 2014 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.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Threading;
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// Allows animated collapsing of the content of this panel.
/// </summary>
public class CollapsiblePanel : ContentControl
{
static CollapsiblePanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CollapsiblePanel),
new FrameworkPropertyMetadata(typeof(CollapsiblePanel)));
FocusableProperty.OverrideMetadata(typeof(CollapsiblePanel),
new FrameworkPropertyMetadata(false));
}
public static readonly DependencyProperty IsCollapsedProperty = DependencyProperty.Register(
"IsCollapsed", typeof(bool), typeof(CollapsiblePanel),
new UIPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged)));
public bool IsCollapsed {
get { return (bool)GetValue(IsCollapsedProperty); }
set { SetValue(IsCollapsedProperty, value); }
}
public static readonly DependencyProperty CollapseOrientationProperty =
DependencyProperty.Register("CollapseOrientation", typeof(Orientation), typeof(CollapsiblePanel),
new FrameworkPropertyMetadata(Orientation.Vertical));
public Orientation CollapseOrientation {
get { return (Orientation)GetValue(CollapseOrientationProperty); }
set { SetValue(CollapseOrientationProperty, value); }
}
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
"Duration", typeof(TimeSpan), typeof(CollapsiblePanel),
new UIPropertyMetadata(TimeSpan.FromMilliseconds(250)));
/// <summary>
/// The duration in milliseconds of the animation.
/// </summary>
public TimeSpan Duration {
get { return (TimeSpan)GetValue(DurationProperty); }
set { SetValue(DurationProperty, value); }
}
protected internal static readonly DependencyProperty AnimationProgressProperty = DependencyProperty.Register(
"AnimationProgress", typeof(double), typeof(CollapsiblePanel),
new FrameworkPropertyMetadata(1.0));
/// <summary>
/// Value between 0 and 1 specifying how far the animation currently is.
/// </summary>
protected internal double AnimationProgress {
get { return (double)GetValue(AnimationProgressProperty); }
set { SetValue(AnimationProgressProperty, value); }
}
protected internal static readonly DependencyProperty AnimationProgressXProperty = DependencyProperty.Register(
"AnimationProgressX", typeof(double), typeof(CollapsiblePanel),
new FrameworkPropertyMetadata(1.0));
/// <summary>
/// Value between 0 and 1 specifying how far the animation currently is.
/// </summary>
protected internal double AnimationProgressX {
get { return (double)GetValue(AnimationProgressXProperty); }
set { SetValue(AnimationProgressXProperty, value); }
}
protected internal static readonly DependencyProperty AnimationProgressYProperty = DependencyProperty.Register(
"AnimationProgressY", typeof(double), typeof(CollapsiblePanel),
new FrameworkPropertyMetadata(1.0));
/// <summary>
/// Value between 0 and 1 specifying how far the animation currently is.
/// </summary>
protected internal double AnimationProgressY {
get { return (double)GetValue(AnimationProgressYProperty); }
set { SetValue(AnimationProgressYProperty, value); }
}
static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((CollapsiblePanel)d).SetupAnimation((bool)e.NewValue);
}
void SetupAnimation(bool isCollapsed)
{
if (this.IsLoaded)
{
// If the animation is already running, calculate remaining portion of the time
double currentProgress = AnimationProgress;
if (!isCollapsed)
{
currentProgress = 1.0 - currentProgress;
}
DoubleAnimation animation = new DoubleAnimation();
animation.To = isCollapsed ? 0.0 : 1.0;
animation.Duration = TimeSpan.FromSeconds(Duration.TotalSeconds * currentProgress);
animation.FillBehavior = FillBehavior.HoldEnd;
this.BeginAnimation(AnimationProgressProperty, animation);
if (CollapseOrientation == Orientation.Horizontal)
{
this.BeginAnimation(AnimationProgressXProperty, animation);
this.AnimationProgressY = 1.0;
}
else
{
this.AnimationProgressX = 1.0;
this.BeginAnimation(AnimationProgressYProperty, animation);
}
}
else
{
this.AnimationProgress = isCollapsed ? 0.0 : 1.0;
this.AnimationProgressX = (CollapseOrientation == Orientation.Horizontal) ? this.AnimationProgress : 1.0;
this.AnimationProgressY = (CollapseOrientation == Orientation.Vertical) ? this.AnimationProgress : 1.0;
}
}
}
sealed class CollapsiblePanelProgressToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is double)
return (double)value > 0 ? Visibility.Visible : Visibility.Collapsed;
else
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class SelfCollapsingPanel : CollapsiblePanel
{
public static readonly DependencyProperty CanCollapseProperty =
DependencyProperty.Register("CanCollapse", typeof(bool), typeof(SelfCollapsingPanel),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnCanCollapseChanged)));
public bool CanCollapse {
get { return (bool)GetValue(CanCollapseProperty); }
set { SetValue(CanCollapseProperty, value); }
}
static void OnCanCollapseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SelfCollapsingPanel panel = (SelfCollapsingPanel)d;
if ((bool)e.NewValue)
{
if (!panel.HeldOpenByMouse)
panel.IsCollapsed = true;
}
else
{
panel.IsCollapsed = false;
}
}
bool HeldOpenByMouse {
get { return IsMouseOver || IsMouseCaptureWithin; }
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
if (CanCollapse && !HeldOpenByMouse)
IsCollapsed = true;
}
protected override void OnLostMouseCapture(MouseEventArgs e)
{
base.OnLostMouseCapture(e);
if (CanCollapse && !HeldOpenByMouse)
IsCollapsed = true;
}
}
}

47
ILSpy/Controls/CultureSelectionConverter.cs

@ -1,47 +0,0 @@ @@ -1,47 +0,0 @@
// Copyright (c) 2021 Siegfried Pammer
//
// 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.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace ICSharpCode.ILSpy.Controls
{
public class CultureSelectionConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string s)
return s.Equals(parameter);
return value == parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return parameter;
return Binding.DoNothing;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}

169
ILSpy/Controls/CustomDialog.cs

@ -1,169 +0,0 @@ @@ -1,169 +0,0 @@
// Copyright (c) 2014 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.Drawing;
using System.Windows.Forms;
namespace ICSharpCode.ILSpy.Controls
{
public sealed class CustomDialog : System.Windows.Forms.Form
{
System.Windows.Forms.Label label;
System.Windows.Forms.Panel panel;
int acceptButton;
int cancelButton;
int result = -1;
/// <summary>
/// Gets the index of the button pressed.
/// </summary>
public int Result {
get {
return result;
}
}
public CustomDialog(string caption, string message, int acceptButton, int cancelButton, string[] buttonLabels)
{
this.SuspendLayout();
MyInitializeComponent();
this.Icon = null;
this.acceptButton = acceptButton;
this.cancelButton = cancelButton;
this.Text = caption;
using (Graphics g = this.CreateGraphics())
{
Size size = TextRenderer.MeasureText(message, label.Font, new((int)(Screen.FromControl(this).WorkingArea.Width * 0.9), int.MaxValue), TextFormatFlags.NoPrefix | TextFormatFlags.WordBreak);
Size clientSize = new Size((int)Math.Ceiling(size.Width * 96 / g.DpiX) + DockPadding.Left + DockPadding.Right, (int)Math.Ceiling(size.Height * 96 / g.DpiY) + DockPadding.Top + DockPadding.Bottom);
Button[] buttons = new Button[buttonLabels.Length];
int[] positions = new int[buttonLabels.Length];
int pos = 0;
for (int i = 0; i < buttons.Length; i++)
{
Button newButton = new Button();
newButton.FlatStyle = FlatStyle.System;
newButton.Tag = i;
string buttonLabel = buttonLabels[i];
newButton.Text = buttonLabel;
newButton.Click += new EventHandler(ButtonClick);
Size buttonSize = TextRenderer.MeasureText(buttonLabel, newButton.Font);
newButton.Width = Math.Max(newButton.Width, ((int)Math.Ceiling(buttonSize.Width * 96 / g.DpiX / 8.0) + 1) * 8);
positions[i] = pos;
buttons[i] = newButton;
pos += newButton.Width + 4;
}
if (acceptButton >= 0)
{
AcceptButton = buttons[acceptButton];
}
if (cancelButton >= 0)
{
CancelButton = buttons[cancelButton];
}
pos += 4; // add space before first button
// (we don't start with pos=4 because this space doesn't belong to the button panel)
if (pos > clientSize.Width)
{
clientSize.Width = pos;
}
clientSize.Height += panel.Height;
this.ClientSize = clientSize;
int start = (clientSize.Width - pos) / 2;
for (int i = 0; i < buttons.Length; i++)
{
buttons[i].Location = new Point(start + positions[i], 4);
}
panel.Controls.AddRange(buttons);
}
label.Text = message;
this.ResumeLayout(false);
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (cancelButton == -1 && e.KeyCode == Keys.Escape)
{
this.Close();
}
else if (e.KeyCode == Keys.C && e.Control)
{
Clipboard.SetText(this.Text + Environment.NewLine + label.Text);
}
}
void ButtonClick(object sender, EventArgs e)
{
result = (int)((Control)sender).Tag;
this.Close();
}
/// <summary>
/// This method is required for Windows Forms designer support.
/// Do not change the method contents inside the source code editor. The Forms designer might
/// not be able to load this method if it was changed manually.
/// </summary>
void MyInitializeComponent()
{
this.panel = new System.Windows.Forms.Panel();
this.label = new System.Windows.Forms.Label();
//
// panel
//
this.panel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel.Location = new System.Drawing.Point(4, 80);
this.panel.Name = "panel";
this.panel.Size = new System.Drawing.Size(266, 32);
this.panel.TabIndex = 0;
//
// label
//
this.label.Dock = System.Windows.Forms.DockStyle.Fill;
this.label.FlatStyle = System.Windows.Forms.FlatStyle.System;
this.label.Location = new System.Drawing.Point(4, 4);
this.label.Name = "label";
this.label.Size = new System.Drawing.Size(266, 76);
this.label.TabIndex = 1;
this.label.UseMnemonic = false;
//
// CustomDialog
//
this.ClientSize = new System.Drawing.Size(274, 112);
this.Controls.Add(this.label);
this.Controls.Add(this.panel);
this.DockPadding.Left = 4;
this.DockPadding.Right = 4;
this.DockPadding.Top = 4;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.ShowInTaskbar = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "CustomDialog";
this.KeyPreview = true;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "CustomDialog";
this.AutoScaleMode = AutoScaleMode.Dpi;
this.AutoScaleDimensions = new SizeF(96, 96);
}
}
}

80
ILSpy/Controls/ExtensionMethods.cs

@ -1,80 +0,0 @@ @@ -1,80 +0,0 @@
// Copyright (c) 2011 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.Windows;
using System.Windows.Markup;
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// ExtensionMethods used in ILSpy.
/// </summary>
public static class ExtensionMethods
{
/// <summary>
/// Sets the value of a dependency property on <paramref name="targetObject"/> using a markup extension.
/// </summary>
/// <remarks>This method does not support markup extensions like x:Static that depend on
/// having a XAML file as context.</remarks>
public static void SetValueToExtension(this DependencyObject targetObject, DependencyProperty property, MarkupExtension markupExtension)
{
// This method was copied from ICSharpCode.Core.Presentation (with permission to switch license to X11)
if (targetObject == null)
throw new ArgumentNullException(nameof(targetObject));
if (property == null)
throw new ArgumentNullException(nameof(property));
if (markupExtension == null)
throw new ArgumentNullException(nameof(markupExtension));
var serviceProvider = new SetValueToExtensionServiceProvider(targetObject, property);
targetObject.SetValue(property, markupExtension.ProvideValue(serviceProvider));
}
sealed class SetValueToExtensionServiceProvider : IServiceProvider, IProvideValueTarget
{
// This class was copied from ICSharpCode.Core.Presentation (with permission to switch license to X11)
readonly DependencyObject targetObject;
readonly DependencyProperty targetProperty;
public SetValueToExtensionServiceProvider(DependencyObject targetObject, DependencyProperty property)
{
this.targetObject = targetObject;
this.targetProperty = property;
}
public object GetService(Type serviceType)
{
if (serviceType == typeof(IProvideValueTarget))
return this;
else
return null;
}
public object TargetObject {
get { return targetObject; }
}
public object TargetProperty {
get { return targetProperty; }
}
}
}
}

108
ILSpy/Controls/GridViewColumnAutoSize.cs

@ -1,108 +0,0 @@ @@ -1,108 +0,0 @@
// Copyright (c) 2014 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.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// This class adds the AutoWidth property to the WPF ListView.
/// It supports a semi-colon-separated list of values, for each defined cell.
/// Each value can either be a fixed size double, or a percentage.
/// The sizes of columns with a percentage will be calculated from the
/// remaining width (after assigning the fixed sizes).
/// Examples: 50%;25%;25% or 30;100%;50
/// </summary>
public class GridViewColumnAutoSize
{
// This class was copied from ICSharpCode.Core.Presentation.
public static readonly DependencyProperty AutoWidthProperty =
DependencyProperty.RegisterAttached("AutoWidth", typeof(string), typeof(GridViewColumnAutoSize),
new FrameworkPropertyMetadata(null, AutoWidthPropertyChanged));
public static string GetAutoWidth(DependencyObject obj)
{
return (string)obj.GetValue(AutoWidthProperty);
}
public static void SetAutoWidth(DependencyObject obj, string value)
{
obj.SetValue(AutoWidthProperty, value);
}
static void AutoWidthPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
ListView grid = sender as ListView;
if (grid == null)
return;
grid.SizeChanged += delegate (object listView, SizeChangedEventArgs e) {
ListView lv = listView as ListView;
if (lv == null)
return;
GridView v = lv.View as GridView;
if (v == null)
return;
CalculateSizes(v, GetAutoWidth(lv), e.NewSize.Width);
};
GridView view = grid.View as GridView;
if (view == null)
return;
CalculateSizes(view, args.NewValue as string, grid.ActualWidth);
}
static void CalculateSizes(GridView view, string sizeValue, double fullWidth)
{
string[] sizes = (sizeValue ?? "").Split(';');
if (sizes.Length != view.Columns.Count)
return;
Dictionary<int, Func<double, double>> percentages = new Dictionary<int, Func<double, double>>();
double remainingWidth = fullWidth - 30; // 30 is a good offset for the scrollbar
for (int i = 0; i < view.Columns.Count; i++)
{
var column = view.Columns[i];
double size;
bool isPercentage = !double.TryParse(sizes[i], out size);
if (isPercentage)
{
size = double.Parse(sizes[i].TrimEnd('%', ' '));
percentages.Add(i, w => w * size / 100.0);
}
else
{
column.Width = size;
remainingWidth -= size;
}
}
if (remainingWidth < 0)
return;
foreach (var p in percentages)
{
var column = view.Columns[p.Key];
column.Width = p.Value(remainingWidth);
}
}
}
}

53
ILSpy/Controls/MainMenu.xaml

@ -1,53 +0,0 @@ @@ -1,53 +0,0 @@
<UserControl x:Class="ICSharpCode.ILSpy.Controls.MainMenu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
xmlns:composition="urn:TomsToolbox.Composition"
xmlns:toms="urn:TomsToolbox"
xmlns:commands="clr-namespace:ICSharpCode.ILSpy.Commands"
mc:Ignorable="d" d:DesignWidth="800" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Menu DockPanel.Dock="Top" Name="Menu" Height="23" KeyboardNavigation.TabNavigation="None">
<MenuItem Header="{x:Static properties:Resources._File}" Tag="_File">
<!-- content of file menu is added using MEF -->
</MenuItem>
<MenuItem Header="{x:Static properties:Resources._View}" Tag="_View">
<MenuItem Header="{x:Static properties:Resources.Show_publiconlyTypesMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicOnly}" />
<MenuItem Header="{x:Static properties:Resources.Show_internalTypesMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicAndInternal}" />
<MenuItem Header="{x:Static properties:Resources.Show_allTypesAndMembers}" IsCheckable="True"
IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisAll}" />
<Separator />
<MenuItem Header="{x:Static properties:Resources.Theme}" ItemsSource="{x:Static themes:ThemeManager.AllThemes}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Command" Value="{composition:Import commands:SetThemeCommand}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="IsCheckable" Value="True" />
<!-- Required by AvalonDock's MenuItem style to show the checkmark -->
<Setter Property="IsChecked">
<Setter.Value>
<MultiBinding Converter="{x:Static toms:BinaryOperationConverter.Equality}" Mode="OneWay">
<Binding />
<Binding Path="SessionSettings.Theme"
RelativeSource="{RelativeSource FindAncestor, AncestorType=UserControl}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="{x:Static properties:Resources.UILanguage}">
<MenuItem Header="{x:Static properties:Resources.UILanguage_System}" IsCheckable="True"
IsChecked="{Binding SessionSettings.CurrentCulture, Converter={local:CultureSelectionConverter}, ConverterParameter={x:Null}}" />
</MenuItem>
</MenuItem>
<MenuItem x:Name="WindowMenuItem" Header="{x:Static properties:Resources._Window}" Tag="_Window">
<!-- content of window menu is added using MEF -->
</MenuItem>
</Menu>
</UserControl>

212
ILSpy/Controls/MainMenu.xaml.cs

@ -1,212 +0,0 @@ @@ -1,212 +0,0 @@
// Copyright (c) 2024 Tom Englert 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.Collections.Generic;
using System.Composition;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using ICSharpCode.ILSpy.Commands;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.ViewModels;
using TomsToolbox.Composition;
using TomsToolbox.ObservableCollections;
using TomsToolbox.Wpf;
using TomsToolbox.Wpf.Converters;
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// Interaction logic for MainMenu.xaml
/// </summary>
[Export]
[NonShared]
public partial class MainMenu
{
public MainMenu(SettingsService settingsService, IExportProvider exportProvider, DockWorkspace dockWorkspace)
{
SessionSettings = settingsService.SessionSettings;
InitializeComponent();
this.BeginInvoke(() => {
InitMainMenu(Menu, exportProvider);
InitWindowMenu(WindowMenuItem, Window.GetWindow(this)!.InputBindings, dockWorkspace);
});
}
public SessionSettings SessionSettings { get; }
static void InitMainMenu(Menu mainMenu, IExportProvider exportProvider)
{
var mainMenuCommands = exportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
// Start by constructing the individual flat menus
var parentMenuItems = new Dictionary<string, MenuItem>();
var menuGroups = mainMenuCommands.OrderBy(c => c.Metadata?.MenuOrder).GroupBy(c => c.Metadata?.ParentMenuID).ToArray();
foreach (var menu in menuGroups)
{
// Get or add the target menu item and add all items grouped by menu category
var parentMenuItem = GetOrAddParentMenuItem(menu.Key, menu.Key);
foreach (var category in menu.GroupBy(c => c.Metadata?.MenuCategory))
{
if (parentMenuItem.Items.Count > 0)
{
parentMenuItem.Items.Add(new Separator { Tag = category.Key });
}
foreach (var entry in category)
{
if (menuGroups.Any(g => g.Key == entry.Metadata?.MenuID))
{
var menuItem = GetOrAddParentMenuItem(entry.Metadata?.MenuID, entry.Metadata?.Header);
// replace potential dummy text with real name
menuItem.Header = ResourceHelper.GetString(entry.Metadata?.Header);
parentMenuItem.Items.Add(menuItem);
}
else
{
var command = entry.Value;
var menuItem = new MenuItem {
Command = CommandWrapper.Unwrap(command),
Tag = entry.Metadata?.MenuID,
Header = ResourceHelper.GetString(entry.Metadata?.Header)
};
if (!string.IsNullOrEmpty(entry.Metadata?.MenuIcon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(command, entry.Metadata.MenuIcon)
};
}
menuItem.IsEnabled = entry.Metadata?.IsEnabled ?? false;
menuItem.InputGestureText = entry.Metadata?.InputGestureText;
if (command is IProvideParameterBinding parameterBinding)
{
BindingOperations.SetBinding(menuItem, MenuItem.CommandParameterProperty, parameterBinding.ParameterBinding);
}
parentMenuItem.Items.Add(menuItem);
}
}
}
}
foreach (var item in parentMenuItems.Values.Where(item => item.Parent == null))
{
mainMenu.Items.Add(item);
}
MenuItem GetOrAddParentMenuItem(string menuId, string resourceKey)
{
if (!parentMenuItems.TryGetValue(menuId, out var parentMenuItem))
{
var topLevelMenuItem = mainMenu.Items.OfType<MenuItem>().FirstOrDefault(m => (string)m.Tag == menuId);
if (topLevelMenuItem == null)
{
parentMenuItem = new() {
Header = ResourceHelper.GetString(resourceKey),
Tag = menuId
};
parentMenuItems.Add(menuId, parentMenuItem);
}
else
{
parentMenuItems.Add(menuId, topLevelMenuItem);
parentMenuItem = topLevelMenuItem;
}
}
return parentMenuItem;
}
}
static void InitWindowMenu(MenuItem windowMenuItem, InputBindingCollection inputBindings, DockWorkspace dockWorkspace)
{
var defaultItems = windowMenuItem.Items.Cast<Control>().ToArray();
windowMenuItem.Items.Clear();
var toolItems = dockWorkspace.ToolPanes.Select(toolPane => CreateMenuItem(toolPane, inputBindings, dockWorkspace)).ToArray();
var tabItems = dockWorkspace.TabPages.ObservableSelect(tabPage => CreateMenuItem(tabPage, dockWorkspace));
var allItems = new ObservableCompositeCollection<Control>(defaultItems, [new Separator()], toolItems, [new Separator()], tabItems);
windowMenuItem.ItemsSource = allItems;
}
static Control CreateMenuItem(TabPageModel pane, DockWorkspace dockWorkspace)
{
var header = new TextBlock {
MaxWidth = 200,
TextTrimming = TextTrimming.CharacterEllipsis
};
header.SetBinding(TextBlock.TextProperty, new Binding(nameof(pane.Title)) {
Source = pane
});
var menuItem = new MenuItem {
Command = new TabPageCommand(pane, dockWorkspace),
Header = header,
IsCheckable = true
};
menuItem.SetBinding(MenuItem.IsCheckedProperty, new Binding(nameof(dockWorkspace.ActiveTabPage)) {
Source = dockWorkspace,
ConverterParameter = pane,
Converter = BinaryOperationConverter.Equality,
Mode = BindingMode.OneWay
});
return menuItem;
}
static Control CreateMenuItem(ToolPaneModel pane, InputBindingCollection inputBindings, DockWorkspace dockWorkspace)
{
var menuItem = new MenuItem {
Command = pane.AssociatedCommand ?? new ToolPaneCommand(pane.ContentId, dockWorkspace),
Header = pane.Title
};
var shortcutKey = pane.ShortcutKey;
if (shortcutKey != null)
{
inputBindings.Add(new InputBinding(menuItem.Command, shortcutKey));
menuItem.InputGestureText = shortcutKey.GetDisplayStringForCulture(CultureInfo.CurrentUICulture);
}
if (!string.IsNullOrEmpty(pane.Icon))
{
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(pane, pane.Icon)
};
}
return menuItem;
}
}
}

88
ILSpy/Controls/MainToolBar.xaml

@ -1,88 +0,0 @@ @@ -1,88 +0,0 @@
<UserControl x:Class="ICSharpCode.ILSpy.Controls.MainToolBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:toms="urn:TomsToolbox"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:themes="clr-namespace:ICSharpCode.ILSpy.Themes"
xmlns:composition="urn:TomsToolbox.Composition"
xmlns:ilSpy="clr-namespace:ICSharpCode.ILSpy"
mc:Ignorable="d" d:DesignWidth="800">
<ToolBar Name="ToolBar"
DockPanel.Dock="Top" ToolBarTray.IsLocked="True" KeyboardNavigation.TabNavigation="None">
<ToolBar.Resources>
<!-- Make images transparent if menu command is disabled -->
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
<Setter Property="Opacity" Value="0.30" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Image}" x:Key="DarkModeAwareImageStyle">
<Setter Property="Effect" Value="{DynamicResource {x:Static themes:ResourceKeys.ThemeAwareButtonEffect}}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ButtonBase}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
<Setter Property="Opacity" Value="0.30" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToolBar.Resources>
<!-- 'Navigation' toolbar category is inserted here -->
<Separator />
<!-- 'Open' toolbar category is inserted here -->
<Separator />
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding AssemblyListManager.AssemblyLists}" Height="0" Margin="15,0" />
<ComboBox Name="assemblyListComboBox" MaxDropDownHeight="Auto"
ItemsSource="{Binding AssemblyListManager.AssemblyLists}"
ToolTip="{x:Static properties:Resources.SelectAssemblyListDropdownTooltip}"
SelectedItem="{Binding SessionSettings.ActiveAssemblyList}" />
</Grid>
<Button Command="{composition:Import ilSpy:ManageAssemblyListsCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
ToolTip="{x:Static properties:Resources.ManageAssemblyLists}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/AssemblyList}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</Button>
<Separator />
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicOnly}"
ToolTip="{x:Static properties:Resources.ShowPublicOnlyTypesMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowPublicOnly}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisPublicAndInternal}"
ToolTip="{x:Static properties:Resources.ShowInternalTypesMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowPrivateInternal}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<CheckBox IsChecked="{Binding SessionSettings.LanguageSettings.ApiVisAll}"
ToolTip="{x:Static properties:Resources.ShowAllTypesAndMembers}">
<Image Width="16" Height="16" Source="{controls:XamlResource Images/ShowAll}"
Style="{StaticResource DarkModeAwareImageStyle}" />
</CheckBox>
<Separator />
<Grid Margin="2,0">
<ItemsControl ItemsSource="{Binding LanguageService.AllLanguages}" DisplayMemberPath="Name" Height="0" Margin="15,0" />
<ComboBox Name="languageComboBox" DisplayMemberPath="Name" MaxDropDownHeight="Auto"
IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}"
ItemsSource="{Binding LanguageService.AllLanguages}"
ToolTip="{x:Static properties:Resources.SelectLanguageDropdownTooltip}"
SelectedItem="{Binding LanguageService.Language}" />
</Grid>
<Grid Margin="2,0">
<ItemsControl
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding SelectedItem.HasLanguageVersions, ElementName=languageComboBox, Converter={toms:BooleanToVisibilityConverter}}"
DisplayMemberPath="DisplayName" Height="0" Margin="15,0" />
<ComboBox Name="languageVersionComboBox" DisplayMemberPath="DisplayName" MaxDropDownHeight="Auto"
ToolTip="{x:Static properties:Resources.SelectVersionDropdownTooltip}"
Visibility="{Binding SelectedItem.HasLanguageVersions, ElementName=languageComboBox, Converter={toms:BooleanToVisibilityConverter}}"
IsEnabled="{Binding Workspace.ActiveTabPage.SupportsLanguageSwitching}"
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding LanguageService.LanguageVersion, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</ToolBar>
</UserControl>

237
ILSpy/Controls/MainToolBar.xaml.cs

@ -1,237 +0,0 @@ @@ -1,237 +0,0 @@
// Copyright (c) 2024 Tom Englert 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.ComponentModel;
using System.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.ILSpy.Themes;
using ICSharpCode.ILSpyX.TreeView;
using TomsToolbox.Composition;
namespace ICSharpCode.ILSpy.Controls
{
/// <summary>
/// Interaction logic for MainToolBar.xaml
/// </summary>
[Export]
[NonShared]
public partial class MainToolBar
{
public MainToolBar(IExportProvider exportProvider)
{
InitializeComponent();
this.Dispatcher.BeginInvoke(DispatcherPriority.Background, () => {
InitToolbar(ToolBar, exportProvider);
Window.GetWindow(this)!.KeyDown += MainWindow_KeyDown;
});
}
static void InitToolbar(ToolBar toolBar, IExportProvider exportProvider)
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommandsByCategory = exportProvider
.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand")
.OrderBy(c => c.Metadata?.ToolbarOrder)
.GroupBy(c => c.Metadata?.ToolbarCategory);
foreach (var commandCategory in toolbarCommandsByCategory)
{
if (commandCategory.Key == nameof(Properties.Resources.Navigation))
{
foreach (var command in commandCategory)
{
toolBar.Items.Insert(navigationPos++, CreateToolbarItem(command));
openPos++;
}
}
else if (commandCategory.Key == nameof(Properties.Resources.Open))
{
foreach (var command in commandCategory)
{
toolBar.Items.Insert(openPos++, CreateToolbarItem(command));
}
}
else
{
toolBar.Items.Add(new Separator());
foreach (var command in commandCategory)
{
toolBar.Items.Add(CreateToolbarItem(command));
}
}
}
}
static UIElement CreateToolbarItem(IExport<ICommand, IToolbarCommandMetadata> commandExport)
{
var command = commandExport.Value;
Button toolbarItem = new() {
Style = ThemeManager.Current.CreateToolBarButtonStyle(),
Command = CommandWrapper.Unwrap(command),
ToolTip = Properties.Resources.ResourceManager.GetString(
commandExport.Metadata?.ToolTip ?? string.Empty),
Tag = commandExport.Metadata?.Tag,
Content = new Image {
Width = 16,
Height = 16,
Source = Images.Load(command, commandExport.Metadata?.ToolbarIcon)
}
};
if (command is IProvideParameterBinding parameterBinding)
{
BindingOperations.SetBinding(toolbarItem, ButtonBase.CommandParameterProperty,
parameterBinding.ParameterBinding);
}
if (command is IProvideParameterList parameterList)
{
toolbarItem.Margin = new Thickness(2, 0, 0, 0);
var dropDownPanel = new StackPanel { Orientation = Orientation.Horizontal };
var dropDownToggle = new ToggleButton {
Style = ThemeManager.Current.CreateToolBarToggleButtonStyle(),
Content = "▾",
Padding = new Thickness(0),
MinWidth = 0,
Margin = new Thickness(0, 0, 2, 0)
};
var contextMenu = new ContextMenu {
PlacementTarget = dropDownPanel,
Tag = command
};
ContextMenuService.SetPlacement(toolbarItem, PlacementMode.Bottom);
toolbarItem.ContextMenu = contextMenu;
toolbarItem.ContextMenuOpening += (_, _) =>
PrepareParameterList(contextMenu);
dropDownToggle.Checked += (_, _) => {
PrepareParameterList(contextMenu);
contextMenu.Placement = PlacementMode.Bottom;
contextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true);
};
BindingOperations.SetBinding(dropDownToggle, ToggleButton.IsCheckedProperty,
new Binding(nameof(contextMenu.IsOpen)) { Source = contextMenu });
BindingOperations.SetBinding(dropDownToggle, IsEnabledProperty,
new Binding(nameof(IsEnabled)) { Source = toolbarItem });
// When the toggle button is checked, clicking it to uncheck will dismiss the menu first
// which unchecks the toggle button via binding above and the click is used to open it again.
// This is a workaround to ignore the click to uncheck the already unchecked toggle button.
// We have to ensure the dismissing click is on the toggle button, otherwise the flag
// will not get cleared and menu will not open next time.
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(contextMenu, (_, e) => {
var point = e.GetPosition(dropDownToggle);
dropDownToggle.Tag = dropDownToggle.InputHitTest(point);
});
dropDownToggle.PreviewMouseLeftButtonDown += (_, e) => {
e.Handled = dropDownToggle.Tag != null;
dropDownToggle.Tag = null;
};
dropDownPanel.Children.Add(toolbarItem);
dropDownPanel.Children.Add(dropDownToggle);
return dropDownPanel;
}
return toolbarItem;
}
static void PrepareParameterList(ContextMenu menu)
{
const int maximumParameterListCount = 20;
var command = (ICommand)menu.Tag;
var parameterList = (IProvideParameterList)command;
menu.Items.Clear();
foreach (var parameter in parameterList.ParameterList)
{
MenuItem parameterItem = new MenuItem();
parameterItem.Command = CommandWrapper.Unwrap(command);
parameterItem.CommandParameter = parameter;
parameterItem.CommandTarget = menu.PlacementTarget;
parameterItem.InputGestureText = " ";
var headerPresenter = new ContentPresenter { RecognizesAccessKey = false };
parameterItem.Header = headerPresenter;
var header = parameterList.GetParameterText(parameter);
switch (header)
{
case SharpTreeNode node:
headerPresenter.Content = node.NavigationText;
if (node.Icon is ImageSource icon)
parameterItem.Icon = new Image {
Width = 16,
Height = 16,
Source = icon
};
break;
default:
headerPresenter.Content = header;
break;
}
menu.Items.Add(parameterItem);
if (menu.Items.Count >= maximumParameterListCount)
break;
}
}
void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Handled || e.KeyboardDevice.Modifiers != ModifierKeys.Alt || e.Key != Key.System)
return;
switch (e.SystemKey)
{
case Key.A:
assemblyListComboBox.Focus();
e.Handled = true;
break;
case Key.L:
languageComboBox.Focus();
e.Handled = true;
break;
case Key.E: // Alt+V was already taken by _View menu
languageVersionComboBox.Focus();
e.Handled = true;
break;
}
}
}
}

69
ILSpy/Controls/MarkupExtensions.cs

@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
// Copyright (c) 2011 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.Windows;
using System.Windows.Markup;
using System.Windows.Media;
namespace ICSharpCode.ILSpy.Controls
{
[MarkupExtensionReturnType(typeof(Color))]
class ControlColor : MarkupExtension
{
readonly float val;
/// <summary>
/// Amount of highlight (0..1)
/// </summary>
public float Highlight { get; set; }
/// <summary>
/// val: Color value in the range 105..255.
/// </summary>
public ControlColor(float val)
{
if (!(val >= 105 && val <= 255))
throw new ArgumentOutOfRangeException(nameof(val));
this.val = val;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (val > 227)
{
return Interpolate(227, SystemColors.ControlLightColor, 255, SystemColors.ControlLightLightColor);
}
else if (val > 160)
{
return Interpolate(160, SystemColors.ControlDarkColor, 227, SystemColors.ControlLightColor);
}
else
{
return Interpolate(105, SystemColors.ControlDarkDarkColor, 160, SystemColors.ControlDarkColor);
}
}
Color Interpolate(float v1, Color c1, float v2, Color c2)
{
float v = (val - v1) / (v2 - v1);
Color c = c1 * (1 - v) + c2 * v;
return c * (1 - Highlight) + SystemColors.HighlightColor * Highlight;
}
}
}

82
ILSpy/Controls/ResourceObjectTable.xaml

@ -1,82 +0,0 @@ @@ -1,82 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<UserControl x:Class="ICSharpCode.ILSpy.Controls.ResourceObjectTable"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:local="clr-namespace:ICSharpCode.ILSpy.Controls"
Cursor="Arrow">
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Copy"
Executed="ExecuteCopy"
CanExecute="CanExecuteCopy" />
</UserControl.CommandBindings>
<Grid Margin="5,0,0,0">
<Grid.Resources>
<AlternationConverter x:Key="BackgroundConverter">
<SolidColorBrush Color="Transparent" />
<SolidColorBrush Color="#CCCC33" Opacity="0.15" />
</AlternationConverter>
<Style x:Key="alternatingWithBinding"
TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(ItemsControl.AlternationIndex),
Converter={StaticResource BackgroundConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="_Copy" Command="ApplicationCommands.Copy" />
<MenuItem Header="Copy _name" Command="ApplicationCommands.Copy" CommandParameter="Key" InputGestureText=" " />
<MenuItem Header="Copy _value" Command="ApplicationCommands.Copy" CommandParameter="Value" InputGestureText=" " />
<MenuItem Header="Copy _type" Command="ApplicationCommands.Copy" CommandParameter="Type" InputGestureText=" " />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="{x:Static properties:Resources.OtherResources}"
FontFamily="Segoe UI"
FontWeight="Bold"
FontSize="12pt" />
<local:SearchBox x:Name="resourceFilterBox"
FontFamily="Segoe UI"
FontSize="9pt"
Grid.Row="1"
TextChanged="OnFilterTextChanged" />
<ListView Name="resourceListView"
FontFamily="Segoe UI"
FontSize="9pt"
Foreground="Black"
Grid.Row="2"
AlternationCount="2"
ItemContainerStyle="{StaticResource alternatingWithBinding}"
local:SortableGridViewColumn.SortMode="Automatic">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridView.Columns>
<local:SortableGridViewColumn DisplayMemberBinding="{Binding Key}" SortBy="Key">
<GridViewColumnHeader Content="{x:Static properties:Resources.Name}"
HorizontalContentAlignment="Left"
FontWeight="Bold" />
</local:SortableGridViewColumn>
<local:SortableGridViewColumn DisplayMemberBinding="{Binding Value}" SortBy="Value">
<GridViewColumnHeader Content="{x:Static properties:Resources.ValueString}"
HorizontalContentAlignment="Left"
FontWeight="Bold" />
</local:SortableGridViewColumn>
<local:SortableGridViewColumn DisplayMemberBinding="{Binding Type}" SortBy="Type">
<GridViewColumnHeader Content="{x:Static properties:Resources.Type}"
HorizontalContentAlignment="Left"
FontWeight="Bold" />
</local:SortableGridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Grid>
</UserControl>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save