Browse Source

#2421: Refactored OptionalHeaderTreeNode and CoffHeaderTreeNode in an attempt to fix the memory leak issue. Now, it's leaking less, but still leaking, if you resize the table so that it's very small and press (and keep holding) the down arrow after selecting the row "Loader Flags" for an extended amount of time. Looks like a bug in WPF.

pull/2536/head
Siegfried Pammer 4 years ago
parent
commit
0a753817c0
  1. 98
      ILSpy/Metadata/CoffHeaderTreeNode.cs
  2. 18
      ILSpy/Metadata/Helpers.cs
  3. 10
      ILSpy/Metadata/MetadataTableViews.xaml
  4. 18
      ILSpy/Metadata/MetadataTreeNode.cs
  5. 80
      ILSpy/Metadata/OptionalHeaderTreeNode.cs

98
ILSpy/Metadata/CoffHeaderTreeNode.cs

@ -24,7 +24,6 @@ using System.Windows.Data; @@ -24,7 +24,6 @@ using System.Windows.Data;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.ViewModels;
@ -48,21 +47,22 @@ namespace ICSharpCode.ILSpy.Metadata @@ -48,21 +47,22 @@ namespace ICSharpCode.ILSpy.Metadata
tabPage.Title = Text.ToString();
tabPage.SupportsLanguageSwitching = false;
var dataGrid = new DataGrid {
Columns = {
var dataGrid = Helpers.PrepareDataGrid(tabPage, this);
dataGrid.RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector("Characteristics");
dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
dataGrid.Columns.Clear();
dataGrid.AutoGenerateColumns = false;
dataGrid.Columns.AddRange(
new[] {
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,
GridLinesVisibility = DataGridGridLinesVisibility.None,
RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector(),
RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible
};
new DataGridTextColumn { IsReadOnly = true, Header = "Meaning", Binding = new Binding("Meaning") }
}
);
var headers = module.Reader.PEHeaders;
var header = headers.CoffHeader;
@ -73,54 +73,52 @@ namespace ICSharpCode.ILSpy.Metadata @@ -73,54 +73,52 @@ namespace ICSharpCode.ILSpy.Metadata
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."));
Entry characteristics;
entries.Add(characteristics = new Entry(headers.CoffHeaderStartOffset + 18, (int)header.Characteristics, 2, "Characteristics", "Flags indicating attributes of the file.", new[] {
new BitEntry(((int)header.Characteristics & 0x0001) != 0, "<0001> Relocation info stripped from file"),
new BitEntry(((int)header.Characteristics & 0x0002) != 0, "<0002> File is executable"),
new BitEntry(((int)header.Characteristics & 0x0004) != 0, "<0004> Line numbers stripped from file"),
new BitEntry(((int)header.Characteristics & 0x0008) != 0, "<0008> Local symbols stripped from file"),
new BitEntry(((int)header.Characteristics & 0x0010) != 0, "<0010> Aggressively trim working set"),
new BitEntry(((int)header.Characteristics & 0x0020) != 0, "<0020> Large address aware"),
new BitEntry(((int)header.Characteristics & 0x0040) != 0, "<0040> Reserved"),
new BitEntry(((int)header.Characteristics & 0x0080) != 0, "<0080> Bytes of machine words are reversed (Low)"),
new BitEntry(((int)header.Characteristics & 0x0100) != 0, "<0100> 32-bit word machine"),
new BitEntry(((int)header.Characteristics & 0x0200) != 0, "<0200> Debugging info stripped from file in .DBG file"),
new BitEntry(((int)header.Characteristics & 0x0400) != 0, "<0400> If image is on removable media, copy and run from the swap file"),
new BitEntry(((int)header.Characteristics & 0x0800) != 0, "<0800> If image is on Net, copy and run from the swap file"),
new BitEntry(((int)header.Characteristics & 0x1000) != 0, "<1000> System"),
new BitEntry(((int)header.Characteristics & 0x2000) != 0, "<2000> DLL"),
new BitEntry(((int)header.Characteristics & 0x4000) != 0, "<4000> File should only be run on a UP machine"),
new BitEntry(((int)header.Characteristics & 0x8000) != 0, "<8000> Bytes of machine words are reversed (High)"),
}));
dataGrid.ItemsSource = entries;
dataGrid.SetDetailsVisibilityForItem(characteristics, Visibility.Visible);
tabPage.Content = dataGrid;
return true;
}
private class CharacteristicsDataTemplateSelector : DataTemplateSelector
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
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 = "<0001> Relocation info stripped from file" },
new { Value = (flags & 0x0002) != 0, Meaning = "<0002> File is executable" },
new { Value = (flags & 0x0004) != 0, Meaning = "<0004> Line numbers stripped from file" },
new { Value = (flags & 0x0008) != 0, Meaning = "<0008> Local symbols stripped from file" },
new { Value = (flags & 0x0010) != 0, Meaning = "<0010> Aggressively trim working set" },
new { Value = (flags & 0x0020) != 0, Meaning = "<0020> Large address aware" },
new { Value = (flags & 0x0040) != 0, Meaning = "<0040> Reserved" },
new { Value = (flags & 0x0080) != 0, Meaning = "<0080> Bytes of machine words are reversed (Low)" },
new { Value = (flags & 0x0100) != 0, Meaning = "<0100> 32-bit word machine" },
new { Value = (flags & 0x0200) != 0, Meaning = "<0200> Debugging info stripped from file in .DBG file" },
new { Value = (flags & 0x0400) != 0, Meaning = "<0400> If image is on removable media, copy and run from the swap file" },
new { Value = (flags & 0x0800) != 0, Meaning = "<0800> If image is on Net, copy and run from the swap file" },
new { Value = (flags & 0x1000) != 0, Meaning = "<1000> System" },
new { Value = (flags & 0x2000) != 0, Meaning = "<2000> DLL" },
new { Value = (flags & 0x4000) != 0, Meaning = "<4000> File should only be run on a UP machine" },
new { Value = (flags & 0x8000) != 0, Meaning = "<8000> Bytes of machine words are reversed (High)" },
});
dataGridFactory.SetValue(DataGrid.GridLinesVisibilityProperty, DataGridGridLinesVisibility.None);
DataTemplate template = new DataTemplate();
template.VisualTree = dataGridFactory;
return template;
}
language.WriteCommentLine(output, "COFF Header");
}
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
public class CharacteristicsDataTemplateSelector : DataTemplateSelector
{
string detailsFieldName;
public CharacteristicsDataTemplateSelector(string detailsFieldName)
{
language.WriteCommentLine(output, "COFF Header");
this.detailsFieldName = detailsFieldName;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (((Entry)item).Member == detailsFieldName)
return (DataTemplate)MetadataTableViews.Instance["HeaderFlagsDetailsDataGrid"];
return null;
}
}
}

