Browse Source

Add NuGet packages (and other files) to TreeView

pull/2191/head
Daniel Grunwald 5 years ago
parent
commit
f74d3c40a9
  1. 32
      ICSharpCode.Decompiler/Metadata/Dom.cs
  2. 2
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  3. 27
      ILSpy/AssemblyList.cs
  4. 10
      ILSpy/ILSpy.csproj
  5. 4
      ILSpy/Images/Images.cs
  6. BIN
      ILSpy/Images/NuGet.png
  7. 4
      ILSpy/Languages/CSharpLanguage.cs
  8. 129
      ILSpy/LoadedAssembly.cs
  9. 121
      ILSpy/LoadedNugetPackage.cs
  10. 99
      ILSpy/LoadedPackage.cs
  11. 65
      ILSpy/MainWindow.xaml.cs
  12. 29
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  13. 106
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  14. 1
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  15. 2
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  16. 40
      ILSpy/Views/NugetPackageBrowserDialog.xaml
  17. 86
      ILSpy/Views/NugetPackageBrowserDialog.xaml.cs

32
ICSharpCode.Decompiler/Metadata/Dom.cs

@ -1,16 +1,11 @@ @@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography;
using System.Text;
using ICSharpCode.Decompiler.Documentation;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -32,13 +27,21 @@ namespace ICSharpCode.Decompiler.Metadata @@ -32,13 +27,21 @@ namespace ICSharpCode.Decompiler.Metadata
AssemblyLinked,
}
public struct Resource : IEquatable<Resource>
public abstract class Resource
{
public virtual ResourceType ResourceType => ResourceType.Embedded;
public virtual ManifestResourceAttributes Attributes => ManifestResourceAttributes.Public;
public abstract string Name { get; }
public abstract Stream TryOpenStream();
}
sealed class MetadataResource : Resource
{
public PEFile Module { get; }
public ManifestResourceHandle Handle { get; }
public bool IsNil => Handle.IsNil;
public Resource(PEFile module, ManifestResourceHandle handle) : this()
public MetadataResource(PEFile module, ManifestResourceHandle handle)
{
this.Module = module ?? throw new ArgumentNullException(nameof(module));
this.Handle = handle;
@ -46,14 +49,14 @@ namespace ICSharpCode.Decompiler.Metadata @@ -46,14 +49,14 @@ namespace ICSharpCode.Decompiler.Metadata
ManifestResource This() => Module.Metadata.GetManifestResource(Handle);
public bool Equals(Resource other)
public bool Equals(MetadataResource other)
{
return Module == other.Module && Handle == other.Handle;
}
public override bool Equals(object obj)
{
if (obj is Resource res)
if (obj is MetadataResource res)
return Equals(res);
return false;
}
@ -63,14 +66,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -63,14 +66,11 @@ namespace ICSharpCode.Decompiler.Metadata
return unchecked(982451629 * Module.GetHashCode() + 982451653 * MetadataTokens.GetToken(Handle));
}
public static bool operator ==(Resource lhs, Resource rhs) => lhs.Equals(rhs);
public static bool operator !=(Resource lhs, Resource rhs) => !lhs.Equals(rhs);
public string Name => Module.Metadata.GetString(This().Name);
public override string Name => Module.Metadata.GetString(This().Name);
public ManifestResourceAttributes Attributes => This().Attributes;
public override ManifestResourceAttributes Attributes => This().Attributes;
public bool HasFlag(ManifestResourceAttributes flag) => (Attributes & flag) == flag;
public ResourceType ResourceType => GetResourceType();
public override ResourceType ResourceType => GetResourceType();
ResourceType GetResourceType()
{
@ -81,7 +81,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -81,7 +81,7 @@ namespace ICSharpCode.Decompiler.Metadata
return ResourceType.Linked;
}
public unsafe Stream TryOpenStream()
public override unsafe Stream TryOpenStream()
{
if (ResourceType != ResourceType.Embedded)
return null;

2
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -112,7 +112,7 @@ namespace ICSharpCode.Decompiler.Metadata
var metadata = Metadata;
foreach (var h in metadata.ManifestResources)
{
yield return new Resource(this, h);
yield return new MetadataResource(this, h);
}
}

27
ILSpy/AssemblyList.cs

@ -163,28 +163,7 @@ namespace ICSharpCode.ILSpy @@ -163,28 +163,7 @@ namespace ICSharpCode.ILSpy
public LoadedAssembly Open(string assemblyUri, bool isAutoLoaded = false)
{
if (assemblyUri.StartsWith("nupkg://", StringComparison.OrdinalIgnoreCase))
{
string fileName = assemblyUri.Substring("nupkg://".Length);
int separator = fileName.LastIndexOf(';');
string componentName = null;
if (separator > -1)
{
componentName = fileName.Substring(separator + 1);
fileName = fileName.Substring(0, separator);
LoadedNugetPackage package = new LoadedNugetPackage(fileName);
var entry = package.Entries.FirstOrDefault(e => e.Name == componentName);
if (entry != null)
{
return OpenAssembly(assemblyUri, entry.Stream, true);
}
}
return null;
}
else
{
return OpenAssembly(assemblyUri, isAutoLoaded);
}
return OpenAssembly(assemblyUri, isAutoLoaded);
}
/// <summary>
@ -225,7 +204,7 @@ namespace ICSharpCode.ILSpy @@ -225,7 +204,7 @@ namespace ICSharpCode.ILSpy
return asm;
}
var newAsm = new LoadedAssembly(this, file, stream);
var newAsm = new LoadedAssembly(this, file, Task.FromResult(stream));
newAsm.IsAutoLoaded = isAutoLoaded;
lock (assemblies)
{
@ -248,7 +227,7 @@ namespace ICSharpCode.ILSpy @@ -248,7 +227,7 @@ namespace ICSharpCode.ILSpy
return null;
var index = this.assemblies.IndexOf(target);
var newAsm = new LoadedAssembly(this, file, stream);
var newAsm = new LoadedAssembly(this, file, Task.FromResult(stream));
newAsm.IsAutoLoaded = target.IsAutoLoaded;
lock (assemblies)
{

10
ILSpy/ILSpy.csproj

@ -382,7 +382,7 @@ @@ -382,7 +382,7 @@
<Compile Include="Search\LiteralSearchStrategy.cs" />
<Compile Include="LoadedAssembly.cs" />
<Compile Include="LoadedAssemblyExtensions.cs" />
<Compile Include="LoadedNugetPackage.cs" />
<Compile Include="LoadedPackage.cs" />
<Compile Include="Search\MemberSearchStrategy.cs" />
<Compile Include="Search\MetadataTokenSearchStrategy.cs" />
<Compile Include="NativeMethods.cs" />
@ -390,9 +390,6 @@ @@ -390,9 +390,6 @@
<Compile Include="NavigationState.cs" />
<Compile Include="Commands\OpenCommand.cs" />
<Compile Include="Commands\OpenFromGacCommand.cs" />
<Compile Include="Views\NugetPackageBrowserDialog.xaml.cs">
<DependentUpon>NugetPackageBrowserDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Views\OpenFromGacDialog.xaml.cs">
<DependentUpon>OpenFromGacDialog.xaml</DependentUpon>
</Compile>
@ -581,7 +578,6 @@ @@ -581,7 +578,6 @@
<Page Include="Metadata\MetadataTableViews.xaml" />
<Page Include="Views\OpenFromGacDialog.xaml" />
<Page Include="Views\ManageAssemblyListsDialog.xaml" />
<Page Include="Views\NugetPackageBrowserDialog.xaml" />
<Page Include="Options\DecompilerSettingsPanel.xaml" />
<Page Include="Options\DisplaySettingsPanel.xaml" />
<Page Include="Options\MiscSettingsPanel.xaml" />
@ -853,6 +849,10 @@ @@ -853,6 +849,10 @@
<SortResXStamp Include="obj\sort-resx.stamp" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\NuGet.png" />
</ItemGroup>
<Target Name="SortResX" BeforeTargets="BeforeBuild" Inputs="@(SortResXInput)" Outputs="@(SortResXStamp)">
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
<SortResX>powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/sort-resx.ps1</SortResX>

4
ILSpy/Images/Images.cs

@ -20,11 +20,8 @@ using System; @@ -20,11 +20,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace ICSharpCode.ILSpy
{
@ -50,6 +47,7 @@ namespace ICSharpCode.ILSpy @@ -50,6 +47,7 @@ namespace ICSharpCode.ILSpy
public static readonly ImageSource Namespace = Load("Namespace");
public static readonly ImageSource ReferenceFolder = Load("ReferenceFolder");
public static readonly ImageSource NuGet = Load(null, "Images/NuGet.png");
public static readonly ImageSource SubTypes = Load("SubTypes");
public static readonly ImageSource SuperTypes = Load("SuperTypes");

BIN
ILSpy/Images/NuGet.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

4
ILSpy/Languages/CSharpLanguage.cs

@ -386,6 +386,10 @@ namespace ICSharpCode.ILSpy @@ -386,6 +386,10 @@ namespace ICSharpCode.ILSpy
public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
var module = assembly.GetPEFileOrNull();
if (module == null)
{
return null;
}
if (options.FullDecompilation && options.SaveAsProjectDirectory != null)
{
var decompiler = new ILSpyWholeProjectDecompiler(assembly, options);

129
ILSpy/LoadedAssembly.cs

@ -24,7 +24,6 @@ using System.Linq; @@ -24,7 +24,6 @@ using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
@ -40,24 +39,53 @@ using ICSharpCode.ILSpy.Options; @@ -40,24 +39,53 @@ using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Represents an assembly loaded into ILSpy.
/// Represents a file loaded into ILSpy.
///
/// Note: the file is not necessarily an assembly.
/// A LoadedAssembly can refer to:
/// * a .NET module (single-file) loaded into ILSpy
/// * a non-existant file
/// * a file of unknown format that could not be loaded
/// * a .nupkg file or .NET core bundle
/// * a file that is still being loaded in the background
/// </summary>
[DebuggerDisplay("[LoadedAssembly {shortName}]")]
public sealed class LoadedAssembly
{
/// <summary>
/// Maps from PEFile (successfully loaded .NET module) back to the LoadedAssembly instance
/// that was used to load the module.
/// </summary>
internal static readonly ConditionalWeakTable<PEFile, LoadedAssembly> loadedAssemblies = new ConditionalWeakTable<PEFile, LoadedAssembly>();
readonly Task<PEFile> assemblyTask;
public sealed class LoadResult
{
public PEFile PEFile { get; }
public PEFileNotSupportedException PEFileLoadException { get; }
public LoadedPackage Package { get; }
public LoadResult(PEFile peFile)
{
this.PEFile = peFile ?? throw new ArgumentNullException(nameof(peFile));
}
public LoadResult(PEFileNotSupportedException peFileLoadException, LoadedPackage package)
{
this.PEFileLoadException = peFileLoadException ?? throw new ArgumentNullException(nameof(peFileLoadException));
this.Package = package ?? throw new ArgumentNullException(nameof(package));
}
}
readonly Task<LoadResult> loadingTask;
readonly AssemblyList assemblyList;
readonly string fileName;
readonly string shortName;
public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null)
public LoadedAssembly(AssemblyList assemblyList, string fileName, Task<Stream> stream = null)
{
this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList));
this.fileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
this.assemblyTask = Task.Factory.StartNew(LoadAssembly, stream); // requires that this.fileName is set
this.loadingTask = Task.Run(() => LoadAsync(stream)); // requires that this.fileName is set
this.shortName = Path.GetFileNameWithoutExtension(fileName);
this.resolver = new MyAssemblyResolver(this);
}
@ -65,6 +93,8 @@ namespace ICSharpCode.ILSpy @@ -65,6 +93,8 @@ namespace ICSharpCode.ILSpy
/// <summary>
/// Returns a target framework identifier in the form '&lt;framework&gt;Version=v&lt;version&gt;'.
/// Returns an empty string if no TargetFrameworkAttribute was found or the file doesn't contain an assembly header, i.e., is only a module.
///
/// Throws an exception if the file does not contain any .NET metadata (e.g. file of unknown format).
/// </summary>
public async Task<string> GetTargetFrameworkIdAsync()
{
@ -76,12 +106,24 @@ namespace ICSharpCode.ILSpy @@ -76,12 +106,24 @@ namespace ICSharpCode.ILSpy
IDebugInfoProvider debugInfoProvider;
/// <summary>
/// Gets the <see cref="LoadResult"/>.
/// </summary>
public Task<LoadResult> GetLoadResultAsync()
{
return loadingTask;
}
/// <summary>
/// Gets the <see cref="PEFile"/>.
/// </summary>
public Task<PEFile> GetPEFileAsync()
public async Task<PEFile> GetPEFileAsync()
{
return assemblyTask;
var loadResult = await loadingTask.ConfigureAwait(false);
if (loadResult.PEFile != null)
return loadResult.PEFile;
else
throw loadResult.PEFileLoadException;
}
/// <summary>
@ -176,42 +218,71 @@ namespace ICSharpCode.ILSpy @@ -176,42 +218,71 @@ namespace ICSharpCode.ILSpy
}
}
public bool IsLoaded => assemblyTask.IsCompleted;
public bool IsLoaded => loadingTask.IsCompleted;
public bool HasLoadError => assemblyTask.IsFaulted;
public bool HasLoadError => loadingTask.IsFaulted;
public bool IsAutoLoaded { get; set; }
public string PdbFileOverride { get; set; }
PEFile LoadAssembly(object state)
async Task<LoadResult> LoadAsync(Task<Stream> streamTask)
{
MetadataReaderOptions options;
if (DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections)
// runs on background thread
if (streamTask != null)
{
options = MetadataReaderOptions.ApplyWindowsRuntimeProjections;
var stream = await streamTask;
// Read the module from a precrafted stream
if (!stream.CanSeek)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
stream.Close();
memoryStream.Position = 0;
stream = memoryStream;
}
var streamOptions = stream is MemoryStream ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default;
return LoadAssembly(stream, streamOptions);
}
else
// Read the module from disk
PEFileNotSupportedException loadAssemblyException;
try
{
options = MetadataReaderOptions.None;
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
return LoadAssembly(fileStream, PEStreamOptions.PrefetchEntireImage);
}
}
catch (PEFileNotSupportedException ex)
{
loadAssemblyException = ex;
}
// If it's not a .NET module, maybe it's a zip archive (e.g. .nupkg)
try
{
var zip = LoadedPackage.FromZipFile(fileName);
return new LoadResult(loadAssemblyException, zip);
}
catch (InvalidDataException)
{
throw loadAssemblyException;
}
}
PEFile module;
// runs on background thread
if (state is Stream stream)
LoadResult LoadAssembly(Stream stream, PEStreamOptions streamOptions)
{
MetadataReaderOptions options;
if (DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections)
{
// Read the module from a precrafted stream
var streamOptions = stream is MemoryStream ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default;
module = new PEFile(fileName, stream, streamOptions, metadataOptions: options);
options = MetadataReaderOptions.ApplyWindowsRuntimeProjections;
}
else
{
// Read the module from disk (by default)
stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage,
metadataOptions: options);
options = MetadataReaderOptions.None;
}
PEFile module = new PEFile(fileName, stream, streamOptions, metadataOptions: options);
if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols)
{
try
@ -234,7 +305,7 @@ namespace ICSharpCode.ILSpy @@ -234,7 +305,7 @@ namespace ICSharpCode.ILSpy
{
loadedAssemblies.Add(module, this);
}
return module;
return new LoadResult(module);
}
[ThreadStatic]
@ -524,9 +595,10 @@ namespace ICSharpCode.ILSpy @@ -524,9 +595,10 @@ namespace ICSharpCode.ILSpy
return asm;
}
[Obsolete("Use GetPEFileAsync() or GetLoadResultAsync() instead")]
public Task ContinueWhenLoaded(Action<Task<PEFile>> onAssemblyLoaded, TaskScheduler taskScheduler)
{
return this.assemblyTask.ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler);
return this.GetPEFileAsync().ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler);
}
/// <summary>
@ -535,8 +607,7 @@ namespace ICSharpCode.ILSpy @@ -535,8 +607,7 @@ namespace ICSharpCode.ILSpy
/// </summary>
public void WaitUntilLoaded()
{
assemblyTask.Wait();
loadingTask.Wait();
}
}
}

