.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

351 lines
12 KiB

// 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.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView;
using Microsoft.Win32;
using Mono.Cecil.Rocks;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// The main window of the application.
/// </summary>
partial class MainWindow : Window
{
ILSpySettings spySettings;
SessionSettings sessionSettings;
AssemblyListManager assemblyListManager;
AssemblyList assemblyList;
AssemblyListTreeNode assemblyListTreeNode;
public MainWindow()
{
spySettings = ILSpySettings.Load();
this.sessionSettings = new SessionSettings(spySettings);
this.assemblyListManager = new AssemblyListManager(spySettings);
this.DataContext = sessionSettings;
this.Left = sessionSettings.WindowBounds.Left;
this.Top = sessionSettings.WindowBounds.Top;
this.Width = sessionSettings.WindowBounds.Width;
this.Height = sessionSettings.WindowBounds.Height;
// TODO: validate bounds (maybe a screen was removed...)
this.WindowState = sessionSettings.WindowState;
InitializeComponent();
decompilerTextView.mainWindow = this;
sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged;
#if DEBUG
AddDebugItemsToToolbar();
#endif
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// Load AssemblyList only in Loaded event so that WPF is initialized before we start the CPU-heavy stuff.
// This makes the UI come up a bit faster.
this.assemblyList = assemblyListManager.LoadList(this.spySettings, sessionSettings.ActiveAssemblyList);
this.spySettings = null;
ShowAssemblyList(this.assemblyList);
string[] args = Environment.GetCommandLineArgs();
for (int i = 1; i < args.Length; i++) {
assemblyList.OpenAssembly(args[i]);
}
if (assemblyList.Assemblies.Count == 0)
LoadInitialAssemblies();
SelectNode(FindNodeByPath(sessionSettings.ActiveTreeViewPath, true));
}
void ShowAssemblyList(AssemblyList assemblyList)
{
this.assemblyList = assemblyList;
assemblyListTreeNode = new AssemblyListTreeNode(assemblyList);
assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone();
assemblyListTreeNode.Select = SelectNode;
treeView.Root = assemblyListTreeNode;
if (assemblyList.ListName == AssemblyListManager.DefaultListName)
this.Title = "ILSpy";
else
this.Title = "ILSpy - " + assemblyList.ListName;
}
void LoadInitialAssemblies()
{
// Called when loading an empty assembly list; so that
// the user can see something initially.
System.Reflection.Assembly[] initialAssemblies = {
typeof(object).Assembly,
typeof(Uri).Assembly,
typeof(System.Linq.Enumerable).Assembly,
typeof(System.Xml.XmlDocument).Assembly,
typeof(System.Windows.Markup.MarkupExtension).Assembly,
typeof(System.Windows.Rect).Assembly,
typeof(System.Windows.UIElement).Assembly,
typeof(System.Windows.FrameworkElement).Assembly,
typeof(ICSharpCode.TreeView.SharpTreeView).Assembly,
typeof(Mono.Cecil.AssemblyDefinition).Assembly,
typeof(ICSharpCode.AvalonEdit.TextEditor).Assembly,
typeof(ICSharpCode.Decompiler.GraphVizGraph).Assembly,
typeof(MainWindow).Assembly
};
foreach (System.Reflection.Assembly asm in initialAssemblies)
assemblyList.OpenAssembly(asm.Location);
}
void filterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// filterSettings is mutable; but the ILSpyTreeNode filtering assumes that filter settings are immutable.
// Thus, the main window will use one mutable instance (for data-binding), and assign a new clone to the ILSpyTreeNodes whenever the main
// mutable instance changes.
if (assemblyListTreeNode != null)
assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone();
if (e.PropertyName == "Language") {
TreeView_SelectionChanged(null, null);
}
}
internal AssemblyList AssemblyList {
get { return assemblyList; }
}
internal void SelectNode(SharpTreeNode obj)
{
if (obj != null) {
// Set both the selection and focus to ensure that keyboard navigation works as expected.
treeView.FocusNode(obj);
treeView.SelectedItem = obj;
}
}
/// <summary>
/// Retrieves a node using the .ToString() representations of its ancestors.
/// </summary>
SharpTreeNode FindNodeByPath(string[] path, bool returnBestMatch)
{
if (path == null)
return null;
SharpTreeNode node = treeView.Root;
SharpTreeNode bestMatch = node;
foreach (var element in path) {
if (node == null)
break;
bestMatch = node;
node.EnsureLazyChildren();
node = node.Children.FirstOrDefault(c => c.ToString() == element);
}
if (returnBestMatch)
return node ?? bestMatch;
else
return node;
}
/// <summary>
/// Gets the .ToString() representation of the node's ancestors.
/// </summary>
string[] GetPathForNode(SharpTreeNode node)
{
if (node == null)
return null;
List<string> path = new List<string>();
while (node.Parent != null) {
path.Add(node.ToString());
node = node.Parent;
}
path.Reverse();
return path.ToArray();
}
#region Debugging CFG
#if DEBUG
void AddDebugItemsToToolbar()
{
toolBar.Items.Add(new Separator());
Button cfg = new Button() { Content = "CFG" };
cfg.Click += new RoutedEventHandler(cfg_Click);
toolBar.Items.Add(cfg);
Button ssa = new Button() { Content = "SSA" };
ssa.Click += new RoutedEventHandler(ssa_Click);
toolBar.Items.Add(ssa);
Button varGraph = new Button() { Content = "Var" };
varGraph.Click += new RoutedEventHandler(varGraph_Click);
toolBar.Items.Add(varGraph);
}
void cfg_Click(object sender, RoutedEventArgs e)
{
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
if (node != null && node.MethodDefinition.HasBody) {
var cfg = ControlFlowGraphBuilder.Build(node.MethodDefinition.Body);
cfg.ComputeDominance();
cfg.ComputeDominanceFrontier();
ShowGraph(node.MethodDefinition.Name + "-cfg", cfg.ExportGraph());
}
}
void ssa_Click(object sender, RoutedEventArgs e)
{
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
if (node != null && node.MethodDefinition.HasBody) {
node.MethodDefinition.Body.SimplifyMacros();
ShowGraph(node.MethodDefinition.Name + "-ssa", SsaFormBuilder.Build(node.MethodDefinition).ExportBlockGraph());
}
}
void varGraph_Click(object sender, RoutedEventArgs e)
{
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
if (node != null && node.MethodDefinition.HasBody) {
node.MethodDefinition.Body.SimplifyMacros();
ShowGraph(node.MethodDefinition.Name + "-var", SsaFormBuilder.Build(node.MethodDefinition).ExportVariableGraph());
}
}
void ShowGraph(string name, GraphVizGraph graph)
{
foreach (char c in Path.GetInvalidFileNameChars())
name = name.Replace(c, '-');
string fileName = Path.Combine(Path.GetTempPath(), name);
graph.Save(fileName + ".gv");
Process.Start("dot", "\"" + fileName + ".gv\" -Tpng -o \"" + fileName + ".png\"").WaitForExit();
Process.Start(fileName + ".png");
}
#endif
#endregion
void OpenCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = ".NET assemblies|*.dll;*.exe|All files|*.*";
dlg.Multiselect = true;
dlg.RestoreDirectory = true;
if (dlg.ShowDialog() == true) {
OpenFiles(dlg.FileNames);
}
}
void OpenFiles(string[] fileNames)
{
treeView.UnselectAll();
SharpTreeNode lastNode = null;
foreach (string file in fileNames) {
var asm = assemblyList.OpenAssembly(file);
if (asm != null) {
treeView.SelectedItems.Add(asm);
lastNode = asm;
}
}
if (lastNode != null)
treeView.ScrollIntoView(lastNode);
}
void RefreshCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
var path = GetPathForNode(treeView.SelectedItem as SharpTreeNode);
ShowAssemblyList(assemblyListManager.LoadList(ILSpySettings.Load(), assemblyList.ListName));
SelectNode(FindNodeByPath(path, true));
}
void ExitClick(object sender, RoutedEventArgs e)
{
Close();
}
void AboutClick(object sender, RoutedEventArgs e)
{
AboutDialog dlg = new AboutDialog();
dlg.Owner = this;
dlg.ShowDialog();
}
void OpenFromGac_Click(object sender, RoutedEventArgs e)
{
OpenFromGacDialog dlg = new OpenFromGacDialog();
dlg.Owner = this;
if (dlg.ShowDialog() == true) {
OpenFiles(dlg.SelectedFileNames);
}
}
void TreeView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (treeView.SelectedItems.Count == 1) {
ILSpyTreeNodeBase node = treeView.SelectedItem as ILSpyTreeNodeBase;
if (node != null && node.View(decompilerTextView))
return;
}
decompilerTextView.Decompile(sessionSettings.FilterSettings.Language,
treeView.GetTopLevelSelection().OfType<ILSpyTreeNodeBase>(),
new DecompilationOptions());
}
void saveCode_Click(object sender, RoutedEventArgs e)
{
if (treeView.SelectedItems.Count == 1) {
ILSpyTreeNodeBase node = treeView.SelectedItem as ILSpyTreeNodeBase;
if (node != null && node.Save())
return;
}
decompilerTextView.SaveToDisk(sessionSettings.FilterSettings.Language,
treeView.GetTopLevelSelection().OfType<ILSpyTreeNodeBase>(),
new DecompilationOptions());
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
// store window state in settings only if it's not minimized
if (this.WindowState != System.Windows.WindowState.Minimized)
sessionSettings.WindowState = this.WindowState;
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
sessionSettings.ActiveAssemblyList = assemblyList.ListName;
sessionSettings.ActiveTreeViewPath = GetPathForNode(treeView.SelectedItem as SharpTreeNode);
sessionSettings.WindowBounds = this.RestoreBounds;
sessionSettings.Save();
}
}
}