18
ILSpy/Metadata/Helpers.cs

@ -71,6 +71,8 @@ namespace ICSharpCode.ILSpy.Metadata @@ -71,6 +71,8 @@ namespace ICSharpCode.ILSpy.Metadata
DataGridFilter.GetFilter(view).Clear();
view.RowDetailsTemplateSelector = null;
view.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
view.EnableColumnVirtualization = true;
view.EnableRowVirtualization = true;
((MetaDataGrid)view).SelectedTreeNode = selectedNode;
if (!view.AutoGenerateColumns)
view.Columns.Clear();
@ -91,9 +93,15 @@ namespace ICSharpCode.ILSpy.Metadata @@ -91,9 +93,15 @@ namespace ICSharpCode.ILSpy.Metadata
internal static void View_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var binding = new Binding(e.PropertyName) { Mode = BindingMode.OneWay };
e.Column = new DataGridTextColumn() {
Header = e.PropertyName,
Binding = binding
e.Column = e.PropertyType.FullName switch {
"System.Boolean" => new DataGridCheckBoxColumn() {
Header = e.PropertyName,
Binding = binding
},
_ => new DataGridTextColumn() {
Header = e.PropertyName,
Binding = binding
}
};
switch (e.PropertyName)
{
@ -112,6 +120,10 @@ namespace ICSharpCode.ILSpy.Metadata @@ -112,6 +120,10 @@ namespace ICSharpCode.ILSpy.Metadata
case "RowDetails":
e.Cancel = true;
break;
case "Value" when e.PropertyDescriptor is PropertyDescriptor dp && dp.ComponentType == typeof(Entry):
binding.Path = new PropertyPath(".");
binding.Converter = ByteWidthConverter.Instance;
break;
default:
e.Cancel = e.PropertyName.Contains("Tooltip");
if (!e.Cancel)

10
ILSpy/Metadata/MetadataTableViews.xaml

@ -101,7 +101,7 @@ @@ -101,7 +101,7 @@
<DataTemplate x:Key="CustomDebugInformationDetailsDataGrid">
<DataGrid ItemsSource="{Binding RowDetails, Mode=OneWay}" GridLinesVisibility="None" CanUserAddRows="False"
CanUserDeleteRows="False" CanUserReorderColumns="False" RowHeaderWidth="0" EnableColumnVirtualization="True"
CanUserDeleteRows="False" CanUserReorderColumns="False" HeadersVisibility="Column" EnableColumnVirtualization="True"
EnableRowVirtualization="True" RowHeight="20" IsReadOnly="True" SelectionMode="Single" SelectionUnit="FullRow"
AutoGenerateColumns="True" VerticalContentAlignment="Center" AutoGeneratedColumns="DataGrid_AutoGeneratedColumns" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
CellStyle="{StaticResource DataGridCellStyle}" MaxHeight="250">
@ -117,4 +117,12 @@ @@ -117,4 +117,12 @@
MinLines="10" MaxLines="25" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="HeaderFlagsDetailsDataGrid">
<DataGrid ItemsSource="{Binding RowDetails, Mode=OneWay}" GridLinesVisibility="None" CanUserAddRows="False"
CanUserDeleteRows="False" CanUserReorderColumns="False" RowHeaderWidth="0" EnableColumnVirtualization="True"
EnableRowVirtualization="True" RowHeight="20" HeadersVisibility="None" IsReadOnly="True" SelectionMode="Single" SelectionUnit="FullRow"
VerticalContentAlignment="Center">
</DataGrid>
</DataTemplate>
</ResourceDictionary>

18
ILSpy/Metadata/MetadataTreeNode.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection.Metadata;
@ -87,13 +88,28 @@ namespace ICSharpCode.ILSpy.Metadata @@ -87,13 +88,28 @@ namespace ICSharpCode.ILSpy.Metadata
public object Value { get; }
public string Meaning { get; }
public Entry(int offset, object value, int size, string member, string meaning)
public IList<BitEntry> RowDetails { get; }
public Entry(int offset, object value, int size, string member, string meaning, IList<BitEntry> rowDetails = null)
{
this.Member = member;
this.Offset = offset;
this.Size = size;
this.Value = value;
this.Meaning = meaning;
this.RowDetails = rowDetails;
}
}
class BitEntry
{
public bool Value { get; }
public string Meaning { get; }
public BitEntry(bool value, string meaning)
{
this.Value = value;
this.Meaning = meaning;
}
}

80
ILSpy/Metadata/OptionalHeaderTreeNode.cs

@ -48,14 +48,21 @@ namespace ICSharpCode.ILSpy.Metadata @@ -48,14 +48,21 @@ namespace ICSharpCode.ILSpy.Metadata
tabPage.SupportsLanguageSwitching = false;
var dataGrid = Helpers.PrepareDataGrid(tabPage, this);
dataGrid.RowDetailsTemplateSelector = new DllCharacteristicsDataTemplateSelector();
dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Visible;
dataGrid.RowDetailsTemplateSelector = new CharacteristicsDataTemplateSelector("DLL Characteristics");
dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
dataGrid.Columns.Clear();
dataGrid.AutoGenerateColumns = false;
dataGrid.Columns.Add(new DataGridTextColumn { Header = "Member", Binding = new Binding("Member") { Mode = BindingMode.OneWay } });
dataGrid.Columns.Add(new DataGridTextColumn { Header = "Offset", Binding = new Binding("Offset") { StringFormat = "X8", Mode = BindingMode.OneWay } });
dataGrid.Columns.Add(new DataGridTextColumn { Header = "Size", Binding = new Binding("Size") { Mode = BindingMode.OneWay } });
dataGrid.Columns.Add(new DataGridTextColumn { Header = "Value", Binding = new Binding(".") { Converter = ByteWidthConverter.Instance, Mode = BindingMode.OneWay } });
dataGrid.Columns.Add(new DataGridTextColumn { Header = "Meaning", Binding = new Binding("Meaning") { Mode = BindingMode.OneWay } });
dataGrid.Columns.AddRange(
new[] {
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") }
}
);
var headers = module.Reader.PEHeaders;
var reader = module.Reader.GetEntireImage().GetReader(headers.PEHeaderStartOffset, 128);
@ -63,6 +70,8 @@ namespace ICSharpCode.ILSpy.Metadata @@ -63,6 +70,8 @@ namespace ICSharpCode.ILSpy.Metadata
var isPE32Plus = (header.Magic == PEMagic.PE32Plus);
var entries = new List<Entry>();
ushort dllCharacteristics;
Entry characteristics;
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Magic", header.Magic.ToString()));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadByte(), 1, "Major Linker Version", ""));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadByte(), 1, "Minor Linker Version", ""));
@ -86,7 +95,24 @@ namespace ICSharpCode.ILSpy.Metadata @@ -86,7 +95,24 @@ namespace ICSharpCode.ILSpy.Metadata
entries.Add(new Entry(headers.PEHeaderStartOffset + 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(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "File Checksum", ""));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "Subsystem", header.Subsystem.ToString()));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadUInt16(), 2, "DLL Characteristics", header.DllCharacteristics.ToString()));
entries.Add(characteristics = new Entry(headers.PEHeaderStartOffset + reader.Offset, dllCharacteristics = reader.ReadUInt16(), 2, "DLL Characteristics", header.DllCharacteristics.ToString(), new[] {
new BitEntry((dllCharacteristics & 0x0001) != 0, "<0001> Process Init (Reserved)"),
new BitEntry((dllCharacteristics & 0x0002) != 0, "<0002> Process Term (Reserved)"),
new BitEntry((dllCharacteristics & 0x0004) != 0, "<0004> Thread Init (Reserved)"),
new BitEntry((dllCharacteristics & 0x0008) != 0, "<0008> Thread Term (Reserved)"),
new BitEntry((dllCharacteristics & 0x0010) != 0, "<0010> Unused"),
new BitEntry((dllCharacteristics & 0x0020) != 0, "<0020> Image can handle a high entropy 64-bit virtual address space (ASLR)"),
new BitEntry((dllCharacteristics & 0x0040) != 0, "<0040> DLL can be relocated at load time"),
new BitEntry((dllCharacteristics & 0x0080) != 0, "<0080> Code integrity checks are enforced"),
new BitEntry((dllCharacteristics & 0x0100) != 0, "<0100> Image is NX compatible"),
new BitEntry((dllCharacteristics & 0x0200) != 0, "<0200> Isolation aware, but do not isolate the image"),
new BitEntry((dllCharacteristics & 0x0400) != 0, "<0400> Does not use structured exception handling (SEH)"),
new BitEntry((dllCharacteristics & 0x0800) != 0, "<0800> Do not bind the image"),
new BitEntry((dllCharacteristics & 0x1000) != 0, "<1000> Image must execute in an AppContainer"),
new BitEntry((dllCharacteristics & 0x2000) != 0, "<2000> Driver is a WDM Driver"),
new BitEntry((dllCharacteristics & 0x4000) != 0, "<4000> Image supports Control Flow Guard"),
new BitEntry((dllCharacteristics & 0x8000) != 0, "<8000> Image is Terminal Server aware"),
}));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Stack Reserve Size", ""));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Stack Commit Size", ""));
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, isPE32Plus ? reader.ReadUInt64() : reader.ReadUInt32(), isPE32Plus ? 8 : 4, "Heap Reserve Size", ""));
@ -95,48 +121,12 @@ namespace ICSharpCode.ILSpy.Metadata @@ -95,48 +121,12 @@ namespace ICSharpCode.ILSpy.Metadata
entries.Add(new Entry(headers.PEHeaderStartOffset + reader.Offset, reader.ReadInt32(), 4, "Number of Data Directories", ""));
dataGrid.ItemsSource = entries;
dataGrid.SetDetailsVisibilityForItem(characteristics, Visibility.Visible);
tabPage.Content = 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 = "<0001> Process Init (Reserved)" },
new { Value = (flags & 0x0002) != 0, Meaning = "<0002> Process Term (Reserved)" },
new { Value = (flags & 0x0004) != 0, Meaning = "<0004> Thread Init (Reserved)" },
new { Value = (flags & 0x0008) != 0, Meaning = "<0008> Thread Term (Reserved)" },
new { Value = (flags & 0x0010) != 0, Meaning = "<0010> Unused" },
new { Value = (flags & 0x0020) != 0, Meaning = "<0020> Image can handle a high entropy 64-bit virtual address space (ASLR)" },
new { Value = (flags & 0x0040) != 0, Meaning = "<0040> DLL can be relocated at load time" },
new { Value = (flags & 0x0080) != 0, Meaning = "<0080> Code integrity checks are enforced" },
new { Value = (flags & 0x0100) != 0, Meaning = "<0100> Image is NX compatible" },
new { Value = (flags & 0x0200) != 0, Meaning = "<0200> Isolation aware, but do not isolate the image" },
new { Value = (flags & 0x0400) != 0, Meaning = "<0400> Does not use structured exception handling (SEH)" },
new { Value = (flags & 0x0800) != 0, Meaning = "<0800> Do not bind the image" },
new { Value = (flags & 0x1000) != 0, Meaning = "<1000> Image must execute in an AppContainer" },
new { Value = (flags & 0x2000) != 0, Meaning = "<2000> Driver is a WDM Driver" },
new { Value = (flags & 0x4000) != 0, Meaning = "<4000> Image supports Control Flow Guard" },
new { Value = (flags & 0x8000) != 0, Meaning = "<8000> Image is Terminal Server aware" },
});
dataGridFactory.SetValue(DataGrid.GridLinesVisibilityProperty, DataGridGridLinesVisibility.None);
DataTemplate template = new DataTemplate();
template.VisualTree = dataGridFactory;
return template;
}
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, "Optional Header");

Loading…
Cancel
Save