121
ILSpy/LoadedNugetPackage.cs

@ -1,121 +0,0 @@ @@ -1,121 +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.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.ILSpy
{
public class LoadedNugetPackage : INotifyPropertyChanged
{
public List<Entry> Entries { get; } = new List<Entry>();
public List<Entry> SelectedEntries { get; } = new List<Entry>();
public LoadedNugetPackage(string file)
{
using (var archive = ZipFile.OpenRead(file))
{
foreach (var entry in archive.Entries)
{
switch (Path.GetExtension(entry.FullName))
{
case ".dll":
case ".exe":
var memory = new MemoryStream();
entry.Open().CopyTo(memory);
memory.Position = 0;
var e = new Entry(Uri.UnescapeDataString(entry.FullName), memory);
e.PropertyChanged += EntryPropertyChanged;
Entries.Add(e);
break;
}
}
}
}
void EntryPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Entry.IsSelected))
{
var entry = (Entry)sender;
if (entry.IsSelected)
SelectedEntries.Add(entry);
else
SelectedEntries.Remove(entry);
OnPropertyChanged(nameof(SelectedEntries));
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Entry : INotifyPropertyChanged
{
public string Name { get; }
public bool IsSelected {
get { return isSelected; }
set {
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged();
}
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public Stream Stream { get; }
bool isSelected;
public event PropertyChangedEventHandler PropertyChanged;
public Entry(string name, Stream stream)
{
this.Name = name;
this.Stream = stream;
}
}
}

99
ILSpy/LoadedPackage.cs

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
// 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.IO;
using System.IO.Compression;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// NuGet package or .NET bundle:
/// </summary>
public class LoadedPackage
{
public enum PackageKind
{
Zip,
}
public PackageKind Kind { get; }
public List<PackageEntry> Entries { get; } = new List<PackageEntry>();
public static LoadedPackage FromZipFile(string file)
{
using (var archive = ZipFile.OpenRead(file))
{
LoadedPackage result = new LoadedPackage();
foreach (var entry in archive.Entries)
{
result.Entries.Add(new ZipFileEntry(file, entry));
}
return result;
}
}
class ZipFileEntry : PackageEntry
{
readonly string zipFile;
public override string Name { get; }
public override string FullName => $"zip://{zipFile};{Name}";
public ZipFileEntry(string zipFile, ZipArchiveEntry entry)
{
this.zipFile = zipFile;
this.Name = entry.FullName;
}
public override Stream TryOpenStream()
{
Debug.WriteLine("Decompress " + Name);
using (var archive = ZipFile.OpenRead(zipFile))
{
var entry = archive.GetEntry(Name);
if (entry == null)
return null;
var memoryStream = new MemoryStream();
using (var s = entry.Open())
{
s.CopyTo(memoryStream);
}
memoryStream.Position = 0;
return memoryStream;
}
}
}
}
public abstract class PackageEntry : Resource
{
/// <summary>
/// Gets the file name of the entry (may include path components, relative to the package root).
/// </summary>
public abstract override string Name { get; }
/// <summary>
/// Gets the full file name for the entry.
/// </summary>
public abstract string FullName { get; }
}
}

