PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs

https://github.com/josephcooney/ILSpy
C# | 409 lines | 349 code | 46 blank | 14 comment | 52 complexity | d58c0791e6a1dc94ffc05e8242f51f0e MD5 | raw file
  1. // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
  2. // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
  3. using System;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Input;
  10. using System.Windows.Interop;
  11. using System.Windows.Media;
  12. using System.Xml.Linq;
  13. using ICSharpCode.AvalonEdit.Document;
  14. using ICSharpCode.ILSpy.Bookmarks;
  15. using ICSharpCode.ILSpy.Debugger;
  16. using ICSharpCode.ILSpy.Debugger.Bookmarks;
  17. using ICSharpCode.ILSpy.Debugger.Services;
  18. using ICSharpCode.ILSpy.Debugger.UI;
  19. using ICSharpCode.ILSpy.TreeNodes;
  20. using ICSharpCode.TreeView;
  21. using Microsoft.Win32;
  22. using Mono.Cecil;
  23. namespace ICSharpCode.ILSpy.Debugger.Commands
  24. {
  25. public abstract class DebuggerCommand : SimpleCommand
  26. {
  27. public DebuggerCommand()
  28. {
  29. MainWindow.Instance.KeyUp += OnKeyUp;
  30. }
  31. void OnKeyUp(object sender, KeyEventArgs e)
  32. {
  33. switch (e.Key) {
  34. case Key.F5:
  35. if (this is ContinueDebuggingCommand) {
  36. ((ContinueDebuggingCommand)this).Execute(null);
  37. e.Handled = true;
  38. }
  39. break;
  40. case Key.System:
  41. if (this is StepOverCommand) {
  42. ((StepOverCommand)this).Execute(null);
  43. e.Handled = true;
  44. }
  45. break;
  46. case Key.F11:
  47. if (this is StepIntoCommand) {
  48. ((StepIntoCommand)this).Execute(null);
  49. e.Handled = true;
  50. }
  51. break;
  52. default:
  53. // do nothing
  54. break;
  55. }
  56. }
  57. #region Static members
  58. [System.Runtime.InteropServices.DllImport("user32.dll")]
  59. static extern bool SetWindowPos(
  60. IntPtr hWnd,
  61. IntPtr hWndInsertAfter,
  62. int X,
  63. int Y,
  64. int cx,
  65. int cy,
  66. uint uFlags);
  67. const UInt32 SWP_NOSIZE = 0x0001;
  68. const UInt32 SWP_NOMOVE = 0x0002;
  69. static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
  70. static readonly IntPtr HWND_TOP = new IntPtr(0);
  71. static void SendWpfWindowPos(Window window, IntPtr place)
  72. {
  73. var hWnd = new WindowInteropHelper(window).Handle;
  74. SetWindowPos(hWnd, place, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
  75. }
  76. #endregion
  77. public override void Execute(object parameter)
  78. {
  79. }
  80. protected static IDebugger CurrentDebugger {
  81. get {
  82. return DebuggerService.CurrentDebugger;
  83. }
  84. }
  85. protected void StartExecutable(string fileName, string workingDirectory, string arguments)
  86. {
  87. CurrentDebugger.Start(new ProcessStartInfo {
  88. FileName = fileName,
  89. WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(fileName),
  90. Arguments = arguments
  91. });
  92. Finish();
  93. }
  94. protected void StartAttaching(Process process)
  95. {
  96. CurrentDebugger.Attach(process);
  97. Finish();
  98. }
  99. protected void Finish()
  100. {
  101. EnableDebuggerUI(false);
  102. CurrentDebugger.DebugStopped += OnDebugStopped;
  103. CurrentDebugger.IsProcessRunningChanged += CurrentDebugger_IsProcessRunningChanged;
  104. DebugInformation.IsDebuggerLoaded = true;
  105. MainWindow.Instance.SetStatus("Running...", Brushes.Black);
  106. }
  107. protected void OnDebugStopped(object sender, EventArgs e)
  108. {
  109. EnableDebuggerUI(true);
  110. CurrentDebugger.DebugStopped -= OnDebugStopped;
  111. CurrentDebugger.IsProcessRunningChanged -= CurrentDebugger_IsProcessRunningChanged;
  112. DebugInformation.IsDebuggerLoaded = false;
  113. MainWindow.Instance.SetStatus("Stand by...", Brushes.Black);
  114. }
  115. protected void EnableDebuggerUI(bool enable)
  116. {
  117. var menuItems = MainWindow.Instance.GetMainMenuItems();
  118. var toolbarItems = MainWindow.Instance.GetToolBarItems();
  119. // menu
  120. if (menuItems != null)
  121. {
  122. var items = menuItems.OfType<MenuItem>().Where(m => (m.Header as string) == "_Debugger");
  123. foreach (var item in items.First().Items.OfType<MenuItem>())
  124. {
  125. string header = (string)item.Header;
  126. if (header.StartsWith("Remove") || header.StartsWith("Show")) continue;
  127. if (header.StartsWith("Attach") || header.StartsWith("Debug"))
  128. item.IsEnabled = enable;
  129. else
  130. item.IsEnabled = !enable;
  131. }
  132. }
  133. //toolbar
  134. var buttons = toolbarItems.OfType<Button>().Where(b => (b.Tag as string) == "Debugger");
  135. foreach (var item in buttons) {
  136. item.IsEnabled = enable;
  137. }
  138. // internal types
  139. MainWindow.Instance.SessionSettings.FilterSettings.ShowInternalApi = true;
  140. }
  141. void CurrentDebugger_IsProcessRunningChanged(object sender, EventArgs e)
  142. {
  143. if (CurrentDebugger.IsProcessRunning) {
  144. //SendWpfWindowPos(this, HWND_BOTTOM);
  145. MainWindow.Instance.SetStatus("Running...", Brushes.Black);
  146. return;
  147. }
  148. var inst = MainWindow.Instance;
  149. // breakpoint was hit => bring to front the main window
  150. SendWpfWindowPos(inst, HWND_TOP); inst.Activate();
  151. // jump to type & expand folding
  152. if (DebugInformation.DebugStepInformation != null)
  153. inst.JumpToReference(DebugInformation.DebugStepInformation.Item3);
  154. inst.SetStatus("Debugging...", Brushes.Red);
  155. }
  156. }
  157. [ExportContextMenuEntry(Header = "_Debug Assembly", Icon = "Images/debug.png")]
  158. internal sealed class DebugExecutableNodeCommand : DebuggerCommand, IContextMenuEntry
  159. {
  160. public bool IsVisible(SharpTreeNode[] selectedNodes)
  161. {
  162. return selectedNodes.All(
  163. delegate (SharpTreeNode n) {
  164. AssemblyTreeNode a = n as AssemblyTreeNode;
  165. if (a == null)
  166. return false;
  167. AssemblyDefinition asm = a.LoadedAssembly.AssemblyDefinition;
  168. return asm != null && asm.EntryPoint != null;
  169. });
  170. }
  171. public bool IsEnabled(SharpTreeNode[] selectedNodes)
  172. {
  173. return selectedNodes.Length == 1;
  174. }
  175. public void Execute(SharpTreeNode[] selectedNodes)
  176. {
  177. if (!CurrentDebugger.IsDebugging) {
  178. AssemblyTreeNode n = selectedNodes[0] as AssemblyTreeNode;
  179. var settings = ILSpySettings.Load();
  180. XElement e = settings["DebuggerSettings"];
  181. var askForArguments = (bool?)e.Attribute("askForArguments");
  182. if (askForArguments.HasValue && askForArguments.Value) {
  183. var window = new ExecuteProcessWindow { Owner = MainWindow.Instance,
  184. SelectedExecutable = n.LoadedAssembly.FileName };
  185. if (window.ShowDialog() == true) {
  186. string fileName = window.SelectedExecutable;
  187. // execute the process
  188. this.StartExecutable(fileName, window.WorkingDirectory, window.Arguments);
  189. }
  190. } else {
  191. this.StartExecutable(n.LoadedAssembly.FileName, null, null);
  192. }
  193. }
  194. }
  195. }
  196. [ExportToolbarCommand(ToolTip = "Debug an executable",
  197. ToolbarIcon = "Images/debug.png",
  198. ToolbarCategory = "Debugger",
  199. Tag = "Debugger",
  200. ToolbarOrder = 0)]
  201. [ExportMainMenuCommand(Menu = "_Debugger",
  202. MenuIcon = "Images/debug.png",
  203. MenuCategory = "Start",
  204. Header = "Debug _executable",
  205. MenuOrder = 0)]
  206. internal sealed class DebugExecutableCommand : DebuggerCommand
  207. {
  208. public override void Execute(object parameter)
  209. {
  210. if (!CurrentDebugger.IsDebugging) {
  211. var settings = ILSpySettings.Load();
  212. XElement e = settings["DebuggerSettings"];
  213. var askForArguments = (bool?)e.Attribute("askForArguments");
  214. if (askForArguments.HasValue && askForArguments.Value) {
  215. var window = new ExecuteProcessWindow { Owner = MainWindow.Instance };
  216. if (window.ShowDialog() == true) {
  217. string fileName = window.SelectedExecutable;
  218. // add it to references
  219. MainWindow.Instance.OpenFiles(new [] { fileName }, false);
  220. // execute the process
  221. this.StartExecutable(fileName, window.WorkingDirectory, window.Arguments);
  222. }
  223. } else {
  224. OpenFileDialog dialog = new OpenFileDialog() {
  225. Filter = ".NET Executable (*.exe) | *.exe",
  226. RestoreDirectory = true,
  227. DefaultExt = "exe"
  228. };
  229. if (dialog.ShowDialog() == true) {
  230. string fileName = dialog.FileName;
  231. // add it to references
  232. MainWindow.Instance.OpenFiles(new [] { fileName }, false);
  233. // execute the process
  234. this.StartExecutable(fileName, null, null);
  235. }
  236. }
  237. }
  238. }
  239. }
  240. [ExportMainMenuCommand(Menu = "_Debugger",
  241. MenuCategory = "Start",
  242. Header = "_Attach",
  243. MenuOrder = 1)]
  244. internal sealed class AttachCommand : DebuggerCommand
  245. {
  246. public override void Execute(object parameter)
  247. {
  248. if (!CurrentDebugger.IsDebugging) {
  249. var settings = ILSpySettings.Load();
  250. XElement e = settings["DebuggerSettings"];
  251. var showWarnings = (bool?)e.Attribute("showWarnings");
  252. if ((showWarnings.HasValue && showWarnings.Value) || !showWarnings.HasValue)
  253. MessageBox.Show("Warning: When attaching to an application, some local variables might not be available. If possible, use the \"Start Executable\" command.",
  254. "Attach to a process", MessageBoxButton.OK, MessageBoxImage.Warning);
  255. var window = new AttachToProcessWindow { Owner = MainWindow.Instance };
  256. if (window.ShowDialog() == true) {
  257. StartAttaching(window.SelectedProcess);
  258. }
  259. }
  260. }
  261. }
  262. [ExportMainMenuCommand(Menu = "_Debugger",
  263. MenuIcon = "Images/ContinueDebugging.png",
  264. MenuCategory = "SteppingArea",
  265. Header = "Continue debugging",
  266. InputGestureText = "F5",
  267. IsEnabled = false,
  268. MenuOrder = 2)]
  269. internal sealed class ContinueDebuggingCommand : DebuggerCommand
  270. {
  271. public override void Execute(object parameter)
  272. {
  273. if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) {
  274. CurrentLineBookmark.Remove();
  275. CurrentDebugger.Continue();
  276. MainWindow.Instance.SetStatus("Running...", Brushes.Black);
  277. }
  278. }
  279. }
  280. [ExportMainMenuCommand(Menu = "_Debugger",
  281. MenuIcon = "Images/StepInto.png",
  282. MenuCategory = "SteppingArea",
  283. Header = "Step into",
  284. InputGestureText = "F11",
  285. IsEnabled = false,
  286. MenuOrder = 3)]
  287. internal sealed class StepIntoCommand : DebuggerCommand
  288. {
  289. public override void Execute(object parameter)
  290. {
  291. if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) {
  292. base.Execute(null);
  293. CurrentDebugger.StepInto();
  294. }
  295. }
  296. }
  297. [ExportMainMenuCommand(Menu = "_Debugger",
  298. MenuIcon = "Images/StepOver.png",
  299. MenuCategory = "SteppingArea",
  300. Header = "Step over",
  301. InputGestureText = "F10",
  302. IsEnabled = false,
  303. MenuOrder = 4)]
  304. internal sealed class StepOverCommand : DebuggerCommand
  305. {
  306. public override void Execute(object parameter)
  307. {
  308. if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) {
  309. base.Execute(null);
  310. CurrentDebugger.StepOver();
  311. }
  312. }
  313. }
  314. [ExportMainMenuCommand(Menu = "_Debugger",
  315. MenuIcon = "Images/StepOut.png",
  316. MenuCategory = "SteppingArea",
  317. Header = "Step out",
  318. IsEnabled = false,
  319. MenuOrder = 5)]
  320. internal sealed class StepOutCommand : DebuggerCommand
  321. {
  322. public override void Execute(object parameter)
  323. {
  324. if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) {
  325. base.Execute(null);
  326. CurrentDebugger.StepOut();
  327. }
  328. }
  329. }
  330. [ExportMainMenuCommand(Menu = "_Debugger",
  331. MenuCategory = "SteppingArea",
  332. Header = "_Detach",
  333. IsEnabled = false,
  334. MenuOrder = 6)]
  335. internal sealed class DetachCommand : DebuggerCommand
  336. {
  337. public override void Execute(object parameter)
  338. {
  339. if (CurrentDebugger.IsDebugging){
  340. CurrentDebugger.Detach();
  341. EnableDebuggerUI(true);
  342. CurrentDebugger.DebugStopped -= OnDebugStopped;
  343. }
  344. }
  345. }
  346. [ExportMainMenuCommand(Menu = "_Debugger",
  347. MenuIcon = "Images/DeleteAllBreakpoints.png",
  348. MenuCategory = "Others",
  349. Header = "Remove _breakpoints",
  350. MenuOrder = 7)]
  351. internal sealed class RemoveBreakpointsCommand : DebuggerCommand
  352. {
  353. public override void Execute(object parameter)
  354. {
  355. for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) {
  356. var bookmark = BookmarkManager.Bookmarks[i];
  357. if (bookmark is BreakpointBookmark) {
  358. BookmarkManager.RemoveMark(bookmark);
  359. }
  360. }
  361. }
  362. }
  363. }