From 8dbfa825d3f821c7b68259b4431384577c42ab88 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 21 Mar 2019 01:23:39 +0100 Subject: [PATCH] Initial implementation of metadata explorer. --- ILSpy/Commands/DecompileCommand.cs | 91 ++++++ ILSpy/ILSpy.csproj | 20 ++ ILSpy/Metadata/CoffHeaderTreeNode.cs | 101 ++++++ ILSpy/Metadata/DataDirectoriesTreeNode.cs | 91 ++++++ ILSpy/Metadata/DosHeaderTreeNode.cs | 74 +++++ ILSpy/Metadata/EntityHandleTemplate.cs | 55 ++++ ILSpy/Metadata/ExportedTypeTableTreeNode.cs | 96 ++++++ ILSpy/Metadata/FieldTableTreeNode.cs | 100 ++++++ ILSpy/Metadata/Helpers.cs | 70 ++++ ILSpy/Metadata/MetadataTableViews.xaml | 339 ++++++++++++++++++++ ILSpy/Metadata/MetadataTableViews.xaml.cs | 39 +++ ILSpy/Metadata/MetadataTreeNode.cs | 84 +++++ ILSpy/Metadata/MethodTableTreeNode.cs | 100 ++++++ ILSpy/Metadata/ModuleTableTreeNode.cs | 95 ++++++ ILSpy/Metadata/OptionalHeaderTreeNode.cs | 125 ++++++++ ILSpy/Metadata/StringHandleTemplate.cs | 75 +++++ ILSpy/Metadata/TypeDefTableTreeNode.cs | 125 ++++++++ ILSpy/Metadata/TypeRefTableTreeNode.cs | 89 +++++ ILSpy/TreeNodes/AssemblyTreeNode.cs | 2 +- 19 files changed, 1770 insertions(+), 1 deletion(-) create mode 100644 ILSpy/Commands/DecompileCommand.cs create mode 100644 ILSpy/Metadata/CoffHeaderTreeNode.cs create mode 100644 ILSpy/Metadata/DataDirectoriesTreeNode.cs create mode 100644 ILSpy/Metadata/DosHeaderTreeNode.cs create mode 100644 ILSpy/Metadata/EntityHandleTemplate.cs create mode 100644 ILSpy/Metadata/ExportedTypeTableTreeNode.cs create mode 100644 ILSpy/Metadata/FieldTableTreeNode.cs create mode 100644 ILSpy/Metadata/Helpers.cs create mode 100644 ILSpy/Metadata/MetadataTableViews.xaml create mode 100644 ILSpy/Metadata/MetadataTableViews.xaml.cs create mode 100644 ILSpy/Metadata/MetadataTreeNode.cs create mode 100644 ILSpy/Metadata/MethodTableTreeNode.cs create mode 100644 ILSpy/Metadata/ModuleTableTreeNode.cs create mode 100644 ILSpy/Metadata/OptionalHeaderTreeNode.cs create mode 100644 ILSpy/Metadata/StringHandleTemplate.cs create mode 100644 ILSpy/Metadata/TypeDefTableTreeNode.cs create mode 100644 ILSpy/Metadata/TypeRefTableTreeNode.cs diff --git a/ILSpy/Commands/DecompileCommand.cs b/ILSpy/Commands/DecompileCommand.cs new file mode 100644 index 000000000..3d8840024 --- /dev/null +++ b/ILSpy/Commands/DecompileCommand.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Analyzers; +using ICSharpCode.ILSpy.Controls; +using ICSharpCode.ILSpy.Metadata; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Commands +{ + [ExportContextMenuEntry(Header = "Decompile", Order = 10)] + class DecompileCommand : IContextMenuEntry + { + public bool IsVisible(TextViewContext context) + { + var (c, selectedItem) = context.GetColumnAndRowFromMousePosition(); + if (c is FilterableGridViewColumn column && column.CellTemplateSelector is HandleTemplate && selectedItem is IMemberTreeNode) { + var selectedHandle = MetadataTokens.EntityHandle((int)selectedItem.GetType().GetProperty(column.SortBy).GetValue(selectedItem)); + return !selectedHandle.IsNil && (selectedHandle.Kind == HandleKind.TypeDefinition || selectedHandle.Kind == HandleKind.FieldDefinition || selectedHandle.Kind == HandleKind.MethodDefinition || selectedHandle.Kind == HandleKind.PropertyDefinition || selectedHandle.Kind == HandleKind.EventDefinition); + } + 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) + { + var (c, selectedItem) = context.GetColumnAndRowFromMousePosition(); + if (c is FilterableGridViewColumn column && column.CellTemplateSelector is HandleTemplate && selectedItem is IMemberTreeNode) { + var selectedHandle = MetadataTokens.EntityHandle((int)selectedItem.GetType().GetProperty(column.SortBy).GetValue(selectedItem)); + return !selectedHandle.IsNil && (selectedHandle.Kind == HandleKind.TypeDefinition || selectedHandle.Kind == HandleKind.FieldDefinition || selectedHandle.Kind == HandleKind.MethodDefinition || selectedHandle.Kind == HandleKind.PropertyDefinition || selectedHandle.Kind == HandleKind.EventDefinition); + } + 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; + var (c, selectedItem) = context.GetColumnAndRowFromMousePosition(); + if (c is FilterableGridViewColumn column && column.CellTemplateSelector is HandleTemplate && selectedItem is IMemberTreeNode semanticContext) { + var selectedHandle = MetadataTokens.EntityHandle((int)selectedItem.GetType().GetProperty(column.SortBy).GetValue(selectedItem)); + var module = (MetadataModule)semanticContext.Member.ParentModule; + switch (selectedHandle.Kind) { + case HandleKind.TypeDefinition: + selection = module.GetDefinition((TypeDefinitionHandle)selectedHandle); + break; + case HandleKind.FieldDefinition: + selection = module.GetDefinition((FieldDefinitionHandle)selectedHandle); + break; + case HandleKind.MethodDefinition: + selection = module.GetDefinition((MethodDefinitionHandle)selectedHandle); + break; + case HandleKind.PropertyDefinition: + selection = module.GetDefinition((PropertyDefinitionHandle)selectedHandle); + break; + case HandleKind.EventDefinition: + selection = module.GetDefinition((EventDefinitionHandle)selectedHandle); + break; + } + } else if (context.SelectedTreeNodes?[0] is IMemberTreeNode node) { + selection = node.Member; + } else if (context.Reference?.Reference is IEntity entity) { + selection = entity; + } + if (selection != null) + MainWindow.Instance.JumpToReference(selection); + } + } +} diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 924d0a615..1dd089b63 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -102,6 +102,7 @@ + @@ -159,6 +160,21 @@ + + + + + + + + + + + + + + + @@ -314,6 +330,7 @@ + @@ -390,6 +407,9 @@ + + MSBuild:Compile + diff --git a/ILSpy/Metadata/CoffHeaderTreeNode.cs b/ILSpy/Metadata/CoffHeaderTreeNode.cs new file mode 100644 index 000000000..0055f52d0 --- /dev/null +++ b/ILSpy/Metadata/CoffHeaderTreeNode.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + class CoffHeaderTreeNode : ILSpyTreeNode + { + private PEFile module; + + public CoffHeaderTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => "COFF Header"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + var dataGrid = new DataGrid { + Columns = { + new DataGridTextColumn { IsReadOnly = true, Header = "Member", Binding = new Binding("Member") }, + new DataGridTextColumn { IsReadOnly = true, Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8" } }, + new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") }, + new DataGridTextColumn { IsReadOnly = true, Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance } }, + new DataGridTextColumn { IsReadOnly = true, Header = "Meaning", Binding = new Binding("Meaning") }, + }, + AutoGenerateColumns = false, + CanUserAddRows = false, + CanUserDeleteRows = false, + RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector(), + RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible + }; + var headers = module.Reader.PEHeaders; + var header = headers.CoffHeader; + + var entries = new List(); + entries.Add(new Entry(headers.CoffHeaderStartOffset, (int)header.Machine, 2, "Machine", header.Machine.ToString())); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 2, (int)header.NumberOfSections, 2, "Number of Sections", "Number of sections; indicates size of the Section Table, which immediately follows the headers.")); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 4, header.TimeDateStamp, 4, "Time/Date Stamp", DateTimeOffset.FromUnixTimeSeconds(unchecked((uint)header.TimeDateStamp)).DateTime + " - Time and date the file was created in seconds since January 1st 1970 00:00:00 or 0.")); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 8, header.PointerToSymbolTable, 4, "Pointer to Symbol Table", "Always 0 in .NET executables.")); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 12, header.NumberOfSymbols, 4, "Number of Symbols", "Always 0 in .NET executables.")); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 16, (int)header.SizeOfOptionalHeader, 2, "Optional Header Size", "Size of the optional header.")); + entries.Add(new Entry(headers.CoffHeaderStartOffset + 18, (int)header.Characteristics, 2, "Characteristics", "Flags indicating attributes of the file.")); + + dataGrid.ItemsSource = entries; + + textView.ShowContent(new[] { this }, dataGrid); + return true; + } + + private class CharacteristicsDataTemplateSelector : DataTemplateSelector + { + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (((Entry)item).Member == "Characteristics") + return MakeDataTemplate((int)((Entry)item).Value); + return new DataTemplate(); + } + + private DataTemplate MakeDataTemplate(int flags) + { + FrameworkElementFactory dataGridFactory = new FrameworkElementFactory(typeof(DataGrid)); + dataGridFactory.SetValue(DataGrid.ItemsSourceProperty, new[] { + new { Value = (flags & 0x0001) != 0, Meaning = "Relocation info stripped from file" }, + new { Value = (flags & 0x0002) != 0, Meaning = "File is executable" }, + new { Value = (flags & 0x0004) != 0, Meaning = "Line numbers stripped from file" }, + new { Value = (flags & 0x0008) != 0, Meaning = "Local symbols stripped from file" }, + new { Value = (flags & 0x0010) != 0, Meaning = "Aggressively trim working set" }, + new { Value = (flags & 0x0020) != 0, Meaning = "Large address aware" }, + new { Value = (flags & 0x0040) != 0, Meaning = "Reserved" }, + new { Value = (flags & 0x0080) != 0, Meaning = "Bytes of machine words are reversed (Low)" }, + new { Value = (flags & 0x0100) != 0, Meaning = "32-bit word machine" }, + new { Value = (flags & 0x0200) != 0, Meaning = "Debugging info stripped from file in .DBG file" }, + new { Value = (flags & 0x0400) != 0, Meaning = "If image is on removable media, copy and run from the swap file" }, + new { Value = (flags & 0x0800) != 0, Meaning = "If image is on Net, copy and run from the swap file" }, + new { Value = (flags & 0x1000) != 0, Meaning = "System" }, + new { Value = (flags & 0x2000) != 0, Meaning = "DLL" }, + new { Value = (flags & 0x4000) != 0, Meaning = "File should only be run on a UP machine" }, + new { Value = (flags & 0x8000) != 0, Meaning = "Bytes of machine words are reversed (High)" }, + }); + DataTemplate template = new DataTemplate(); + template.VisualTree = dataGridFactory; + return template; + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "COFF Header"); + } + } +} diff --git a/ILSpy/Metadata/DataDirectoriesTreeNode.cs b/ILSpy/Metadata/DataDirectoriesTreeNode.cs new file mode 100644 index 000000000..3e1432514 --- /dev/null +++ b/ILSpy/Metadata/DataDirectoriesTreeNode.cs @@ -0,0 +1,91 @@ +using System.Reflection.PortableExecutable; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + class DataDirectoriesTreeNode : ILSpyTreeNode + { + private PEFile module; + + public DataDirectoriesTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => "Data Directories"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + var dataGrid = new DataGrid { + Columns = { + new DataGridTextColumn { IsReadOnly = true, Header = "Name", Binding = new Binding("Name") }, + new DataGridTextColumn { IsReadOnly = true, Header = "RVA", Binding = new Binding("RVA") { StringFormat = "X8" } }, + new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") { StringFormat = "X8" } }, + new DataGridTextColumn { IsReadOnly = true, Header = "Section", Binding = new Binding("Section") }, + }, + AutoGenerateColumns = false, + CanUserAddRows = false, + CanUserDeleteRows = false, + }; + var headers = module.Reader.PEHeaders; + var reader = module.Reader.GetEntireImage().GetReader(headers.PEHeaderStartOffset, 128); + var header = headers.PEHeader; + + var entries = new DataDirectoryEntry[] { + new DataDirectoryEntry(headers, "Export Table", header.ExportTableDirectory), + new DataDirectoryEntry(headers, "Import Table", header.ImportTableDirectory), + new DataDirectoryEntry(headers, "Resource Table", header.ResourceTableDirectory), + new DataDirectoryEntry(headers, "Exception Table", header.ExceptionTableDirectory), + new DataDirectoryEntry(headers, "Certificate Table", header.CertificateTableDirectory), + new DataDirectoryEntry(headers, "Base Relocation Table", header.BaseRelocationTableDirectory), + new DataDirectoryEntry(headers, "Debug Table", header.DebugTableDirectory), + new DataDirectoryEntry(headers, "Copyright Table", header.CopyrightTableDirectory), + new DataDirectoryEntry(headers, "Global Pointer Table", header.GlobalPointerTableDirectory), + new DataDirectoryEntry(headers, "Thread Local Storage Table", header.ThreadLocalStorageTableDirectory), + new DataDirectoryEntry(headers, "Load Config", header.LoadConfigTableDirectory), + new DataDirectoryEntry(headers, "Bound Import", header.BoundImportTableDirectory), + new DataDirectoryEntry(headers, "Import Address Table", header.ImportAddressTableDirectory), + new DataDirectoryEntry(headers, "Delay Import Descriptor", header.DelayImportTableDirectory), + new DataDirectoryEntry(headers, "CLI Header", header.CorHeaderTableDirectory), + }; + + dataGrid.ItemsSource = entries; + + textView.ShowContent(new[] { this }, dataGrid); + return true; + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "Data Directories"); + } + + class DataDirectoryEntry + { + public string Name { get; set; } + public int RVA { get; set; } + public int Size { get; set; } + public string Section { get; set; } + + public DataDirectoryEntry(string name, int rva, int size, string section) + { + this.Name = name; + this.RVA = rva; + this.Size = size; + this.Section = section; + } + + public DataDirectoryEntry(PEHeaders headers, string name, DirectoryEntry entry) + : this(name, entry.RelativeVirtualAddress, entry.Size, (headers.GetContainingSectionIndex(entry.RelativeVirtualAddress) >= 0) ? headers.SectionHeaders[headers.GetContainingSectionIndex(entry.RelativeVirtualAddress)].Name : "") + { + } + } + } +} diff --git a/ILSpy/Metadata/DosHeaderTreeNode.cs b/ILSpy/Metadata/DosHeaderTreeNode.cs new file mode 100644 index 000000000..9fa4bddd5 --- /dev/null +++ b/ILSpy/Metadata/DosHeaderTreeNode.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + class DosHeaderTreeNode : ILSpyTreeNode + { + private PEFile module; + + public DosHeaderTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => "DOS Header"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + var view = Helpers.CreateListView("EntryView"); + var reader = module.Reader.GetEntireImage().GetReader(0, 64); + + var entries = new List(); + + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_magic", "Magic Number (MZ)")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cblp", "Bytes on last page of file")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cp", "Pages in file")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_crlc", "Relocations")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cparhdr", "Size of header in paragraphs")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_minalloc", "Minimum extra paragraphs needed")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_maxalloc", "Maximum extra paragraphs needed")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_ss", "Initial (relative) SS value")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_sp", "Initial SP value")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_csum", "Checksum")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_ip", "Initial IP value")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_cs", "Initial (relative) CS value")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_lfarlc", "File address of relocation table")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_ovno", "Overlay number")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[0]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[1]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[2]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res[3]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_oemid", "OEM identifier (for e_oeminfo)")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_oeminfo", "OEM information; e_oemid specific")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[0]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[1]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[2]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[3]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[4]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[5]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[6]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[7]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[8]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "e_res2[9]", "Reserved words")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "e_lfanew", "File address of new exe header")); + + view.ItemsSource = entries; + + textView.ShowContent(new[] { this }, view); + return true; + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "DOS Header"); + } + } +} diff --git a/ILSpy/Metadata/EntityHandleTemplate.cs b/ILSpy/Metadata/EntityHandleTemplate.cs new file mode 100644 index 000000000..fb153315b --- /dev/null +++ b/ILSpy/Metadata/EntityHandleTemplate.cs @@ -0,0 +1,55 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; + +namespace ICSharpCode.ILSpy.Metadata +{ + public class HandleTemplate : DataTemplateSelector + { + public string ValuePropertyName { get; set; } + public string TooltipPropertyName { get; set; } + + private DataTemplate template; + + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (template != null) + return template; + var textBlock = new FrameworkElementFactory(typeof(TextBlock), "textBlock"); + textBlock.SetValue(FrameworkElement.MarginProperty, new Thickness(3, 1, 0, 0)); + textBlock.SetBinding(TextBlock.TextProperty, new Binding(ValuePropertyName) { StringFormat = "X8" }); + var textBox = new FrameworkElementFactory(typeof(TextBox), "textBox"); + textBox.SetBinding(TextBox.TextProperty, new Binding(ValuePropertyName) { StringFormat = "X8", Mode = BindingMode.OneWay }); + textBox.SetValue(TextBox.VisibilityProperty, Visibility.Hidden); + textBox.SetValue(TextBox.IsReadOnlyCaretVisibleProperty, true); + textBox.SetValue(TextBox.IsReadOnlyProperty, true); + if (TooltipPropertyName != null) + textBox.SetBinding(FrameworkElement.ToolTipProperty, new Binding(TooltipPropertyName)); + var grid = new FrameworkElementFactory(typeof(Grid)); + grid.AppendChild(textBlock); + grid.AppendChild(textBox); + template = new DataTemplate { + VisualTree = grid, + Triggers = { + new Trigger { + Property = UIElement.IsMouseOverProperty, + Value = true, + Setters = { + new Setter(UIElement.VisibilityProperty, Visibility.Visible, "textBox"), + new Setter(UIElement.VisibilityProperty, Visibility.Hidden, "textBlock"), + } + }, + new Trigger { + Property = UIElement.IsKeyboardFocusWithinProperty, + Value = true, + Setters = { + new Setter(UIElement.VisibilityProperty, Visibility.Visible, "textBox"), + new Setter(UIElement.VisibilityProperty, Visibility.Hidden, "textBlock"), + } + } + } + }; + return template; + } + } +} diff --git a/ILSpy/Metadata/ExportedTypeTableTreeNode.cs b/ILSpy/Metadata/ExportedTypeTableTreeNode.cs new file mode 100644 index 000000000..c5beb64af --- /dev/null +++ b/ILSpy/Metadata/ExportedTypeTableTreeNode.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Controls; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.Metadata +{ + internal class ExportedTypeTableTreeNode : ILSpyTreeNode + { + private PEFile module; + + public ExportedTypeTableTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => $"27 ExportedType ({module.Metadata.GetTableRowCount(TableIndex.ExportedType)})"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + ListView view = Helpers.CreateListView("ExportedTypesView"); + + var metadata = module.Metadata; + + var list = new List(); + + foreach (var row in metadata.ExportedTypes) { + list.Add(new ExportedTypeEntry(module.Reader.PEHeaders.MetadataStartOffset, metadata, row, metadata.GetExportedType(row))); + } + + view.ItemsSource = list; + + textView.ShowContent(new[] { this }, view); + return true; + } + + struct ExportedTypeEntry + { + readonly int metadataOffset; + readonly MetadataReader metadata; + readonly ExportedTypeHandle handle; + readonly ExportedType type; + + public int RID => MetadataTokens.GetRowNumber(handle); + + public int Token => MetadataTokens.GetToken(handle); + + public int Offset => metadataOffset + + metadata.GetTableMetadataOffset(TableIndex.ExportedType) + + metadata.GetTableRowSize(TableIndex.ExportedType) * (RID-1); + + public int Attributes => (int)type.Attributes; + + public string AttributesTooltip => Helpers.AttributesToString(type.Attributes); + + public int TypeDefId => type.GetTypeDefinitionId(); + + public int TypeName => MetadataTokens.GetHeapOffset(type.Name); + + public string TypeNameTooltip => metadata.GetString(type.Name); + + public int TypeNamespace => MetadataTokens.GetHeapOffset(type.Namespace); + + public string TypeNamespaceTooltip => metadata.GetString(type.Namespace); + + public int Implementation => MetadataTokens.GetToken(type.Implementation); + + public ExportedTypeEntry(int metadataOffset, MetadataReader metadata, ExportedTypeHandle handle, ExportedType type) + { + this.metadataOffset = metadataOffset; + this.metadata = metadata; + this.handle = handle; + this.type = type; + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "ExportedType"); + } + } +} \ No newline at end of file diff --git a/ILSpy/Metadata/FieldTableTreeNode.cs b/ILSpy/Metadata/FieldTableTreeNode.cs new file mode 100644 index 000000000..c7b0fe010 --- /dev/null +++ b/ILSpy/Metadata/FieldTableTreeNode.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + internal class FieldTableTreeNode : ILSpyTreeNode + { + private PEFile module; + + public FieldTableTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => $"04 Field ({module.Metadata.GetTableRowCount(TableIndex.Field)})"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + ListView view = Helpers.CreateListView("FieldDefsView"); + var metadata = module.Metadata; + + var list = new List(); + + foreach (var row in metadata.FieldDefinitions) + list.Add(new FieldDefEntry(module, row)); + + view.ItemsSource = list; + + textView.ShowContent(new[] { this }, view); + return true; + } + + struct FieldDefEntry : IMemberTreeNode + { + readonly int metadataOffset; + readonly PEFile module; + readonly MetadataReader metadata; + readonly FieldDefinitionHandle handle; + readonly FieldDefinition fieldDef; + + public int RID => MetadataTokens.GetRowNumber(handle); + + public int Token => MetadataTokens.GetToken(handle); + + public int Offset => metadataOffset + + metadata.GetTableMetadataOffset(TableIndex.Field) + + metadata.GetTableRowSize(TableIndex.Field) * (RID - 1); + + public int Attributes => (int)fieldDef.Attributes; + + public string AttributesTooltip => null; //Helpers.AttributesToString(fieldDef.Attributes); + + public int NameStringHandle => MetadataTokens.GetHeapOffset(fieldDef.Name); + + public string Name => metadata.GetString(fieldDef.Name); + + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + + public int Signature => MetadataTokens.GetHeapOffset(fieldDef.Signature); + + public string SignatureTooltip { + get { + ITextOutput output = new PlainTextOutput(); + var context = new Decompiler.Metadata.GenericContext(default(TypeDefinitionHandle), module); + ((EntityHandle)handle).WriteTo(module, output, context); + return output.ToString(); + } + } + + public FieldDefEntry(PEFile module, FieldDefinitionHandle handle) + { + this.metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; + this.module = module; + this.metadata = module.Metadata; + this.handle = handle; + this.fieldDef = metadata.GetFieldDefinition(handle); + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "FieldDefs"); + } + } +} diff --git a/ILSpy/Metadata/Helpers.cs b/ILSpy/Metadata/Helpers.cs new file mode 100644 index 000000000..fcd4abf4c --- /dev/null +++ b/ILSpy/Metadata/Helpers.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using ICSharpCode.ILSpy.Controls; + +namespace ICSharpCode.ILSpy.Metadata +{ + static class Helpers + { + public static ListView CreateListView(string name) + { + MetadataTableViews dict = new MetadataTableViews(); + var view = new ListView { + View = (GridView)dict[name], + ItemContainerStyle = (Style)dict["ItemContainerStyle"] + }; + ContextMenuProvider.Add(view); + FilterableGridViewColumn.SetSortMode(view, ListViewSortMode.Automatic); + foreach (var column in ((GridView)view.View).Columns) { + FilterableGridViewColumn.SetParentView(column, view); + } + + return view; + } + + public static string AttributesToString(TypeAttributes attributes) + { + const TypeAttributes allMasks = TypeAttributes.ClassSemanticsMask | TypeAttributes.CustomFormatMask | TypeAttributes.LayoutMask | TypeAttributes.ReservedMask | TypeAttributes.StringFormatMask | TypeAttributes.VisibilityMask; + StringBuilder sb = new StringBuilder(); + var visibility = attributes & TypeAttributes.VisibilityMask; + sb.AppendLine("Visibility: " + (visibility == 0 ? "NotPublic" : typeof(TypeAttributes).GetEnumName(visibility))); + var layout = attributes & TypeAttributes.LayoutMask; + sb.AppendLine("Class layout: " + (layout == 0 ? "AutoLayout" : typeof(TypeAttributes).GetEnumName(layout))); + var semantics = attributes & TypeAttributes.ClassSemanticsMask; + sb.AppendLine("Class semantics: " + (semantics == 0 ? "Class" : typeof(TypeAttributes).GetEnumName(semantics))); + var stringFormat = attributes & TypeAttributes.StringFormatMask; + sb.AppendLine("String format: " + (stringFormat == 0 ? "AnsiClass" : typeof(TypeAttributes).GetEnumName(stringFormat))); + var customStringFormat = attributes & TypeAttributes.CustomFormatMask; + sb.AppendLine("Custom string format: 0x" + customStringFormat.ToString("x")); + var reserved = attributes & TypeAttributes.ReservedMask; + sb.AppendLine("Reserved attributes: " + (reserved == 0 ? "" : reserved.ToString())); + var additional = attributes & ~allMasks; + sb.Append("Additional attributes: "); + AdditionalAttributes(sb, additional); + if (sb.Length == 0) + return null; + return sb.ToString(); + } + + static void AdditionalAttributes(StringBuilder sb, TypeAttributes attributes) + { + bool first = true; + for (int bit = 0; bit < 32; bit++) { + var value = (TypeAttributes)(1 << bit); + if ((attributes & value) != 0) { + if (!first) + sb.Append(", "); + first = false; + sb.Append(typeof(TypeAttributes).GetEnumName(value)); + } + } + } + } +} diff --git a/ILSpy/Metadata/MetadataTableViews.xaml b/ILSpy/Metadata/MetadataTableViews.xaml new file mode 100644 index 000000000..df572c3b0 --- /dev/null +++ b/ILSpy/Metadata/MetadataTableViews.xaml @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ILSpy/Metadata/MetadataTableViews.xaml.cs b/ILSpy/Metadata/MetadataTableViews.xaml.cs new file mode 100644 index 000000000..2c86e552a --- /dev/null +++ b/ILSpy/Metadata/MetadataTableViews.xaml.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace ICSharpCode.ILSpy.Metadata +{ + /// + /// Interaction logic for MetadataTableViews.xaml + /// + public partial class MetadataTableViews : ResourceDictionary + { + public MetadataTableViews() + { + InitializeComponent(); + } + + static MetadataTableViews instance; + + public static MetadataTableViews Instance { + get { + if (instance == null) { + instance = new MetadataTableViews(); + } + return instance; + } + } + } +} diff --git a/ILSpy/Metadata/MetadataTreeNode.cs b/ILSpy/Metadata/MetadataTreeNode.cs new file mode 100644 index 000000000..255dc7df4 --- /dev/null +++ b/ILSpy/Metadata/MetadataTreeNode.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection.PortableExecutable; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + class MetadataTreeNode : ILSpyTreeNode + { + private PEFile module; + private AssemblyTreeNode assemblyTreeNode; + + public MetadataTreeNode(PEFile module, AssemblyTreeNode assemblyTreeNode) + { + this.module = module; + this.assemblyTreeNode = assemblyTreeNode; + this.LazyLoading = true; + } + + public override object Text => "Metadata"; + + public override object Icon => Images.Library; + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "Metadata"); + } + + protected override void LoadChildren() + { + this.Children.Add(new DosHeaderTreeNode(module)); + this.Children.Add(new CoffHeaderTreeNode(module)); + this.Children.Add(new OptionalHeaderTreeNode(module)); + this.Children.Add(new DataDirectoriesTreeNode(module)); + this.Children.Add(new ModuleTableTreeNode(module)); + this.Children.Add(new TypeRefTableTreeNode(module)); + this.Children.Add(new TypeDefTableTreeNode(module)); + this.Children.Add(new FieldTableTreeNode(module)); + this.Children.Add(new MethodTableTreeNode(module)); + this.Children.Add(new ExportedTypeTableTreeNode(module)); + } + } + + class Entry + { + public string Member { get; set; } + public int Offset { get; set; } + public int Size { get; set; } + public object Value { get; set; } + public string Meaning { get; set; } + + public Entry(int offset, object value, int size, string member, string meaning) + { + this.Member = member; + this.Offset = offset; + this.Size = size; + this.Value = value; + this.Meaning = meaning; + } + } + + class ByteWidthConverter : IValueConverter + { + public static readonly ByteWidthConverter Instance = new ByteWidthConverter(); + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return string.Format("{0:X" + 2 * ((Entry)value).Size + "}", ((Entry)value).Value); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/ILSpy/Metadata/MethodTableTreeNode.cs b/ILSpy/Metadata/MethodTableTreeNode.cs new file mode 100644 index 000000000..18dfaae3c --- /dev/null +++ b/ILSpy/Metadata/MethodTableTreeNode.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + internal class MethodTableTreeNode : ILSpyTreeNode + { + private PEFile module; + + public MethodTableTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => $"06 Method ({module.Metadata.GetTableRowCount(TableIndex.MethodDef)})"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + ListView view = Helpers.CreateListView("MethodDefsView"); + var metadata = module.Metadata; + + var list = new List(); + + foreach (var row in metadata.MethodDefinitions) + list.Add(new MethodDefEntry(module, row)); + + view.ItemsSource = list; + + textView.ShowContent(new[] { this }, view); + return true; + } + + struct MethodDefEntry : IMemberTreeNode + { + readonly int metadataOffset; + readonly PEFile module; + readonly MetadataReader metadata; + readonly MethodDefinitionHandle handle; + readonly MethodDefinition methodDef; + + public int RID => MetadataTokens.GetRowNumber(handle); + + public int Token => MetadataTokens.GetToken(handle); + + public int Offset => metadataOffset + + metadata.GetTableMetadataOffset(TableIndex.MethodDef) + + metadata.GetTableRowSize(TableIndex.MethodDef) * (RID - 1); + + public int Attributes => (int)methodDef.Attributes; + + public string AttributesTooltip => null; //Helpers.AttributesToString(MethodDef.Attributes); + + public int NameStringHandle => MetadataTokens.GetHeapOffset(methodDef.Name); + + public string Name => metadata.GetString(methodDef.Name); + + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + + public int Signature => MetadataTokens.GetHeapOffset(methodDef.Signature); + + public string SignatureTooltip { + get { + ITextOutput output = new PlainTextOutput(); + var context = new Decompiler.Metadata.GenericContext(default(TypeDefinitionHandle), module); + ((EntityHandle)handle).WriteTo(module, output, context); + return output.ToString(); + } + } + + public MethodDefEntry(PEFile module, MethodDefinitionHandle handle) + { + this.metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; + this.module = module; + this.metadata = module.Metadata; + this.handle = handle; + this.methodDef = metadata.GetMethodDefinition(handle); + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "MethodDefs"); + } + } +} diff --git a/ILSpy/Metadata/ModuleTableTreeNode.cs b/ILSpy/Metadata/ModuleTableTreeNode.cs new file mode 100644 index 000000000..640b60887 --- /dev/null +++ b/ILSpy/Metadata/ModuleTableTreeNode.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Controls; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.Metadata +{ + internal class ModuleTableTreeNode : ILSpyTreeNode + { + private PEFile module; + + public ModuleTableTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => $"00 Module ({module.Metadata.GetTableRowCount(TableIndex.Module)})"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + ListView view = Helpers.CreateListView("ModulesView"); + var metadata = module.Metadata; + + var list = new List(); + + list.Add(new ModuleEntry(module.Reader.PEHeaders.MetadataStartOffset, metadata, EntityHandle.ModuleDefinition, metadata.GetModuleDefinition())); + + view.ItemsSource = list; + + textView.ShowContent(new[] { this }, view); + return true; + } + + struct ModuleEntry + { + readonly int metadataOffset; + readonly MetadataReader metadata; + readonly ModuleDefinitionHandle handle; + readonly ModuleDefinition module; + + public int RID => MetadataTokens.GetRowNumber(handle); + + public int Token => MetadataTokens.GetToken(handle); + + public int Offset => metadataOffset + + metadata.GetTableMetadataOffset(TableIndex.Module) + + metadata.GetTableRowSize(TableIndex.Module) * (RID-1); + + public int Generation => module.Generation; + + public int NameStringHandle => MetadataTokens.GetHeapOffset(module.Name); + + public string Name => metadata.GetString(module.Name); + + public int Mvid => MetadataTokens.GetHeapOffset(module.Mvid); + + public string MvidTooltip => metadata.GetGuid(module.Mvid).ToString(); + + public int GenerationId => MetadataTokens.GetHeapOffset(module.GenerationId); + + public string GenerationIdTooltip => module.GenerationId.IsNil ? null : metadata.GetGuid(module.GenerationId).ToString(); + + public int BaseGenerationId => MetadataTokens.GetHeapOffset(module.BaseGenerationId); + + public string BaseGenerationIdTooltip => module.BaseGenerationId.IsNil ? null : metadata.GetGuid(module.BaseGenerationId).ToString(); + + public ModuleEntry(int metadataOffset, MetadataReader metadata, ModuleDefinitionHandle handle, ModuleDefinition module) + { + this.metadataOffset = metadataOffset; + this.metadata = metadata; + this.handle = handle; + this.module = module; + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "Modules"); + } + } +} \ No newline at end of file diff --git a/ILSpy/Metadata/OptionalHeaderTreeNode.cs b/ILSpy/Metadata/OptionalHeaderTreeNode.cs new file mode 100644 index 000000000..9c5255003 --- /dev/null +++ b/ILSpy/Metadata/OptionalHeaderTreeNode.cs @@ -0,0 +1,125 @@ +using System.Collections.Generic; +using System.Reflection.PortableExecutable; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; + +namespace ICSharpCode.ILSpy.Metadata +{ + class OptionalHeaderTreeNode : ILSpyTreeNode + { + private PEFile module; + + public OptionalHeaderTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => "Optional Header"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + var dataGrid = new DataGrid { + Columns = { + new DataGridTextColumn { IsReadOnly = true, Header = "Member", Binding = new Binding("Member") }, + new DataGridTextColumn { IsReadOnly = true, Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8" } }, + new DataGridTextColumn { IsReadOnly = true, Header = "Size", Binding = new Binding("Size") }, + new DataGridTextColumn { IsReadOnly = true, Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance } }, + new DataGridTextColumn { IsReadOnly = true, Header = "Meaning", Binding = new Binding("Meaning") }, + }, + AutoGenerateColumns = false, + CanUserAddRows = false, + CanUserDeleteRows = false, + RowDetailsTemplateSelector = new DllCharacteristicsDataTemplateSelector(), + RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible + }; + var headers = module.Reader.PEHeaders; + var reader = module.Reader.GetEntireImage().GetReader(headers.PEHeaderStartOffset, 128); + var header = headers.PEHeader; + + var entries = new List(); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 2, "Magic", header.Magic.ToString())); + entries.Add(new Entry(reader.Offset, reader.ReadByte(), 1, "Major Linker Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadByte(), 1, "Minor Linker Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Code Size", "Size of the code (text) section, or the sum of all code sections if there are multiple sections.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Initialized Data Size", "Size of the initialized data section, or the sum of all code sections if there are multiple data sections.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Uninitialized Data Size", "Size of the uninitialized data section, or the sum of all code sections if there are multiple uninitialized data sections.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Entry Point RVA", "RVA of entry point, needs to point to bytes 0xFF 0x25 followed by the RVA in a section marked execute / read for EXEs or 0 for DLLs")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Base Of Code", "RVA of the code section.")); + entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Base Of Data", "RVA of the data section.")); + entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Image Base", "Shall be a multiple of 0x10000.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Section Alignment", "Shall be greater than File Alignment.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "File Alignment", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Major OS Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Minor OS Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Major Image Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Minor Image Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Major Subsystem Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Minor Subsystem Version", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt32(), 4, "Win32VersionValue", "")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Image Size", "Size, in bytes, of image, including all headers and padding; shall be a multiple of Section Alignment.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Header Size", "Combined size of MS-DOS Header, PE Header, PE Optional Header and padding; shall be a multiple of the file alignment.")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "File Checksum", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "Subsystem", header.Subsystem.ToString())); + entries.Add(new Entry(reader.Offset, reader.ReadUInt16(), 4, "DLL Characteristics", header.DllCharacteristics.ToString())); + entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Stack Reserve Size", "")); + entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Stack Commit Size", "")); + entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Heap Reserve Size", "")); + entries.Add(new Entry(reader.Offset, header.Magic == PEMagic.PE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), header.Magic == PEMagic.PE32Plus ? 8 : 4, "Heap Commit Size", "")); + entries.Add(new Entry(reader.Offset, reader.ReadUInt32(), 4, "Loader Flags", "")); + entries.Add(new Entry(reader.Offset, reader.ReadInt32(), 4, "Number of Data Directories", "")); + + dataGrid.ItemsSource = entries; + + textView.ShowContent(new[] { this }, dataGrid); + return true; + } + + private class DllCharacteristicsDataTemplateSelector : DataTemplateSelector + { + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (((Entry)item).Member == "DLL Characteristics") + return MakeDataTemplate((ushort)((Entry)item).Value); + return new DataTemplate(); + } + + private DataTemplate MakeDataTemplate(ushort flags) + { + FrameworkElementFactory dataGridFactory = new FrameworkElementFactory(typeof(DataGrid)); + dataGridFactory.SetValue(DataGrid.ItemsSourceProperty, new[] { + new { Value = (flags & 0x0001) != 0, Meaning = "Unused" }, + new { Value = (flags & 0x0002) != 0, Meaning = "Unused" }, + new { Value = (flags & 0x0004) != 0, Meaning = "Unused" }, + new { Value = (flags & 0x0008) != 0, Meaning = "Unused" }, + new { Value = (flags & 0x0010) != 0, Meaning = "Unused" }, + new { Value = (flags & 0x0020) != 0, Meaning = "ASLR with 64-bit address space" }, + new { Value = (flags & 0x0040) != 0, Meaning = "DLL can be relocated at load time" }, + new { Value = (flags & 0x0080) != 0, Meaning = "Code integrity checks are enforced" }, + new { Value = (flags & 0x0100) != 0, Meaning = "Image is NX compatible" }, + new { Value = (flags & 0x0200) != 0, Meaning = "Isolation aware, but do not isolate the image" }, + new { Value = (flags & 0x0400) != 0, Meaning = "Does not use structured exception handling (SEH)" }, + new { Value = (flags & 0x0800) != 0, Meaning = "Do not bind the image" }, + new { Value = (flags & 0x1000) != 0, Meaning = "Image should execute in an AppContainer" }, + new { Value = (flags & 0x2000) != 0, Meaning = "Driver is a WDM Driver" }, + new { Value = (flags & 0x4000) != 0, Meaning = "Image supports Control Flow Guard" }, + new { Value = (flags & 0x8000) != 0, Meaning = "Image is Terminal Server aware" }, + }); + DataTemplate template = new DataTemplate(); + template.VisualTree = dataGridFactory; + return template; + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "Optional Header"); + } + } +} diff --git a/ILSpy/Metadata/StringHandleTemplate.cs b/ILSpy/Metadata/StringHandleTemplate.cs new file mode 100644 index 000000000..86c4ff538 --- /dev/null +++ b/ILSpy/Metadata/StringHandleTemplate.cs @@ -0,0 +1,75 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; + +namespace ICSharpCode.ILSpy.Metadata +{ + public class StringHandleTemplate : DataTemplateSelector + { + public string ValuePropertyName { get; set; } + public string HandlePropertyName { get; set; } + + private DataTemplate template; + + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (template != null) + return template; + var textBlock = new FrameworkElementFactory(typeof(TextBlock), "textBlock"); + textBlock.SetValue(FrameworkElement.MarginProperty, new Thickness(3, 1, 0, 0)); + textBlock.SetBinding(TextBlock.TextProperty, new Binding(ValuePropertyName) { StringFormat = "\"{0}\"" }); + var textBox = new FrameworkElementFactory(typeof(TextBox), "textBox"); + textBox.SetBinding(TextBox.TextProperty, new Binding(ValuePropertyName) { StringFormat = "\"{0}\"", Mode = BindingMode.OneWay }); + textBox.SetValue(TextBox.VisibilityProperty, Visibility.Hidden); + textBox.SetValue(TextBox.IsReadOnlyCaretVisibleProperty, true); + textBox.SetValue(TextBox.IsReadOnlyProperty, true); + textBox.SetBinding(FrameworkElement.ToolTipProperty, new MultiBinding { + Converter = new StringHandleConverter(), + Bindings = { + new Binding(HandlePropertyName), + new Binding(ValuePropertyName) + } + }); + var grid = new FrameworkElementFactory(typeof(Grid)); + grid.AppendChild(textBlock); + grid.AppendChild(textBox); + template = new DataTemplate { + VisualTree = grid, + Triggers = { + new Trigger { + Property = UIElement.IsMouseOverProperty, + Value = true, + Setters = { + new Setter(UIElement.VisibilityProperty, Visibility.Visible, "textBox"), + new Setter(UIElement.VisibilityProperty, Visibility.Hidden, "textBlock"), + } + }, + new Trigger { + Property = UIElement.IsKeyboardFocusWithinProperty, + Value = true, + Setters = { + new Setter(UIElement.VisibilityProperty, Visibility.Visible, "textBox"), + new Setter(UIElement.VisibilityProperty, Visibility.Hidden, "textBlock"), + } + } + } + }; + return template; + } + + private class StringHandleConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + return string.Format("{0:X} \"{1}\"", values[0], values[1]); + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/ILSpy/Metadata/TypeDefTableTreeNode.cs b/ILSpy/Metadata/TypeDefTableTreeNode.cs new file mode 100644 index 000000000..df1a17fd6 --- /dev/null +++ b/ILSpy/Metadata/TypeDefTableTreeNode.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy.Controls; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.Metadata +{ + internal class TypeDefTableTreeNode : ILSpyTreeNode + { + private PEFile module; + + public TypeDefTableTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => $"02 TypeDef ({module.Metadata.GetTableRowCount(TableIndex.TypeDef)})"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + ListView view = Helpers.CreateListView("TypeDefsView"); + var metadata = module.Metadata; + + var list = new List(); + + foreach (var row in metadata.TypeDefinitions) + list.Add(new TypeDefEntry(module, row)); + + view.ItemsSource = list; + + textView.ShowContent(new[] { this }, view); + return true; + } + + struct TypeDefEntry : IMemberTreeNode + { + readonly int metadataOffset; + readonly PEFile module; + readonly MetadataReader metadata; + readonly TypeDefinitionHandle handle; + readonly TypeDefinition typeDef; + + public int RID => MetadataTokens.GetRowNumber(handle); + + public int Token => MetadataTokens.GetToken(handle); + + public int Offset => metadataOffset + + metadata.GetTableMetadataOffset(TableIndex.TypeDef) + + metadata.GetTableRowSize(TableIndex.TypeDef) * (RID-1); + + public int Attributes => (int)typeDef.Attributes; + + public string AttributesTooltip => Helpers.AttributesToString(typeDef.Attributes); + + public int NameStringHandle => MetadataTokens.GetHeapOffset(typeDef.Name); + + public string Name => metadata.GetString(typeDef.Name); + + public int NamespaceStringHandle => MetadataTokens.GetHeapOffset(typeDef.Namespace); + + public string Namespace => metadata.GetString(typeDef.Namespace); + + public int BaseType => MetadataTokens.GetToken(typeDef.BaseType); + + public string BaseTypeSignature { + get { + var output = new PlainTextOutput(); + var provider = new DisassemblerSignatureProvider(module, output); + if (typeDef.BaseType.IsNil) + return null; + switch (typeDef.BaseType.Kind) { + case HandleKind.TypeDefinition: + provider.GetTypeFromDefinition(module.Metadata, (TypeDefinitionHandle)typeDef.BaseType, 0)(ILNameSyntax.Signature); + return output.ToString(); + case HandleKind.TypeReference: + provider.GetTypeFromReference(module.Metadata, (TypeReferenceHandle)typeDef.BaseType, 0)(ILNameSyntax.Signature); + return output.ToString(); + case HandleKind.TypeSpecification: + provider.GetTypeFromSpecification(module.Metadata, new Decompiler.Metadata.GenericContext(default(TypeDefinitionHandle), module), (TypeSpecificationHandle)typeDef.BaseType, 0)(ILNameSyntax.Signature); + return output.ToString(); + default: + return null; + } + } + } + + public int FieldList => MetadataTokens.GetToken(typeDef.GetFields().FirstOrDefault()); + + public int MethodList => MetadataTokens.GetToken(typeDef.GetMethods().FirstOrDefault()); + + IEntity IMemberTreeNode.Member => ((MetadataModule)module.GetTypeSystemOrNull()?.MainModule).GetDefinition(handle); + + public TypeDefEntry(PEFile module, TypeDefinitionHandle handle) + { + this.metadataOffset = module.Reader.PEHeaders.MetadataStartOffset; + this.module = module; + this.metadata = module.Metadata; + this.handle = handle; + this.typeDef = metadata.GetTypeDefinition(handle); + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "TypeDefs"); + } + } +} \ No newline at end of file diff --git a/ILSpy/Metadata/TypeRefTableTreeNode.cs b/ILSpy/Metadata/TypeRefTableTreeNode.cs new file mode 100644 index 000000000..9f858ab20 --- /dev/null +++ b/ILSpy/Metadata/TypeRefTableTreeNode.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Text; +using System.Windows.Controls; +using System.Windows.Data; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.ILSpy.Controls; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; + +namespace ICSharpCode.ILSpy.Metadata +{ + internal class TypeRefTableTreeNode : ILSpyTreeNode + { + private PEFile module; + + public TypeRefTableTreeNode(PEFile module) + { + this.module = module; + } + + public override object Text => $"01 TypeRef ({module.Metadata.GetTableRowCount(TableIndex.TypeRef)})"; + + public override object Icon => Images.Literal; + + public override bool View(DecompilerTextView textView) + { + ListView view = Helpers.CreateListView("TypeRefsView"); + + var metadata = module.Metadata; + + var list = new List(); + + foreach (var row in metadata.TypeReferences) + list.Add(new TypeRefEntry(module.Reader.PEHeaders.MetadataStartOffset, metadata, row, metadata.GetTypeReference(row))); + + view.ItemsSource = list; + + textView.ShowContent(new[] { this }, view); + return true; + } + + struct TypeRefEntry + { + readonly int metadataOffset; + readonly MetadataReader metadata; + readonly TypeReferenceHandle handle; + readonly TypeReference typeRef; + + public int RID => MetadataTokens.GetRowNumber(handle); + + public int Token => MetadataTokens.GetToken(handle); + + public int Offset => metadataOffset + + metadata.GetTableMetadataOffset(TableIndex.TypeRef) + + metadata.GetTableRowSize(TableIndex.TypeRef) * (RID-1); + + public int ResolutionScope => MetadataTokens.GetToken(typeRef.ResolutionScope); + + public int Name => MetadataTokens.GetHeapOffset(typeRef.Name); + + public string NameTooltip => metadata.GetString(typeRef.Name); + + public int Namespace => MetadataTokens.GetHeapOffset(typeRef.Namespace); + + public string NamespaceTooltip => metadata.GetString(typeRef.Namespace); + + public TypeRefEntry(int metadataOffset, MetadataReader metadata, TypeReferenceHandle handle, TypeReference typeRef) + { + this.metadataOffset = metadataOffset; + this.metadata = metadata; + this.handle = handle; + this.typeRef = typeRef; + } + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, "TypeRefs"); + } + } +} \ No newline at end of file diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index feec18e3e..a58bc3295 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -142,7 +142,7 @@ namespace ICSharpCode.ILSpy.TreeNodes typeSystem = LoadedAssembly.GetTypeSystemOrNull(); var assembly = (MetadataModule)typeSystem.MainModule; var metadata = module.Metadata; - + this.Children.Add(new Metadata.MetadataTreeNode(module, this)); this.Children.Add(new ReferenceFolderTreeNode(module, this)); if (module.Resources.Any()) this.Children.Add(new ResourceListTreeNode(module));