65
ILSpy/MainWindow.xaml.cs

@ -69,7 +69,6 @@ namespace ICSharpCode.ILSpy @@ -69,7 +69,6 @@ namespace ICSharpCode.ILSpy
partial class MainWindow : Window
{
bool refreshInProgress;
bool handlingNugetPackageSelection;
readonly NavigationHistory<NavigationState> history = new NavigationHistory<NavigationState>();
ILSpySettings spySettingsForMainWindow_Loaded;
internal SessionSettings sessionSettings;
@ -335,8 +334,6 @@ namespace ICSharpCode.ILSpy @@ -335,8 +334,6 @@ namespace ICSharpCode.ILSpy
string data = new string((char*)copyData->Buffer, 0, copyData->Size / sizeof(char));
if (data.StartsWith("ILSpy:\r\n", StringComparison.Ordinal))
{
if (handlingNugetPackageSelection)
return (IntPtr)1;
data = data.Substring(8);
List<string> lines = new List<string>();
using (StringReader r = new StringReader(data))
@ -1110,58 +1107,22 @@ namespace ICSharpCode.ILSpy @@ -1110,58 +1107,22 @@ namespace ICSharpCode.ILSpy
SharpTreeNode lastNode = null;
foreach (string file in fileNames)
{
switch (Path.GetExtension(file))
var asm = assemblyList.OpenAssembly(file);
if (asm != null)
{
case ".nupkg":
this.handlingNugetPackageSelection = true;
try
{
LoadedNugetPackage package = new LoadedNugetPackage(file);
var selectionDialog = new NugetPackageBrowserDialog(package);
selectionDialog.Owner = this;
if (selectionDialog.ShowDialog() != true)
break;
foreach (var entry in selectionDialog.SelectedItems)
{
var nugetAsm = assemblyList.OpenAssembly("nupkg://" + file + ";" + entry.Name, entry.Stream, true);
if (nugetAsm != null)
{
if (loadedAssemblies != null)
loadedAssemblies.Add(nugetAsm);
else
{
var node = assemblyListTreeNode.FindAssemblyNode(nugetAsm);
if (node != null && focusNode)
{
AssemblyTreeView.SelectedItems.Add(node);
lastNode = node;
}
}
}
}
}
finally
{
this.handlingNugetPackageSelection = false;
}
break;
default:
var asm = assemblyList.OpenAssembly(file);
if (asm != null)
if (loadedAssemblies != null)
{
loadedAssemblies.Add(asm);
}
else
{
var node = assemblyListTreeNode.FindAssemblyNode(asm);
if (node != null && focusNode)
{
if (loadedAssemblies != null)
loadedAssemblies.Add(asm);
else
{
var node = assemblyListTreeNode.FindAssemblyNode(asm);
if (node != null && focusNode)
{
AssemblyTreeView.SelectedItems.Add(node);
lastNode = node;
}
}
AssemblyTreeView.SelectedItems.Add(node);
lastNode = node;
}
break;
}
}
if (lastNode != null && focusNode)

29
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -17,12 +17,9 @@ @@ -17,12 +17,9 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Windows;
using ICSharpCode.Decompiler;
@ -105,7 +102,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -105,7 +102,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
var assemblies = files
.Where(file => file != null)
.SelectMany(file => OpenAssembly(assemblyList, file))
.Select(file => assemblyList.OpenAssembly(file))
.Where(asm => asm != null)
.Distinct()
.ToArray();
@ -127,28 +124,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -127,28 +124,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
private IEnumerable<LoadedAssembly> OpenAssembly(AssemblyList assemblyList, string file)
{
if (file.EndsWith(".nupkg"))
{
LoadedNugetPackage package = new LoadedNugetPackage(file);
var selectionDialog = new NugetPackageBrowserDialog(package);
selectionDialog.Owner = Application.Current.MainWindow;
if (selectionDialog.ShowDialog() != true)
yield break;
foreach (var entry in selectionDialog.SelectedItems)
{
var nugetAsm = assemblyList.OpenAssembly("nupkg://" + file + ";" + entry.Name, entry.Stream, true);
if (nugetAsm != null)
{
yield return nugetAsm;
}
}
yield break;
}
yield return assemblyList.OpenAssembly(file);
}
public Action<SharpTreeNode> Select = delegate { };
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
@ -166,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -166,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
#region Find*Node
public ILSpyTreeNode FindResourceNode(Resource resource)
{
if (resource == null || resource.IsNil)
if (resource == null)
return null;
foreach (AssemblyTreeNode node in this.Children)
{

106
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -48,13 +48,18 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -48,13 +48,18 @@ namespace ICSharpCode.ILSpy.TreeNodes
readonly Dictionary<string, NamespaceTreeNode> namespaces = new Dictionary<string, NamespaceTreeNode>();
readonly Dictionary<TypeDefinitionHandle, TypeTreeNode> typeDict = new Dictionary<TypeDefinitionHandle, TypeTreeNode>();
ICompilation typeSystem;
string textOverride;
public AssemblyTreeNode(LoadedAssembly assembly)
public AssemblyTreeNode(LoadedAssembly assembly) : this(assembly, null)
{
this.LoadedAssembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
assembly.ContinueWhenLoaded(OnAssemblyLoaded, TaskScheduler.FromCurrentSynchronizationContext());
}
private AssemblyTreeNode(LoadedAssembly assembly, string textOverride)
{
this.LoadedAssembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
this.LazyLoading = true;
this.textOverride = textOverride;
Init();
}
public AssemblyList AssemblyList {
@ -69,13 +74,18 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -69,13 +74,18 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override object Text => LoadedAssembly.Text;
public override object Text => textOverride ?? LoadedAssembly.Text;
public override object Icon {
get {
if (LoadedAssembly.IsLoaded)
{
return LoadedAssembly.HasLoadError ? Images.AssemblyWarning : Images.Assembly;
if (LoadedAssembly.HasLoadError)
return Images.AssemblyWarning;
var loadResult = LoadedAssembly.GetLoadResultAsync().Result;
if (loadResult.Package != null)
return Images.NuGet;
return Images.Assembly;
}
else
{
@ -105,19 +115,22 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -105,19 +115,22 @@ namespace ICSharpCode.ILSpy.TreeNodes
tooltip.Inlines.Add(new Bold(new Run("Location: ")));
tooltip.Inlines.Add(new Run(LoadedAssembly.FileName));
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Architecture: ")));
tooltip.Inlines.Add(new Run(Language.GetPlatformDisplayName(module)));
string runtimeName = Language.GetRuntimeDisplayName(module);
if (runtimeName != null)
if (module != null)
{
tooltip.Inlines.Add(new Bold(new Run("Architecture: ")));
tooltip.Inlines.Add(new Run(Language.GetPlatformDisplayName(module)));
string runtimeName = Language.GetRuntimeDisplayName(module);
if (runtimeName != null)
{
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Runtime: ")));
tooltip.Inlines.Add(new Run(runtimeName));
}
var debugInfo = LoadedAssembly.GetDebugInfoOrNull();
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Runtime: ")));
tooltip.Inlines.Add(new Run(runtimeName));
tooltip.Inlines.Add(new Bold(new Run("Debug info: ")));
tooltip.Inlines.Add(new Run(debugInfo?.Description ?? "none"));
}
var debugInfo = LoadedAssembly.GetDebugInfoOrNull();
tooltip.Inlines.Add(new LineBreak());
tooltip.Inlines.Add(new Bold(new Run("Debug info: ")));
tooltip.Inlines.Add(new Run(debugInfo?.Description ?? "none"));
}
return tooltip;
@ -128,30 +141,47 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -128,30 +141,47 @@ namespace ICSharpCode.ILSpy.TreeNodes
get { return !LoadedAssembly.HasLoadError; }
}
void OnAssemblyLoaded(Task<PEFile> moduleTask)
async void Init()
{
// change from "Loading" icon to final icon
RaisePropertyChanged(nameof(Icon));
RaisePropertyChanged(nameof(ExpandedIcon));
RaisePropertyChanged(nameof(ToolTip));
if (moduleTask.IsFaulted)
try
{
RaisePropertyChanged(nameof(ShowExpander)); // cannot expand assemblies with load error
await this.LoadedAssembly.GetLoadResultAsync();
RaisePropertyChanged(nameof(Text)); // shortname might have changed
}
else
catch
{
RaisePropertyChanged(nameof(Text)); // shortname might have changed
RaisePropertyChanged(nameof(ShowExpander)); // cannot expand assemblies with load error
}
// change from "Loading" icon to final icon
RaisePropertyChanged(nameof(Icon));
RaisePropertyChanged(nameof(ExpandedIcon));
RaisePropertyChanged(nameof(ToolTip));
}
protected override void LoadChildren()
{
var module = LoadedAssembly.GetPEFileOrNull();
if (module == null)
LoadedAssembly.LoadResult loadResult;
try
{
loadResult = LoadedAssembly.GetLoadResultAsync().Result;
}
catch
{
// if we crashed on loading, then we don't have any children
return;
}
if (loadResult.PEFile != null)
{
LoadChildrenForPEFile(loadResult.PEFile);
}
else if (loadResult.Package != null)
{
LoadChildrenForPackage(loadResult.Package);
}
}
void LoadChildrenForPEFile(PEFile module)
{
typeSystem = LoadedAssembly.GetTypeSystemOrNull(DecompilerTypeSystem.GetOptions(new DecompilationOptions().DecompilerSettings));
var assembly = (MetadataModule)typeSystem.MainModule;
this.Children.Add(new Metadata.MetadataTreeNode(module, this));
@ -186,6 +216,20 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -186,6 +216,20 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
private void LoadChildrenForPackage(LoadedPackage package)
{
foreach (var entry in package.Entries.OrderBy(e => e.Name))
{
if (entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || entry.Name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
{
var stream = Task.Run(entry.TryOpenStream);
this.Children.Add(new AssemblyTreeNode(new LoadedAssembly(AssemblyList, entry.FullName, stream), entry.Name));
continue;
}
this.Children.Add(ResourceTreeNode.Create(entry));
}
}
public override bool CanExpandRecursively => true;
/// <summary>
@ -220,7 +264,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -220,7 +264,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool CanDrag(SharpTreeNode[] nodes)
{
return nodes.All(n => n is AssemblyTreeNode);
// prohibit dragging assemblies nested in nuget packages
return nodes.All(n => n is AssemblyTreeNode { textOverride: null });
}
public override void StartDrag(DependencyObject dragSource, SharpTreeNode[] nodes)
@ -230,7 +275,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -230,7 +275,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool CanDelete()
{
return true;
// prohibit deleting assemblies nested in nuget packages
return textOverride == null;
}
public override void Delete()
@ -452,7 +498,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -452,7 +498,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
if (context.SelectedTreeNodes == null)
return false;
return context.SelectedTreeNodes.Where(n => n is AssemblyTreeNode).Any(n => !((AssemblyTreeNode)n).LoadedAssembly.FileName.StartsWith("nupkg://"));
return context.SelectedTreeNodes.Any(n => n is AssemblyTreeNode);
}
public void Execute(TextViewContext context)
@ -462,7 +508,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -462,7 +508,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
foreach (var node in context.SelectedTreeNodes)
{
var loadedAssm = ((AssemblyTreeNode)node).LoadedAssembly;
if (!loadedAssm.HasLoadError && !loadedAssm.FileName.StartsWith("nupkg://"))
if (!loadedAssm.HasLoadError)
{
loadedAssm.IsAutoLoaded = false;
node.RaisePropertyChanged(nameof(node.Foreground));

1
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -77,6 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -77,6 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key));
if (dlg.ShowDialog() == true)
{
var data = this.Data;
data.Position = 0;
using (var fs = dlg.OpenFile())
{

2
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -41,7 +41,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
{
public ResourceTreeNode(Resource r)
{
if (r.IsNil)
if (r == null)
throw new ArgumentNullException(nameof(r));
this.Resource = r;
}

40
ILSpy/Views/NugetPackageBrowserDialog.xaml

@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="ICSharpCode.ILSpy.NugetPackageBrowserDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
xmlns:treeview="http://icsharpcode.net/sharpdevelop/treeview"
Title="{x:Static properties:Resources.NugetPackageBrowser}"
Style="{DynamicResource DialogWindow}"
WindowStartupLocation="CenterOwner"
ResizeMode="CanResizeWithGrip"
MinWidth="200"
MinHeight="150"
Height="350"
Width="750">
<Grid
Margin="12,8">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="1*" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{x:Static properties:Resources.SelectAssembliesOpen}" Margin="5" />
<ListBox ItemsSource="{Binding Package.Entries}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button IsDefault="True" Margin="2,0" IsEnabled="{Binding HasSelection}" Name="okButton" Click="OKButton_Click" Content="{x:Static properties:Resources.Open}"/>
<Button IsCancel="True" Margin="2,0" Content="{x:Static properties:Resources.Cancel}"/>
</StackPanel>
</Grid>
</Window>

86
ILSpy/Views/NugetPackageBrowserDialog.xaml.cs

@ -1,86 +0,0 @@ @@ -1,86 +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.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using ICSharpCode.ILSpy.Controls;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Interaction logic for NugetPackageBrowserDialog.xaml
/// </summary>
public partial class NugetPackageBrowserDialog : Window, INotifyPropertyChanged
{
public LoadedNugetPackage Package { get; }
public NugetPackageBrowserDialog()
{
InitializeComponent();
}
public NugetPackageBrowserDialog(LoadedNugetPackage package)
{
InitializeComponent();
this.Package = package;
this.Package.PropertyChanged += Package_PropertyChanged;
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
void Package_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Package.SelectedEntries))
{
OnPropertyChanged(new PropertyChangedEventArgs("HasSelection"));
}
}
void OKButton_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
Close();
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public Entry[] SelectedItems {
get {
return Package.SelectedEntries.ToArray();
}
}
public bool HasSelection {
get { return SelectedItems.Length > 0; }
}
}
}
Loading…
Cancel
Save