From f74d3c40a99bc228e1b62fc1c5d5d00eeb9598a2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 18 Oct 2020 19:22:13 +0200 Subject: [PATCH] Add NuGet packages (and other files) to TreeView --- ICSharpCode.Decompiler/Metadata/Dom.cs | 32 ++--- ICSharpCode.Decompiler/Metadata/PEFile.cs | 2 +- ILSpy/AssemblyList.cs | 27 +--- ILSpy/ILSpy.csproj | 10 +- ILSpy/Images/Images.cs | 4 +- ILSpy/Images/NuGet.png | Bin 0 -> 13750 bytes ILSpy/Languages/CSharpLanguage.cs | 4 + ILSpy/LoadedAssembly.cs | 129 ++++++++++++++---- ILSpy/LoadedNugetPackage.cs | 121 ---------------- ILSpy/LoadedPackage.cs | 99 ++++++++++++++ ILSpy/MainWindow.xaml.cs | 65 ++------- ILSpy/TreeNodes/AssemblyListTreeNode.cs | 29 +--- ILSpy/TreeNodes/AssemblyTreeNode.cs | 106 ++++++++++---- .../ResourceNodes/ResourceEntryNode.cs | 1 + .../ResourceNodes/ResourceTreeNode.cs | 2 +- ILSpy/Views/NugetPackageBrowserDialog.xaml | 40 ------ ILSpy/Views/NugetPackageBrowserDialog.xaml.cs | 86 ------------ 17 files changed, 322 insertions(+), 435 deletions(-) create mode 100644 ILSpy/Images/NuGet.png delete mode 100644 ILSpy/LoadedNugetPackage.cs create mode 100644 ILSpy/LoadedPackage.cs delete mode 100644 ILSpy/Views/NugetPackageBrowserDialog.xaml delete mode 100644 ILSpy/Views/NugetPackageBrowserDialog.xaml.cs diff --git a/ICSharpCode.Decompiler/Metadata/Dom.cs b/ICSharpCode.Decompiler/Metadata/Dom.cs index d9ed722ad..a8a7668ad 100644 --- a/ICSharpCode.Decompiler/Metadata/Dom.cs +++ b/ICSharpCode.Decompiler/Metadata/Dom.cs @@ -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 AssemblyLinked, } - public struct Resource : IEquatable + 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 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 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 return ResourceType.Linked; } - public unsafe Stream TryOpenStream() + public override unsafe Stream TryOpenStream() { if (ResourceType != ResourceType.Embedded) return null; diff --git a/ICSharpCode.Decompiler/Metadata/PEFile.cs b/ICSharpCode.Decompiler/Metadata/PEFile.cs index a81258a10..2b8192a90 100644 --- a/ICSharpCode.Decompiler/Metadata/PEFile.cs +++ b/ICSharpCode.Decompiler/Metadata/PEFile.cs @@ -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); } } diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index a57606e17..4c10546e1 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -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); } /// @@ -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 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) { diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 9e90df629..4a9cb4e16 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -382,7 +382,7 @@ - + @@ -390,9 +390,6 @@ - - NugetPackageBrowserDialog.xaml - OpenFromGacDialog.xaml @@ -581,7 +578,6 @@ - @@ -853,6 +849,10 @@ + + + + powershell -NoProfile -ExecutionPolicy Bypass -File BuildTools/sort-resx.ps1 diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index c542172a0..29a2c6847 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -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 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"); diff --git a/ILSpy/Images/NuGet.png b/ILSpy/Images/NuGet.png new file mode 100644 index 0000000000000000000000000000000000000000..0f7717cf585287b7076153919481e13c5f1b1d63 GIT binary patch literal 13750 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 - /// 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 /// [DebuggerDisplay("[LoadedAssembly {shortName}]")] public sealed class LoadedAssembly { + /// + /// Maps from PEFile (successfully loaded .NET module) back to the LoadedAssembly instance + /// that was used to load the module. + /// internal static readonly ConditionalWeakTable loadedAssemblies = new ConditionalWeakTable(); - readonly Task 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 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 = 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 /// /// Returns a target framework identifier in the form '<framework>Version=v<version>'. /// 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). /// public async Task GetTargetFrameworkIdAsync() { @@ -76,12 +106,24 @@ namespace ICSharpCode.ILSpy IDebugInfoProvider debugInfoProvider; + /// + /// Gets the . + /// + public Task GetLoadResultAsync() + { + return loadingTask; + } + /// /// Gets the . /// - public Task GetPEFileAsync() + public async Task GetPEFileAsync() { - return assemblyTask; + var loadResult = await loadingTask.ConfigureAwait(false); + if (loadResult.PEFile != null) + return loadResult.PEFile; + else + throw loadResult.PEFileLoadException; } /// @@ -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 LoadAsync(Task 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 { loadedAssemblies.Add(module, this); } - return module; + return new LoadResult(module); } [ThreadStatic] @@ -524,9 +595,10 @@ namespace ICSharpCode.ILSpy return asm; } + [Obsolete("Use GetPEFileAsync() or GetLoadResultAsync() instead")] public Task ContinueWhenLoaded(Action> onAssemblyLoaded, TaskScheduler taskScheduler) { - return this.assemblyTask.ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler); + return this.GetPEFileAsync().ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler); } /// @@ -535,8 +607,7 @@ namespace ICSharpCode.ILSpy /// public void WaitUntilLoaded() { - assemblyTask.Wait(); + loadingTask.Wait(); } - } } diff --git a/ILSpy/LoadedNugetPackage.cs b/ILSpy/LoadedNugetPackage.cs deleted file mode 100644 index e685409b2..000000000 --- a/ILSpy/LoadedNugetPackage.cs +++ /dev/null @@ -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 Entries { get; } = new List(); - public List SelectedEntries { get; } = new List(); - - 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; - } - } -} diff --git a/ILSpy/LoadedPackage.cs b/ILSpy/LoadedPackage.cs new file mode 100644 index 000000000..4dfa9aeda --- /dev/null +++ b/ILSpy/LoadedPackage.cs @@ -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 +{ + /// + /// NuGet package or .NET bundle: + /// + public class LoadedPackage + { + public enum PackageKind + { + Zip, + } + + public PackageKind Kind { get; } + + public List Entries { get; } = new List(); + + 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 + { + /// + /// Gets the file name of the entry (may include path components, relative to the package root). + /// + public abstract override string Name { get; } + + /// + /// Gets the full file name for the entry. + /// + public abstract string FullName { get; } + } +} diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 8975160af..884535761 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -69,7 +69,6 @@ namespace ICSharpCode.ILSpy partial class MainWindow : Window { bool refreshInProgress; - bool handlingNugetPackageSelection; readonly NavigationHistory history = new NavigationHistory(); ILSpySettings spySettingsForMainWindow_Loaded; internal SessionSettings sessionSettings; @@ -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 lines = new List(); using (StringReader r = new StringReader(data)) @@ -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) diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index 624a29f23..ff819c9be 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -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 { 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 } } - private IEnumerable 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 Select = delegate { }; public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) @@ -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) { diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index a53f4b084..10e891ab1 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -48,13 +48,18 @@ namespace ICSharpCode.ILSpy.TreeNodes readonly Dictionary namespaces = new Dictionary(); readonly Dictionary typeDict = new Dictionary(); 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 } } - 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 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 get { return !LoadedAssembly.HasLoadError; } } - void OnAssemblyLoaded(Task 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 } } + 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; /// @@ -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 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 { 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 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)); diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index 160ad478c..2eaaf73f6 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -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()) { diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index 308e29c67..c1fa916cc 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -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; } diff --git a/ILSpy/Views/NugetPackageBrowserDialog.xaml b/ILSpy/Views/NugetPackageBrowserDialog.xaml deleted file mode 100644 index fe29101fb..000000000 --- a/ILSpy/Views/NugetPackageBrowserDialog.xaml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - -