mirror of https://github.com/icsharpcode/ILSpy.git
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.
395 lines
12 KiB
395 lines
12 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
using System; |
|
using System.Diagnostics; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Windows; |
|
using System.Windows.Controls; |
|
using System.Windows.Input; |
|
using System.Windows.Interop; |
|
using System.Windows.Media; |
|
using System.Xml.Linq; |
|
|
|
using ICSharpCode.AvalonEdit.Document; |
|
using ICSharpCode.ILSpy.Bookmarks; |
|
using ICSharpCode.ILSpy.Debugger; |
|
using ICSharpCode.ILSpy.Debugger.Bookmarks; |
|
using ICSharpCode.ILSpy.Debugger.Services; |
|
using ICSharpCode.ILSpy.Debugger.UI; |
|
using ICSharpCode.ILSpy.TreeNodes; |
|
using ICSharpCode.TreeView; |
|
using Microsoft.Win32; |
|
|
|
namespace ICSharpCode.ILSpy.Debugger.Commands |
|
{ |
|
public abstract class DebuggerCommand : SimpleCommand |
|
{ |
|
public DebuggerCommand() |
|
{ |
|
MainWindow.Instance.KeyUp += OnKeyUp; |
|
} |
|
|
|
void OnKeyUp(object sender, KeyEventArgs e) |
|
{ |
|
switch (e.Key) { |
|
case Key.F5: |
|
if (this is ContinueDebuggingCommand) { |
|
((ContinueDebuggingCommand)this).Execute(null); |
|
e.Handled = true; |
|
} |
|
break; |
|
case Key.System: |
|
if (this is StepOverCommand) { |
|
((StepOverCommand)this).Execute(null); |
|
e.Handled = true; |
|
} |
|
break; |
|
case Key.F11: |
|
if (this is StepIntoCommand) { |
|
((StepIntoCommand)this).Execute(null); |
|
e.Handled = true; |
|
} |
|
break; |
|
default: |
|
// do nothing |
|
break; |
|
} |
|
} |
|
|
|
#region Static members |
|
[System.Runtime.InteropServices.DllImport("user32.dll")] |
|
static extern bool SetWindowPos( |
|
IntPtr hWnd, |
|
IntPtr hWndInsertAfter, |
|
int X, |
|
int Y, |
|
int cx, |
|
int cy, |
|
uint uFlags); |
|
|
|
const UInt32 SWP_NOSIZE = 0x0001; |
|
const UInt32 SWP_NOMOVE = 0x0002; |
|
|
|
static readonly IntPtr HWND_BOTTOM = new IntPtr(1); |
|
static readonly IntPtr HWND_TOP = new IntPtr(0); |
|
|
|
static void SendWpfWindowPos(Window window, IntPtr place) |
|
{ |
|
var hWnd = new WindowInteropHelper(window).Handle; |
|
SetWindowPos(hWnd, place, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); |
|
} |
|
#endregion |
|
|
|
public override void Execute(object parameter) |
|
{ |
|
DebugData.LoadedAssemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies().Select(a => a.AssemblyDefinition); |
|
} |
|
|
|
protected static IDebugger CurrentDebugger { |
|
get { |
|
return DebuggerService.CurrentDebugger; |
|
} |
|
} |
|
|
|
protected void StartExecutable(string fileName, string workingDirectory, string arguments) |
|
{ |
|
CurrentDebugger.Start(new ProcessStartInfo { |
|
FileName = fileName, |
|
WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(fileName), |
|
Arguments = arguments |
|
}); |
|
Finish(); |
|
} |
|
|
|
protected void StartAttaching(Process process) |
|
{ |
|
CurrentDebugger.Attach(process); |
|
Finish(); |
|
} |
|
|
|
protected void Finish() |
|
{ |
|
EnableDebuggerUI(false); |
|
CurrentDebugger.DebugStopped += OnDebugStopped; |
|
CurrentDebugger.IsProcessRunningChanged += CurrentDebugger_IsProcessRunningChanged; |
|
|
|
MainWindow.Instance.SetStatus("Running...", Brushes.Black); |
|
} |
|
|
|
protected void OnDebugStopped(object sender, EventArgs e) |
|
{ |
|
EnableDebuggerUI(true); |
|
CurrentDebugger.DebugStopped -= OnDebugStopped; |
|
CurrentDebugger.IsProcessRunningChanged -= CurrentDebugger_IsProcessRunningChanged; |
|
|
|
MainWindow.Instance.SetStatus("Stand by...", Brushes.Black); |
|
} |
|
|
|
protected void EnableDebuggerUI(bool enable) |
|
{ |
|
var menuItems = MainWindow.Instance.GetMainMenuItems(); |
|
var toolbarItems = MainWindow.Instance.GetToolBarItems(); |
|
|
|
// menu |
|
var items = menuItems.OfType<MenuItem>().Where(m => (m.Header as string) == "_Debugger"); |
|
foreach (var item in items.First().Items.OfType<MenuItem>()) { |
|
string header = (string)item.Header; |
|
|
|
if (header.StartsWith("Remove")) continue; |
|
|
|
if (header.StartsWith("Attach") || header.StartsWith("Debug")) |
|
item.IsEnabled = enable; |
|
else |
|
item.IsEnabled = !enable; |
|
} |
|
|
|
//toolbar |
|
var buttons = toolbarItems.OfType<Button>().Where(b => (b.Tag as string) == "Debugger"); |
|
foreach (var item in buttons) { |
|
item.IsEnabled = enable; |
|
} |
|
|
|
// internal types |
|
MainWindow.Instance.SessionSettings.FilterSettings.ShowInternalApi = true; |
|
} |
|
|
|
void CurrentDebugger_IsProcessRunningChanged(object sender, EventArgs e) |
|
{ |
|
if (CurrentDebugger.IsProcessRunning) { |
|
//SendWpfWindowPos(this, HWND_BOTTOM); |
|
MainWindow.Instance.SetStatus("Running...", Brushes.Black); |
|
return; |
|
} |
|
|
|
var inst = MainWindow.Instance; |
|
|
|
// breakpoint was hit => bring to front the main window |
|
SendWpfWindowPos(inst, HWND_TOP); inst.Activate(); |
|
|
|
// jump to type & expand folding |
|
if (DebugData.DebugStepInformation != null) |
|
inst.JumpToReference(DebugData.DebugStepInformation.Item3); |
|
|
|
inst.SetStatus("Debugging...", Brushes.Red); |
|
} |
|
} |
|
|
|
[ExportContextMenuEntry(Header = "_Debug Assembly", Icon = "Images/application-x-executable.png")] |
|
internal sealed class DebugExecutableNodeCommand : DebuggerCommand, IContextMenuEntry |
|
{ |
|
public bool IsVisible(SharpTreeNode[] selectedNodes) |
|
{ |
|
return selectedNodes.All(n => n is AssemblyTreeNode && null != (n as AssemblyTreeNode).LoadedAssembly.AssemblyDefinition.EntryPoint); |
|
} |
|
|
|
public bool IsEnabled(SharpTreeNode[] selectedNodes) |
|
{ |
|
return selectedNodes.Length == 1; |
|
} |
|
|
|
public void Execute(SharpTreeNode[] selectedNodes) |
|
{ |
|
if (!CurrentDebugger.IsDebugging) { |
|
AssemblyTreeNode n = selectedNodes[0] as AssemblyTreeNode; |
|
|
|
var settings = ILSpySettings.Load(); |
|
XElement e = settings["DebuggerSettings"]; |
|
var askForArguments = (bool?)e.Attribute("askForArguments"); |
|
if (askForArguments.HasValue && askForArguments.Value) { |
|
var window = new ExecuteProcessWindow { Owner = MainWindow.Instance, |
|
SelectedExecutable = n.LoadedAssembly.FileName }; |
|
if (window.ShowDialog() == true) { |
|
string fileName = window.SelectedExecutable; |
|
|
|
// execute the process |
|
this.StartExecutable(fileName, window.WorkingDirectory, window.Arguments); |
|
} |
|
} else { |
|
this.StartExecutable(n.LoadedAssembly.FileName, null, null); |
|
} |
|
} |
|
} |
|
} |
|
|
|
[ExportToolbarCommand(ToolTip = "Debug an executable", |
|
ToolbarIcon = "Images/application-x-executable.png", |
|
ToolbarCategory = "Debugger", |
|
Tag = "Debugger", |
|
ToolbarOrder = 0)] |
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuIcon = "Images/application-x-executable.png", |
|
MenuCategory = "Start", |
|
Header = "Debug an _executable", |
|
MenuOrder = 0)] |
|
internal sealed class DebugExecutableCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (!CurrentDebugger.IsDebugging) { |
|
var settings = ILSpySettings.Load(); |
|
XElement e = settings["DebuggerSettings"]; |
|
var askForArguments = (bool?)e.Attribute("askForArguments"); |
|
if (askForArguments.HasValue && askForArguments.Value) { |
|
var window = new ExecuteProcessWindow { Owner = MainWindow.Instance }; |
|
if (window.ShowDialog() == true) { |
|
string fileName = window.SelectedExecutable; |
|
|
|
// add it to references |
|
MainWindow.Instance.OpenFiles(new [] { fileName }, false); |
|
|
|
// execute the process |
|
this.StartExecutable(fileName, window.WorkingDirectory, window.Arguments); |
|
} |
|
} else { |
|
OpenFileDialog dialog = new OpenFileDialog() { |
|
Filter = ".NET Executable (*.exe) | *.exe", |
|
RestoreDirectory = true, |
|
DefaultExt = "exe" |
|
}; |
|
if (dialog.ShowDialog() == true) { |
|
string fileName = dialog.FileName; |
|
|
|
// add it to references |
|
MainWindow.Instance.OpenFiles(new [] { fileName }, false); |
|
|
|
// execute the process |
|
this.StartExecutable(fileName, null, null); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuCategory = "Start", |
|
Header = "Attach to _running application", |
|
MenuOrder = 1)] |
|
internal sealed class AttachCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (!CurrentDebugger.IsDebugging) { |
|
|
|
var settings = ILSpySettings.Load(); |
|
XElement e = settings["DebuggerSettings"]; |
|
var showWarnings = (bool?)e.Attribute("showWarnings"); |
|
if ((showWarnings.HasValue && showWarnings.Value) || !showWarnings.HasValue) |
|
MessageBox.Show("Warning: When attaching to an application, some local variables might not be available. If possible, use the \"Start Executable\" command.", |
|
"Attach to a process", MessageBoxButton.OK, MessageBoxImage.Warning); |
|
|
|
var window = new AttachToProcessWindow { Owner = MainWindow.Instance }; |
|
if (window.ShowDialog() == true) { |
|
StartAttaching(window.SelectedProcess); |
|
} |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuIcon = "Images/ContinueDebugging.png", |
|
MenuCategory = "SteppingArea", |
|
Header = "Continue debugging", |
|
InputGestureText = "F5", |
|
IsEnabled = false, |
|
MenuOrder = 2)] |
|
internal sealed class ContinueDebuggingCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) { |
|
CurrentDebugger.Continue(); |
|
MainWindow.Instance.SetStatus("Running...", Brushes.Black); |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuIcon = "Images/StepInto.png", |
|
MenuCategory = "SteppingArea", |
|
Header = "Step into", |
|
InputGestureText = "F11", |
|
IsEnabled = false, |
|
MenuOrder = 3)] |
|
internal sealed class StepIntoCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) { |
|
base.Execute(null); |
|
CurrentDebugger.StepInto(); |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuIcon = "Images/StepOver.png", |
|
MenuCategory = "SteppingArea", |
|
Header = "Step over", |
|
InputGestureText = "F10", |
|
IsEnabled = false, |
|
MenuOrder = 4)] |
|
internal sealed class StepOverCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) { |
|
base.Execute(null); |
|
CurrentDebugger.StepOver(); |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuIcon = "Images/StepOut.png", |
|
MenuCategory = "SteppingArea", |
|
Header = "Step out", |
|
IsEnabled = false, |
|
MenuOrder = 5)] |
|
internal sealed class StepOutCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) { |
|
base.Execute(null); |
|
CurrentDebugger.StepOut(); |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuCategory = "SteppingArea", |
|
Header = "_Detach from running application", |
|
IsEnabled = false, |
|
MenuOrder = 6)] |
|
internal sealed class DetachCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
if (CurrentDebugger.IsDebugging){ |
|
CurrentDebugger.Detach(); |
|
|
|
EnableDebuggerUI(true); |
|
CurrentDebugger.DebugStopped -= OnDebugStopped; |
|
} |
|
} |
|
} |
|
|
|
[ExportMainMenuCommand(Menu = "_Debugger", |
|
MenuIcon = "Images/DeleteAllBreakpoints.png", |
|
MenuCategory = "Others", |
|
Header = "Remove all _breakpoints", |
|
MenuOrder = 7)] |
|
internal sealed class RemoveBreakpointsCommand : DebuggerCommand |
|
{ |
|
public override void Execute(object parameter) |
|
{ |
|
for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) { |
|
var bookmark = BookmarkManager.Bookmarks[i]; |
|
if (bookmark is BreakpointBookmark) { |
|
BookmarkManager.RemoveMark(bookmark); |
|
} |
|
} |
|
} |
|
} |
|
}
|
|
|