diff --git a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs
index 4df9182b9..b9118ad9a 100644
--- a/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs
+++ b/Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs
@@ -26,7 +26,6 @@ using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ILSpy.Debugger.Bookmarks;
-using ILSpy.Debugger.Debugging;
using ILSpy.Debugger.Services;
namespace ILSpy.Debugger.AvalonEdit
diff --git a/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs b/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs
index 26e93978a..c459748e5 100644
--- a/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs
+++ b/Debugger/ILSpy.Debugger/Bookmarks/BreakpointBookmark.cs
@@ -28,8 +28,8 @@ namespace ILSpy.Debugger.Bookmarks
public enum BreakpointAction
{
Break,
-// Trace,
-// Condition
+ Trace,
+ Condition
}
public class BreakpointBookmark : BookmarkBase
diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj
index d714bc1b7..742048f58 100644
--- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj
+++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj
@@ -63,11 +63,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
@@ -80,6 +96,7 @@
+
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ArrayRangeNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ArrayRangeNode.cs
new file mode 100644
index 000000000..366d0647b
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ArrayRangeNode.cs
@@ -0,0 +1,120 @@
+// 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.Collections.Generic;
+using System.Text;
+
+using Debugger;
+using ICSharpCode.NRefactory.Ast;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public partial class Utils
+ {
+ public static IEnumerable LazyGetChildNodesOfArray(Expression expression, ArrayDimensions dimensions)
+ {
+ if (dimensions.TotalElementCount == 0)
+ return new TreeNode[] { new TreeNode(null, "(empty)", null, null, null) };
+
+ return new ArrayRangeNode(expression, dimensions, dimensions).ChildNodes;
+ }
+ }
+
+ /// This is a partent node for all elements within a given bounds
+ public class ArrayRangeNode: TreeNode
+ {
+ const int MaxElementCount = 100;
+
+ Expression arrayTarget;
+ ArrayDimensions bounds;
+ ArrayDimensions originalBounds;
+
+ public ArrayRangeNode(Expression arrayTarget, ArrayDimensions bounds, ArrayDimensions originalBounds)
+ {
+ this.arrayTarget = arrayTarget;
+ this.bounds = bounds;
+ this.originalBounds = originalBounds;
+
+ this.Name = GetName();
+ this.ChildNodes = LazyGetChildren();
+ }
+
+ string GetName()
+ {
+ StringBuilder name = new StringBuilder();
+ bool isFirst = true;
+ name.Append("[");
+ for(int i = 0; i < bounds.Count; i++) {
+ if (!isFirst) name.Append(", ");
+ isFirst = false;
+ ArrayDimension dim = bounds[i];
+ ArrayDimension originalDim = originalBounds[i];
+
+ if (dim.Count == 0) {
+ throw new DebuggerException("Empty dimension");
+ } else if (dim.Count == 1) {
+ name.Append(dim.LowerBound.ToString());
+ } else if (dim.Equals(originalDim)) {
+ name.Append("*");
+ } else {
+ name.Append(dim.LowerBound);
+ name.Append("..");
+ name.Append(dim.UpperBound);
+ }
+ }
+ name.Append("]");
+ return name.ToString();
+ }
+
+ static string GetName(int[] indices)
+ {
+ StringBuilder sb = new StringBuilder(indices.Length * 4);
+ sb.Append("[");
+ bool isFirst = true;
+ foreach(int index in indices) {
+ if (!isFirst) sb.Append(", ");
+ sb.Append(index.ToString());
+ isFirst = false;
+ }
+ sb.Append("]");
+ return sb.ToString();
+ }
+
+ IEnumerable LazyGetChildren()
+ {
+ // The whole array is small - just add all elements as childs
+ if (bounds.TotalElementCount <= MaxElementCount) {
+ foreach(int[] indices in bounds.Indices) {
+ string imageName;
+ var image = ExpressionNode.GetImageForArrayIndexer(out imageName);
+ var expression = new ExpressionNode(image, GetName(indices), arrayTarget.AppendIndexer(indices));
+ expression.ImageName = imageName;
+ yield return expression;
+ }
+ yield break;
+ }
+
+ // Find a dimension of size at least 2
+ int splitDimensionIndex = bounds.Count - 1;
+ for(int i = 0; i < bounds.Count; i++) {
+ if (bounds[i].Count > 1) {
+ splitDimensionIndex = i;
+ break;
+ }
+ }
+ ArrayDimension splitDim = bounds[splitDimensionIndex];
+
+ // Split the dimension
+ int elementsPerSegment = 1;
+ while (splitDim.Count > elementsPerSegment * MaxElementCount) {
+ elementsPerSegment *= MaxElementCount;
+ }
+ for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) {
+ List newDims = new List(bounds);
+ newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound));
+ yield return new ArrayRangeNode(arrayTarget, new ArrayDimensions(newDims), originalBounds);
+ }
+ yield break;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ChildNodesOfObject.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ChildNodesOfObject.cs
new file mode 100644
index 000000000..82959d534
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ChildNodesOfObject.cs
@@ -0,0 +1,153 @@
+// 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.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+
+using Debugger;
+using Debugger.MetaData;
+using ICSharpCode.Core;
+using ICSharpCode.NRefactory.Ast;
+using ILSpy.Debugger.Services;
+using ILSpy.Debugger.Services.Debugger;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public partial class Utils
+ {
+ public static IEnumerable LazyGetChildNodesOfObject(Expression targetObject, DebugType shownType)
+ {
+ MemberInfo[] publicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
+ MemberInfo[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
+ MemberInfo[] nonPublicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly);
+ MemberInfo[] nonPublicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
+
+ DebugType baseType = (DebugType)shownType.BaseType;
+ if (baseType != null) {
+ yield return new TreeNode(
+ ImageService.GetImage("Icons.16x16.Class"),
+ "BaseClass",
+ baseType.Name,
+ baseType.FullName,
+ baseType.FullName == "System.Object" ? null : Utils.LazyGetChildNodesOfObject(targetObject, baseType)
+ );
+ }
+
+ if (nonPublicInstance.Length > 0) {
+ yield return new TreeNode(
+ null,
+ "NonPublicMembers",
+ string.Empty,
+ string.Empty,
+ Utils.LazyGetMembersOfObject(targetObject, nonPublicInstance)
+ );
+ }
+
+ if (publicStatic.Length > 0 || nonPublicStatic.Length > 0) {
+ IEnumerable childs = Utils.LazyGetMembersOfObject(targetObject, publicStatic);
+ if (nonPublicStatic.Length > 0) {
+ TreeNode nonPublicStaticNode = new TreeNode(
+ null,
+ "NonPublicStaticMembers",
+ string.Empty,
+ string.Empty,
+ Utils.LazyGetMembersOfObject(targetObject, nonPublicStatic)
+ );
+ childs = Utils.PrependNode(nonPublicStaticNode, childs);
+ }
+ yield return new TreeNode(
+ null,
+ "StaticMembers",
+ string.Empty,
+ string.Empty,
+ childs
+ );
+ }
+
+ DebugType iListType = (DebugType)shownType.GetInterface(typeof(IList).FullName);
+ if (iListType != null) {
+ yield return new IListNode(targetObject);
+ } else {
+ DebugType iEnumerableType, itemType;
+ if (shownType.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) {
+ yield return new IEnumerableNode(targetObject, itemType);
+ }
+ }
+
+ foreach(TreeNode node in LazyGetMembersOfObject(targetObject, publicInstance)) {
+ yield return node;
+ }
+ }
+
+ public static IEnumerable LazyGetMembersOfObject(Expression expression, MemberInfo[] members)
+ {
+ List nodes = new List();
+ foreach(MemberInfo memberInfo in members) {
+ string imageName;
+ var image = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo, out imageName);
+ var exp = new ExpressionNode(image, memberInfo.Name, expression.AppendMemberReference((IDebugMemberInfo)memberInfo));
+ exp.ImageName = imageName;
+ nodes.Add(exp);
+ }
+ nodes.Sort();
+ return nodes;
+ }
+
+
+ public static IEnumerable LazyGetItemsOfIList(Expression targetObject)
+ {
+ // This is needed for expanding IEnumerable
+ targetObject = new CastExpression(
+ new TypeReference(typeof(IList).FullName),
+ targetObject,
+ CastType.Cast
+ );
+ int count = 0;
+ GetValueException error = null;
+ try {
+ count = GetIListCount(targetObject);
+ } catch (GetValueException e) {
+ // Cannot yield a value in the body of a catch clause (CS1631)
+ error = e;
+ }
+ if (error != null) {
+ yield return new TreeNode(null, "(error)", error.Message, null, null);
+ } else if (count == 0) {
+ yield return new TreeNode(null, "(empty)", null, null, null);
+ } else {
+ for(int i = 0; i < count; i++) {
+ string imageName;
+ var image = ExpressionNode.GetImageForArrayIndexer(out imageName);
+ var expression = new ExpressionNode(image, "[" + i + "]", targetObject.AppendIndexer(i));
+ expression.ImageName = imageName;
+ yield return expression;
+ }
+ }
+ }
+
+ ///
+ /// Evaluates System.Collections.ICollection.Count property on given object.
+ ///
+ /// Evaluating System.Collections.ICollection.Count on targetObject failed.
+ public static int GetIListCount(Expression targetObject)
+ {
+ Value list = targetObject.Evaluate(WindowsDebugger.CurrentProcess);
+ var iCollectionInterface = list.Type.GetInterface(typeof(ICollection).FullName);
+ if (iCollectionInterface == null)
+ throw new GetValueException(targetObject, targetObject.PrettyPrint() + " does not implement System.Collections.ICollection");
+ PropertyInfo countProperty = iCollectionInterface.GetProperty("Count");
+ // Do not get string representation since it can be printed in hex
+ return (int)list.GetPropertyValue(countProperty).PrimitiveValue;
+ }
+
+ public static IEnumerable PrependNode(TreeNode node, IEnumerable rest)
+ {
+ yield return node;
+ if (rest != null) {
+ foreach(TreeNode absNode in rest) {
+ yield return absNode;
+ }
+ }
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs
new file mode 100644
index 000000000..34ba634a9
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ExpressionNode.cs
@@ -0,0 +1,420 @@
+// 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.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Media;
+
+using Debugger;
+using Debugger.Interop.CorDebug;
+using Debugger.MetaData;
+using ICSharpCode.NRefactory.Ast;
+using ILSpy.Debugger.Services;
+using ILSpy.Debugger.Services.Debugger;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public class ExpressionNode: TreeNode, ISetText, INotifyPropertyChanged
+ {
+ bool evaluated;
+
+ Expression expression;
+ bool canSetText;
+ GetValueException error;
+ string fullText;
+
+ public bool Evaluated {
+ get { return evaluated; }
+ set { evaluated = value; }
+ }
+
+ public Expression Expression {
+ get { return expression; }
+ }
+
+ public override bool CanSetText {
+ get {
+ if (!evaluated) EvaluateExpression();
+ return canSetText;
+ }
+ }
+
+ public GetValueException Error {
+ get {
+ if (!evaluated) EvaluateExpression();
+ return error;
+ }
+ }
+
+ public string FullText {
+ get { return fullText; }
+ }
+
+ public override string Text {
+ get {
+ if (!evaluated) EvaluateExpression();
+ return base.Text;
+ }
+ set {
+ if (value != base.Text) {
+ base.Text = value;
+ NotifyPropertyChanged("Text");
+ }
+ }
+ }
+
+ public override string FullName {
+ get {
+ if (!evaluated) EvaluateExpression();
+
+ return this.expression.PrettyPrint() ?? Name.Trim();
+ }
+ }
+
+ public override string Type {
+ get {
+ if (!evaluated) EvaluateExpression();
+ return base.Type;
+ }
+ }
+
+ public override IEnumerable ChildNodes {
+ get {
+ if (!evaluated) EvaluateExpression();
+ return base.ChildNodes;
+ }
+ }
+
+ public override bool HasChildNodes {
+ get {
+ if (!evaluated) EvaluateExpression();
+ return base.HasChildNodes;
+ }
+ }
+
+ /// Used to determine available VisualizerCommands
+ private DebugType expressionType;
+ /// Used to determine available VisualizerCommands
+ private bool valueIsNull = true;
+
+ private IEnumerable visualizerCommands;
+ public override IEnumerable VisualizerCommands {
+ get {
+ if (visualizerCommands == null) {
+ visualizerCommands = getAvailableVisualizerCommands();
+ }
+ return visualizerCommands;
+ }
+ }
+
+ private IEnumerable getAvailableVisualizerCommands()
+ {
+ if (!evaluated) EvaluateExpression();
+
+ if (this.expressionType == null) {
+ // no visualizers if EvaluateExpression failed
+ yield break;
+ }
+ if (this.valueIsNull) {
+ // no visualizers if evaluated value is null
+ yield break;
+ }
+ /*if (this.expressionType.IsPrimitive || this.expressionType.IsSystemDotObject() || this.expressionType.IsEnum()) {
+ // no visualizers for primitive types
+ yield break;
+ }*/
+
+ yield break;
+// foreach (var descriptor in VisualizerDescriptors.GetAllDescriptors()) {
+// if (descriptor.IsVisualizerAvailable(this.expressionType)) {
+// yield return descriptor.CreateVisualizerCommand(this.Expression);
+// }
+// }
+ }
+
+ public ExpressionNode(ImageSource image, string name, Expression expression)
+ {
+ this.ImageSource = image;
+ this.Name = name;
+ this.expression = expression;
+ }
+
+ void EvaluateExpression()
+ {
+ evaluated = true;
+
+ Value val;
+ try {
+ val = expression.Evaluate(WindowsDebugger.DebuggedProcess);
+ } catch (GetValueException e) {
+ error = e;
+ this.Text = e.Message;
+ return;
+ }
+
+ this.canSetText = val.Type.IsPrimitive;
+
+ this.expressionType = val.Type;
+ this.Type = val.Type.Name;
+ this.valueIsNull = val.IsNull;
+
+ // Note that these return enumerators so they are lazy-evaluated
+ if (val.IsNull) {
+ } else if (val.Type.IsPrimitive || val.Type.FullName == typeof(string).FullName) { // Must be before IsClass
+ } else if (val.Type.IsArray) { // Must be before IsClass
+ if (val.ArrayLength > 0)
+ this.ChildNodes = Utils.LazyGetChildNodesOfArray(this.Expression, val.ArrayDimensions);
+ } else if (val.Type.IsClass || val.Type.IsValueType) {
+ if (val.Type.FullNameWithoutGenericArguments == typeof(List<>).FullName) {
+ if ((int)val.GetMemberValue("_size").PrimitiveValue > 0)
+ this.ChildNodes = Utils.LazyGetItemsOfIList(this.expression);
+ } else {
+ this.ChildNodes = Utils.LazyGetChildNodesOfObject(this.Expression, val.Type);
+ }
+ } else if (val.Type.IsPointer) {
+ Value deRef = val.Dereference();
+ if (deRef != null) {
+ this.ChildNodes = new ExpressionNode [] { new ExpressionNode(this.ImageSource, "*" + this.Name, this.Expression.AppendDereference()) };
+ }
+ }
+
+ if (true) {
+ TreeNode info = ICorDebug.GetDebugInfoRoot(val.AppDomain, val.CorValue);
+ this.ChildNodes = Utils.PrependNode(info, this.ChildNodes);
+ }
+
+ // Do last since it may expire the object
+ if (val.Type.IsInteger) {
+ fullText = FormatInteger(val.PrimitiveValue);
+ } else if (val.Type.IsPointer) {
+ fullText = String.Format("0x{0:X}", val.PointerAddress);
+ } else if ((val.Type.FullName == typeof(string).FullName ||
+ val.Type.FullName == typeof(char).FullName) && !val.IsNull) {
+ try {
+ fullText = '"' + Escape(val.InvokeToString()) + '"';
+ } catch (GetValueException e) {
+ error = e;
+ fullText = e.Message;
+ return;
+ }
+ } else if ((val.Type.IsClass || val.Type.IsValueType) && !val.IsNull) {
+ try {
+ fullText = val.InvokeToString();
+ } catch (GetValueException e) {
+ error = e;
+ fullText = e.Message;
+ return;
+ }
+ } else {
+ fullText = val.AsString();
+ }
+
+ this.Text = (fullText.Length > 256) ? fullText.Substring(0, 256) + "..." : fullText;
+ }
+
+ string Escape(string source)
+ {
+ return source.Replace("\n", "\\n")
+ .Replace("\t", "\\t")
+ .Replace("\r", "\\r")
+ .Replace("\0", "\\0")
+ .Replace("\b", "\\b")
+ .Replace("\a", "\\a")
+ .Replace("\f", "\\f")
+ .Replace("\v", "\\v")
+ .Replace("\"", "\\\"");
+ }
+
+ string FormatInteger(object i)
+ {
+ if (true)
+ return i.ToString();
+
+ string hex = null;
+ for(int len = 1;; len *= 2) {
+ hex = string.Format("{0:X" + len + "}", i);
+ if (hex.Length == len)
+ break;
+ }
+
+ if (true) {
+ return "0x" + hex;
+ } else {
+ if (ShowAsHex(i)) {
+ return String.Format("{0} (0x{1})", i, hex);
+ } else {
+ return i.ToString();
+ }
+ }
+ }
+
+ bool ShowAsHex(object i)
+ {
+ ulong val;
+ if (i is sbyte || i is short || i is int || i is long) {
+ unchecked { val = (ulong)Convert.ToInt64(i); }
+ if (val > (ulong)long.MaxValue)
+ val = ~val + 1;
+ } else {
+ val = Convert.ToUInt64(i);
+ }
+ if (val >= 0x10000)
+ return true;
+
+ int ones = 0; // How many 1s there is
+ int runs = 0; // How many runs of 1s there is
+ int size = 0; // Size of the integer in bits
+ while(val != 0) { // There is at least one 1
+ while((val & 1) == 0) { // Skip 0s
+ val = val >> 1;
+ size++;
+ }
+ while((val & 1) == 1) { // Skip 1s
+ val = val >> 1;
+ size++;
+ ones++;
+ }
+ runs++;
+ }
+
+ return size >= 7 && runs <= (size + 7) / 8;
+ }
+
+ public override bool SetText(string newText)
+ {
+ string fullName = FullName;
+
+ Value val = null;
+ try {
+ val = this.Expression.Evaluate(WindowsDebugger.DebuggedProcess);
+ if (val.Type.IsInteger && newText.StartsWith("0x")) {
+ try {
+ val.PrimitiveValue = long.Parse(newText.Substring(2), NumberStyles.HexNumber);
+ } catch (FormatException) {
+ throw new NotSupportedException();
+ } catch (OverflowException) {
+ throw new NotSupportedException();
+ }
+ } else {
+ val.PrimitiveValue = newText;
+ }
+ this.Text = newText;
+ return true;
+ } catch (NotSupportedException) {
+ string format = "Can not convert {0} to {1}";
+ string msg = string.Format(format, newText, val.Type.PrimitiveType);
+ System.Windows.MessageBox.Show(msg);
+ } catch (COMException) {
+ // COMException (0x80131330): Cannot perfrom SetValue on non-leaf frames.
+ // Happens if trying to set value after exception is breaked
+ System.Windows.MessageBox.Show("UnknownError");
+ }
+ return false;
+ }
+
+ public static ImageSource GetImageForThis(out string imageName)
+ {
+ imageName = "Icons.16x16.Parameter";
+ return ImageService.GetImage(imageName);
+ }
+
+ public static ImageSource GetImageForParameter(out string imageName)
+ {
+ imageName = "Icons.16x16.Parameter";
+ return ImageService.GetImage(imageName);
+ }
+
+ public static ImageSource GetImageForLocalVariable(out string imageName)
+ {
+ imageName = "Icons.16x16.Local";
+ return ImageService.GetImage(imageName);
+ }
+
+ public static ImageSource GetImageForArrayIndexer(out string imageName)
+ {
+ imageName = "Icons.16x16.Field";
+ return ImageService.GetImage(imageName);
+ }
+
+ public static ImageSource GetImageForMember(IDebugMemberInfo memberInfo, out string imageName)
+ {
+ string name = string.Empty;
+ if (memberInfo.IsPublic) {
+ } else if (memberInfo.IsAssembly) {
+ name += "Internal";
+ } else if (memberInfo.IsFamily) {
+ name += "Protected";
+ } else if (memberInfo.IsPrivate) {
+ name += "Private";
+ }
+ if (memberInfo is FieldInfo) {
+ name += "Field";
+ } else if (memberInfo is PropertyInfo) {
+ name += "Property";
+ } else if (memberInfo is MethodInfo) {
+ name += "Method";
+ } else {
+ throw new DebuggerException("Unknown member type " + memberInfo.GetType().FullName);
+ }
+
+ imageName = "Icons.16x16." + name;
+ return ImageService.GetImage(imageName);
+ }
+
+// public ContextMenuStrip GetContextMenu()
+// {
+// if (this.Error != null) return GetErrorContextMenu();
+//
+// ContextMenuStrip menu = new ContextMenuStrip();
+//
+// ToolStripMenuItem copyItem;
+// copyItem = new ToolStripMenuItem();
+// copyItem.Text = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.CopyToClipboard");
+// copyItem.Checked = false;
+// copyItem.Click += delegate {
+// ClipboardWrapper.SetText(fullText);
+// };
+
+// ToolStripMenuItem hexView;
+// hexView = new ToolStripMenuItem();
+// hexView.Text = ResourceService.GetString("MainWindow.Windows.Debug.LocalVariables.ShowInHexadecimal");
+// hexView.Checked = DebuggingOptions.Instance.ShowValuesInHexadecimal;
+// hexView.Click += delegate {
+// // refresh all pads that use ValueNode for display
+// DebuggingOptions.Instance.ShowValuesInHexadecimal = !DebuggingOptions.Instance.ShowValuesInHexadecimal;
+// // always check if instance is null, might be null if pad is not opened
+// if (LocalVarPad.Instance != null)
+// LocalVarPad.Instance.RefreshPad();
+// if (WatchPad.Instance != null)
+// WatchPad.Instance.RefreshPad();
+// };
+
+// menu.Items.AddRange(new ToolStripItem[] {
+// copyItem,
+// //hexView
+// });
+//
+// return menu;
+// }
+
+ public static WindowsDebugger WindowsDebugger {
+ get {
+ return (WindowsDebugger)DebuggerService.CurrentDebugger;
+ }
+ }
+
+ public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
+
+ private void NotifyPropertyChanged(string info)
+ {
+ if (PropertyChanged != null)
+ {
+ PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(info));
+ }
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ICorDebug.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ICorDebug.cs
new file mode 100644
index 000000000..001547d0d
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ICorDebug.cs
@@ -0,0 +1,153 @@
+// 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.Collections.Generic;
+using Debugger.Interop.CorDebug;
+using Debugger.MetaData;
+using Debugger;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public class ICorDebug
+ {
+ public class InfoNode: TreeNode
+ {
+ List children;
+
+ public InfoNode(string name, string text): this(name, text, null)
+ {
+
+ }
+
+ public InfoNode(string name, string text, List children)
+ {
+ this.Name = name;
+ this.Text = text;
+ this.ChildNodes = children;
+ this.children = children;
+ }
+
+ public void AddChild(string name, string text)
+ {
+ if (children == null) {
+ children = new List();
+ this.ChildNodes = children;
+ }
+ children.Add(new InfoNode(name, text));
+ }
+
+ public void AddChild(string name, string text, List subChildren)
+ {
+ if (children == null) {
+ children = new List();
+ this.ChildNodes = children;
+ }
+ children.Add(new InfoNode(name, text, subChildren));
+ }
+ }
+
+ public static InfoNode GetDebugInfoRoot(AppDomain appDomain, ICorDebugValue corValue)
+ {
+ return new InfoNode("ICorDebug", "", GetDebugInfo(appDomain, corValue));
+ }
+
+ public static List GetDebugInfo(AppDomain appDomain, ICorDebugValue corValue)
+ {
+ List items = new List();
+
+ if (corValue is ICorDebugValue) {
+ InfoNode info = new InfoNode("ICorDebugValue", "");
+ info.AddChild("Address", corValue.GetAddress().ToString("X8"));
+ info.AddChild("Type", ((CorElementType)corValue.GetTheType()).ToString());
+ info.AddChild("Size", corValue.GetSize().ToString());
+ items.Add(info);
+ }
+ if (corValue is ICorDebugValue2) {
+ InfoNode info = new InfoNode("ICorDebugValue2", "");
+ ICorDebugValue2 corValue2 = (ICorDebugValue2)corValue;
+ string fullname;
+ try {
+ fullname = DebugType.CreateFromCorType(appDomain, corValue2.GetExactType()).FullName;
+ } catch (DebuggerException e) {
+ fullname = e.Message;
+ }
+ info.AddChild("ExactType", fullname);
+ items.Add(info);
+ }
+ if (corValue is ICorDebugGenericValue) {
+ InfoNode info = new InfoNode("ICorDebugGenericValue", "");
+ try {
+ byte[] bytes = ((ICorDebugGenericValue)corValue).GetRawValue();
+ for(int i = 0; i < bytes.Length; i += 8) {
+ string val = "";
+ for(int j = i; j < bytes.Length && j < i + 8; j++) {
+ val += bytes[j].ToString("X2") + " ";
+ }
+ info.AddChild("Value" + i.ToString("X2"), val);
+ }
+ } catch (System.ArgumentException) {
+ info.AddChild("Value", "N/A");
+ }
+ items.Add(info);
+ }
+ if (corValue is ICorDebugReferenceValue) {
+ InfoNode info = new InfoNode("ICorDebugReferenceValue", "");
+ ICorDebugReferenceValue refValue = (ICorDebugReferenceValue)corValue;
+ info.AddChild("IsNull", (refValue.IsNull() != 0).ToString());
+ if (refValue.IsNull() == 0) {
+ info.AddChild("Value", refValue.GetValue().ToString("X8"));
+ if (refValue.Dereference() != null) {
+ info.AddChild("Dereference", "", GetDebugInfo(appDomain, refValue.Dereference()));
+ } else {
+ info.AddChild("Dereference", "N/A");
+ }
+ }
+ items.Add(info);
+ }
+ if (corValue is ICorDebugHeapValue) {
+ InfoNode info = new InfoNode("ICorDebugHeapValue", "");
+ items.Add(info);
+ }
+ if (corValue is ICorDebugHeapValue2) {
+ InfoNode info = new InfoNode("ICorDebugHeapValue2", "");
+ items.Add(info);
+ }
+ if (corValue is ICorDebugObjectValue) {
+ InfoNode info = new InfoNode("ICorDebugObjectValue", "");
+ ICorDebugObjectValue objValue = (ICorDebugObjectValue)corValue;
+ info.AddChild("Class", objValue.GetClass().GetToken().ToString("X8"));
+ info.AddChild("IsValueClass", (objValue.IsValueClass() != 0).ToString());
+ items.Add(info);
+ }
+ if (corValue is ICorDebugObjectValue2) {
+ InfoNode info = new InfoNode("ICorDebugObjectValue2", "");
+ items.Add(info);
+ }
+ if (corValue is ICorDebugBoxValue) {
+ InfoNode info = new InfoNode("ICorDebugBoxValue", "");
+ ICorDebugBoxValue boxValue = (ICorDebugBoxValue)corValue;
+ info.AddChild("Object", "", GetDebugInfo(appDomain, boxValue.GetObject()));
+ items.Add(info);
+ }
+ if (corValue is ICorDebugStringValue) {
+ InfoNode info = new InfoNode("ICorDebugStringValue", "");
+ ICorDebugStringValue stringValue = (ICorDebugStringValue)corValue;
+ info.AddChild("Length", stringValue.GetLength().ToString());
+ info.AddChild("String", stringValue.GetString());
+ items.Add(info);
+ }
+ if (corValue is ICorDebugArrayValue) {
+ InfoNode info = new InfoNode("ICorDebugArrayValue", "");
+ info.AddChild("...", "...");
+ items.Add(info);
+ }
+ if (corValue is ICorDebugHandleValue) {
+ InfoNode info = new InfoNode("ICorDebugHandleValue", "");
+ ICorDebugHandleValue handleValue = (ICorDebugHandleValue)corValue;
+ info.AddChild("HandleType", handleValue.GetHandleType().ToString());
+ items.Add(info);
+ }
+
+ return items;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/IEnumerableNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/IEnumerableNode.cs
new file mode 100644
index 000000000..5f8cc96df
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/IEnumerableNode.cs
@@ -0,0 +1,29 @@
+// 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 Debugger.MetaData;
+using ICSharpCode.NRefactory.Ast;
+using ILSpy.Debugger.Services.Debugger;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ ///
+ /// IEnumerable node in the variable tree.
+ ///
+ public class IEnumerableNode : TreeNode
+ {
+ Expression targetObject;
+ Expression debugListExpression;
+
+ public IEnumerableNode(Expression targetObject, DebugType itemType)
+ {
+ this.targetObject = targetObject;
+
+ this.Name = "IEnumerable";
+ this.Text = "Expanding will enumerate the IEnumerable";
+ DebugType debugListType;
+ this.debugListExpression = DebuggerHelpers.CreateDebugListExpression(targetObject, itemType, out debugListType);
+ this.ChildNodes = Utils.LazyGetItemsOfIList(this.debugListExpression);
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/IListNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/IListNode.cs
new file mode 100644
index 000000000..670467011
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/IListNode.cs
@@ -0,0 +1,25 @@
+// 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 ICSharpCode.NRefactory.Ast;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public class IListNode : TreeNode
+ {
+ Expression targetObject;
+ int count;
+
+ public IListNode(Expression targetObject)
+ {
+ this.targetObject = targetObject;
+
+ this.Name = "IList";
+ this.count = Utils.GetIListCount(this.targetObject);
+ this.ChildNodes = Utils.LazyGetItemsOfIList(this.targetObject);
+ }
+
+ public override bool HasChildNodes {
+ get { return this.count > 0; }
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/ISetText.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ISetText.cs
new file mode 100644
index 000000000..ec52873d2
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ISetText.cs
@@ -0,0 +1,13 @@
+// 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;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public interface ISetText
+ {
+ bool CanSetText { get; }
+
+ bool SetText(string text);
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/Tooltips/ITreeNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/ITreeNode.cs
similarity index 95%
rename from Debugger/ILSpy.Debugger/Services/Debugger/Tooltips/ITreeNode.cs
rename to Debugger/ILSpy.Debugger/Models/TreeModel/ITreeNode.cs
index 93e164b09..270dcb0b5 100644
--- a/Debugger/ILSpy.Debugger/Services/Debugger/Tooltips/ITreeNode.cs
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/ITreeNode.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Windows.Media;
-namespace ILSpy.Debugger.Debugging
+namespace ILSpy.Debugger.Models.TreeModel
{
///
/// Node that can be bound to .
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/Tooltips/IVisualizerCommand.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/IVisualizerCommand.cs
similarity index 93%
rename from Debugger/ILSpy.Debugger/Services/Debugger/Tooltips/IVisualizerCommand.cs
rename to Debugger/ILSpy.Debugger/Models/TreeModel/IVisualizerCommand.cs
index df25c392f..f1ab16f33 100644
--- a/Debugger/ILSpy.Debugger/Services/Debugger/Tooltips/IVisualizerCommand.cs
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/IVisualizerCommand.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace ILSpy.Debugger.Debugging
+namespace ILSpy.Debugger.Models.TreeModel
{
///
/// Command called from .
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/SavedTreeNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/SavedTreeNode.cs
new file mode 100644
index 000000000..818b7335f
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/SavedTreeNode.cs
@@ -0,0 +1,26 @@
+// 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.Windows.Media;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public class SavedTreeNode : TreeNode
+ {
+ public override bool CanSetText {
+ get { return true; }
+ }
+
+ public SavedTreeNode(ImageSource image, string fullname, string text)
+ {
+ base.ImageSource = image;
+ FullName = fullname;
+ Text = text;
+ }
+
+ public override bool SetText(string newValue) {
+ Text = newValue;
+ return false;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/StackFrameNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/StackFrameNode.cs
new file mode 100644
index 000000000..3c74db041
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/StackFrameNode.cs
@@ -0,0 +1,47 @@
+// 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.Collections.Generic;
+using Debugger;
+using Debugger.MetaData;
+using ICSharpCode.NRefactory.Ast;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public class StackFrameNode: TreeNode
+ {
+ StackFrame stackFrame;
+
+ public StackFrame StackFrame {
+ get { return stackFrame; }
+ }
+
+ public StackFrameNode(StackFrame stackFrame)
+ {
+ this.stackFrame = stackFrame;
+
+ this.Name = stackFrame.MethodInfo.Name;
+ this.ChildNodes = LazyGetChildNodes();
+ }
+
+ IEnumerable LazyGetChildNodes()
+ {
+ foreach(DebugParameterInfo par in stackFrame.MethodInfo.GetParameters()) {
+ string imageName;
+ var image = ExpressionNode.GetImageForParameter(out imageName);
+ var expression = new ExpressionNode(image, par.Name, par.GetExpression());
+ expression.ImageName = imageName;
+ yield return expression;
+ }
+ foreach(DebugLocalVariableInfo locVar in stackFrame.MethodInfo.GetLocalVariables(this.StackFrame.IP)) {
+ string imageName;
+ var image = ExpressionNode.GetImageForLocalVariable(out imageName);
+ var expression = new ExpressionNode(image, locVar.Name, locVar.GetExpression());
+ expression.ImageName = imageName;
+ yield return expression;
+ }
+ if (stackFrame.Thread.CurrentException != null) {
+ yield return new ExpressionNode(null, "__exception", new IdentifierExpression("__exception"));
+ }
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/TreeNode.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/TreeNode.cs
new file mode 100644
index 000000000..fd264110f
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/TreeNode.cs
@@ -0,0 +1,94 @@
+// 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.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Media;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ ///
+ /// A node in the variable tree.
+ /// The node is imutable.
+ ///
+ public class TreeNode : ITreeNode
+ {
+ string text = string.Empty;
+
+ IEnumerable childNodes = null;
+
+ public ImageSource ImageSource { get; protected set; }
+
+ public string Name { get; set; }
+
+ public string ImageName { get; set; }
+
+ public virtual string FullName {
+ get { return Name; }
+ set { Name = value; }
+ }
+
+ public virtual string Text
+ {
+ get { return text; }
+ set { text = value; }
+ }
+
+ public virtual string Type { get; protected set; }
+
+ public virtual IEnumerable ChildNodes {
+ get { return childNodes; }
+ protected set { childNodes = value; }
+ }
+
+ IEnumerable ITreeNode.ChildNodes {
+ get { return childNodes; }
+ }
+
+ public virtual bool HasChildNodes {
+ get { return childNodes != null; }
+ }
+
+ public virtual bool CanSetText {
+ get { return false; }
+ }
+
+ public virtual IEnumerable VisualizerCommands {
+ get {
+ return null;
+ }
+ }
+
+ public virtual bool HasVisualizerCommands {
+ get {
+ return (VisualizerCommands != null) && (VisualizerCommands.Count() > 0);
+ }
+ }
+
+ public bool IsPinned { get; set; }
+
+ public TreeNode()
+ {
+ }
+
+ public TreeNode(ImageSource iconImage, string name, string text, string type, IEnumerable childNodes)
+ {
+ this.ImageSource = iconImage;
+ this.Name = name;
+ this.text = text;
+ this.Type = type;
+ this.childNodes = childNodes;
+ }
+
+ public int CompareTo(ITreeNode other)
+ {
+ return this.FullName.CompareTo(other.FullName);
+ }
+
+ public virtual bool SetText(string newValue) {
+ return false;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Models/TreeModel/Utils.cs b/Debugger/ILSpy.Debugger/Models/TreeModel/Utils.cs
new file mode 100644
index 000000000..167f4573b
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Models/TreeModel/Utils.cs
@@ -0,0 +1,66 @@
+// 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.Windows.Threading;
+
+using Debugger;
+
+namespace ILSpy.Debugger.Models.TreeModel
+{
+ public static partial class Utils
+ {
+ /// Process on which to track debuggee state
+ public static void DoEvents(Process process)
+ {
+ if (process == null) return;
+ DebuggeeState oldState = process.DebuggeeState;
+ WpfDoEvents();
+ DebuggeeState newState = process.DebuggeeState;
+ if (oldState != newState) {
+ //LoggingService.Info("Aborted because debuggee resumed");
+ throw new AbortedBecauseDebuggeeResumedException();
+ }
+ }
+
+ public static void WpfDoEvents()
+ {
+ DispatcherFrame frame = new DispatcherFrame();
+ Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => frame.Continue = false));
+ Dispatcher.PushFrame(frame);
+ }
+ }
+
+ public class AbortedBecauseDebuggeeResumedException: System.Exception
+ {
+ public AbortedBecauseDebuggeeResumedException(): base()
+ {
+
+ }
+ }
+
+ public class PrintTimes: PrintTime
+ {
+ public PrintTimes(string text): base(text + " - end")
+ {
+ //LoggingService.InfoFormatted("{0} - start", text);
+ }
+ }
+
+ public class PrintTime: IDisposable
+ {
+ string text;
+ System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
+
+ public PrintTime(string text)
+ {
+ this.text = text;
+ stopwatch.Start();
+ }
+
+ public void Dispose()
+ {
+ stopwatch.Stop();
+ //LoggingService.InfoFormatted("{0} ({1} ms)", text, stopwatch.ElapsedMilliseconds);
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerHelper.cs b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerHelper.cs
new file mode 100644
index 000000000..21c55ac79
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerHelper.cs
@@ -0,0 +1,111 @@
+// 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 Debugger.Interop.CorDebug;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Debugger;
+using Debugger.MetaData;
+using ICSharpCode.NRefactory.Ast;
+
+namespace ILSpy.Debugger.Services.Debugger
+{
+ public static class DebuggerHelpers
+ {
+ ///
+ /// Creates an expression which, when evaluated, creates a List<T> in the debugee
+ /// filled with contents of IEnumerable<T> from the debugee.
+ ///
+ /// Expression for IEnumerable variable in the debugee.
+ ///
+ /// The generic argument of IEnumerable<T> that implements.
+ public static Expression CreateDebugListExpression(Expression iEnumerableVariable, DebugType itemType, out DebugType listType)
+ {
+ // is using itemType.AppDomain ok?
+ listType = DebugType.CreateFromType(itemType.AppDomain, typeof(System.Collections.Generic.List<>), itemType);
+ var iEnumerableType = DebugType.CreateFromType(itemType.AppDomain, typeof(IEnumerable<>), itemType);
+ // explicitely cast the variable to IEnumerable, where T is itemType
+ Expression iEnumerableVariableExplicitCast = new CastExpression(iEnumerableType.GetTypeReference() , iEnumerableVariable, CastType.Cast);
+ return new ObjectCreateExpression(listType.GetTypeReference(), iEnumerableVariableExplicitCast.ToList());
+ }
+
+ ///
+ /// Gets underlying address of object in the debuggee.
+ ///
+ public static ulong GetObjectAddress(this Value val)
+ {
+ if (val.IsNull) return 0;
+ ICorDebugReferenceValue refVal = val.CorReferenceValue;
+ return refVal.GetValue();
+ }
+
+ ///
+ /// Returns true if this type is enum.
+ ///
+ public static bool IsEnum(this DebugType type)
+ {
+ return (type.BaseType != null) && (type.BaseType.FullName == "System.Enum");
+ }
+
+ ///
+ /// Returns true is this type is just System.Object.
+ ///
+ public static bool IsSystemDotObject(this DebugType type)
+ {
+ return type.FullName == "System.Object";
+ }
+
+ ///
+ /// Evaluates expression and gets underlying address of object in the debuggee.
+ ///
+ public static ulong GetObjectAddress(this Expression expr)
+ {
+ return expr.Evaluate(WindowsDebugger.CurrentProcess).GetObjectAddress();
+ }
+
+ ///
+ /// System.Runtime.CompilerServices.GetHashCode method, for obtaining non-overriden hash codes from debuggee.
+ ///
+ private static DebugMethodInfo hashCodeMethod;
+
+ ///
+ /// Invokes RuntimeHelpers.GetHashCode on given value, that is a default hashCode ignoring user overrides.
+ ///
+ /// Valid value.
+ /// Hash code of the object in the debugee.
+ public static int InvokeDefaultGetHashCode(this Value value)
+ {
+ if (hashCodeMethod == null || hashCodeMethod.Process.HasExited) {
+ DebugType typeRuntimeHelpers = DebugType.CreateFromType(value.AppDomain, typeof(System.Runtime.CompilerServices.RuntimeHelpers));
+ hashCodeMethod = (DebugMethodInfo)typeRuntimeHelpers.GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Static);
+ if (hashCodeMethod == null) {
+ throw new DebuggerException("Cannot obtain method System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode");
+ }
+ }
+ // David: I had hard time finding out how to invoke static method.
+ // value.InvokeMethod is nice for instance methods.
+ // what about MethodInfo.Invoke() ?
+ // also, there could be an overload that takes 1 parameter instead of array
+ Value defaultHashCode = Eval.InvokeMethod(DebuggerHelpers.hashCodeMethod, null, new Value[]{value});
+
+ //MethodInfo method = value.Type.GetMember("GetHashCode", BindingFlags.Method | BindingFlags.IncludeSuperType) as MethodInfo;
+ //string hashCode = value.InvokeMethod(method, null).AsString;
+ return (int)defaultHashCode.PrimitiveValue;
+ }
+
+ public static Value EvalPermanentReference(this Expression expr)
+ {
+ return expr.Evaluate(WindowsDebugger.CurrentProcess).GetPermanentReference();
+ }
+
+ public static bool IsNull(this Expression expr)
+ {
+ return expr.Evaluate(WindowsDebugger.CurrentProcess).IsNull;
+ }
+
+ public static DebugType GetType(this Expression expr)
+ {
+ return expr.Evaluate(WindowsDebugger.CurrentProcess).Type;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/ListHelper.cs b/Debugger/ILSpy.Debugger/Services/Debugger/ListHelper.cs
new file mode 100644
index 000000000..f4540c104
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Services/Debugger/ListHelper.cs
@@ -0,0 +1,33 @@
+// 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.Collections.Generic;
+
+namespace ILSpy.Debugger.Services.Debugger
+{
+ ///
+ /// ListHelper wraps System.Collection.Generic.List methods to return the original list,
+ /// instead of returning 'void', so we can write eg. list.Sorted().First()
+ ///
+ public static class ListHelper
+ {
+ public static List Sorted(this List list, IComparer comparer)
+ {
+ list.Sort(comparer);
+ return list;
+ }
+
+ public static List Sorted(this List list)
+ {
+ list.Sort();
+ return list;
+ }
+
+ public static List ToList(this T singleItem)
+ {
+ var newList = new List();
+ newList.Add(singleItem);
+ return newList;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/RemotingConfigurationHelpper.cs b/Debugger/ILSpy.Debugger/Services/Debugger/RemotingConfigurationHelpper.cs
new file mode 100644
index 000000000..88fafe46e
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Services/Debugger/RemotingConfigurationHelpper.cs
@@ -0,0 +1,81 @@
+// 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.IO;
+using System.Reflection;
+using System.Runtime.Remoting;
+using System.Security.Policy;
+
+namespace ILSpy.Debugger.Services.Debugger
+{
+ [Serializable]
+ class RemotingConfigurationHelpper
+ {
+ public string path;
+
+ public RemotingConfigurationHelpper(string path)
+ {
+ this.path = path;
+ }
+
+ public static string GetLoadedAssemblyPath(string assemblyName)
+ {
+ string path = null;
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
+ try {
+ string fullFilename = assembly.Location;
+ if (Path.GetFileName(fullFilename).Equals(assemblyName, StringComparison.OrdinalIgnoreCase)) {
+ path = Path.GetDirectoryName(fullFilename);
+ break;
+ }
+ } catch (NotSupportedException) {
+ // assembly.Location throws NotSupportedException for assemblies emitted using
+ // Reflection.Emit by custom controls used in the forms designer
+ }
+ }
+ if (path == null) {
+ throw new Exception("Assembly " + assemblyName + " is not loaded");
+ }
+ return path;
+ }
+
+ public void Configure()
+ {
+ AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
+
+ RemotingConfiguration.Configure(Path.Combine(path, "Client.config"), false);
+
+ string baseDir = Directory.GetDirectoryRoot(AppDomain.CurrentDomain.BaseDirectory);
+ string relDirs = AppDomain.CurrentDomain.BaseDirectory + ";" + path;
+ AppDomain serverAppDomain = AppDomain.CreateDomain("Debugging server",
+ new Evidence(AppDomain.CurrentDomain.Evidence),
+ baseDir,
+ relDirs,
+ AppDomain.CurrentDomain.ShadowCopyFiles);
+ serverAppDomain.DoCallBack(new CrossAppDomainDelegate(ConfigureServer));
+ }
+
+ private void ConfigureServer()
+ {
+ AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
+ RemotingConfiguration.Configure(Path.Combine(path, "Server.config"), false);
+ }
+
+ Assembly AssemblyResolve(object sender, ResolveEventArgs args)
+ {
+ foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
+ try {
+ string fullFilename = assembly.Location;
+ if (Path.GetFileNameWithoutExtension(fullFilename).Equals(args.Name, StringComparison.OrdinalIgnoreCase) ||
+ assembly.FullName == args.Name) {
+ return assembly;
+ }
+ } catch (NotSupportedException) {
+ // assembly.Location throws NotSupportedException for assemblies emitted using
+ // Reflection.Emit by custom controls used in the forms designer
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/TypeResolverExtension.cs b/Debugger/ILSpy.Debugger/Services/Debugger/TypeResolverExtension.cs
new file mode 100644
index 000000000..b913e07d1
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Services/Debugger/TypeResolverExtension.cs
@@ -0,0 +1,82 @@
+// 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.Collections;
+using Debugger.MetaData;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace ILSpy.Debugger.Services.Debugger
+{
+ ///
+ /// Helper for obtaining information about DebugType.
+ ///
+ public static class TypeResolverExtension
+ {
+ ///
+ /// Gets generic interface this type implements.
+ /// The generic argument of the interface does not have to be specified.
+ /// If you know the generic argument, use DebugType.GetInterface.
+ ///
+ /// Eg. "System.Collections.Generic.IList"
+ public static DebugType GetGenericInterface(this DebugType type, string fullNamePrefix)
+ {
+ foreach(DebugType inter in type.GetInterfaces()) {
+ if (inter.FullName.StartsWith(fullNamePrefix) && inter.GetGenericArguments().Length > 0) {
+ return inter;
+ }
+ }
+ // not found, search BaseType
+ return type.BaseType == null ? null : (DebugType)type.BaseType.GetInterface(fullNamePrefix);
+ }
+
+ ///
+ /// Resolves implementation of System.Collections.Generic.IList on this type.
+ ///
+ /// Result found implementation of System.Collections.Generic.IList.
+ /// The only generic argument of
+ /// True if found, false otherwise.
+ public static bool ResolveIListImplementation(this DebugType type, out DebugType iListType, out DebugType itemType)
+ {
+ return type.ResolveGenericInterfaceImplementation(
+ "System.Collections.Generic.IList", out iListType, out itemType);
+ }
+
+ ///
+ /// Resolves implementation of System.Collections.Generic.IEnumerable on this type.
+ ///
+ /// Result found implementation of System.Collections.Generic.IEnumerable.
+ /// The only generic argument of
+ /// True if found, false otherwise.
+ public static bool ResolveIEnumerableImplementation(this DebugType type, out DebugType iEnumerableType, out DebugType itemType)
+ {
+ return type.ResolveGenericInterfaceImplementation(
+ "System.Collections.Generic.IEnumerable", out iEnumerableType, out itemType);
+ }
+
+ ///
+ /// Resolves implementation of a single-generic-argument interface on this type.
+ ///
+ /// Interface name to search for (eg. "System.Collections.Generic.IList")
+ /// Result found implementation.
+ /// The only generic argument of
+ /// True if found, false otherwise.
+ public static bool ResolveGenericInterfaceImplementation(this DebugType type, string fullNamePrefix, out DebugType implementation, out DebugType itemType)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type");
+
+ implementation = null;
+ itemType = null;
+
+ implementation = type.GetGenericInterface(fullNamePrefix);
+ if (implementation != null) {
+ if (implementation.GetGenericArguments().Length == 1) {
+ itemType = (DebugType)implementation.GetGenericArguments()[0];
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs
new file mode 100644
index 000000000..3a3f85139
--- /dev/null
+++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs
@@ -0,0 +1,727 @@
+// 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.Runtime.InteropServices;
+using System.Text;
+using System.Windows;
+using System.Windows.Media;
+
+using Debugger;
+using Debugger.Interop.CorPublish;
+using ICSharpCode.NRefactory;
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.NRefactory.Visitors;
+using ILSpy.Debugger.Bookmarks;
+using ILSpy.Debugger.Models.TreeModel;
+using ILSpy.Debugger.Services.Debugger;
+using CorDbg = Debugger;
+using Process = Debugger.Process;
+
+namespace ILSpy.Debugger.Services
+{
+ public class WindowsDebugger : IDebugger
+ {
+ enum StopAttachedProcessDialogResult {
+ Detach = 0,
+ Terminate = 1,
+ Cancel = 2
+ }
+
+ bool useRemotingForThreadInterop = false;
+ bool attached;
+
+ NDebugger debugger;
+
+ ICorPublish corPublish;
+
+ Process debuggedProcess;
+
+ //DynamicTreeDebuggerRow currentTooltipRow;
+ //Expression currentTooltipExpression;
+
+ public event EventHandler ProcessSelected;
+
+ public NDebugger DebuggerCore {
+ get {
+ return debugger;
+ }
+ }
+
+ public Process DebuggedProcess {
+ get {
+ return debuggedProcess;
+ }
+ }
+
+ public static Process CurrentProcess {
+ get {
+ WindowsDebugger dbgr = DebuggerService.CurrentDebugger as WindowsDebugger;
+ if (dbgr != null && dbgr.DebuggedProcess != null) {
+ return dbgr.DebuggedProcess;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ ///
+ public bool BreakAtBeginning {
+ get;
+ set;
+ }
+
+ protected virtual void OnProcessSelected(ProcessEventArgs e)
+ {
+ if (ProcessSelected != null) {
+ ProcessSelected(this, e);
+ }
+ }
+
+ public bool ServiceInitialized {
+ get {
+ return debugger != null;
+ }
+ }
+
+ public WindowsDebugger()
+ {
+
+ }
+
+ #region IDebugger Members
+
+ string errorDebugging = "Error.Debugging";
+ string errorNotDebugging = "Error.NotDebugging";
+ string errorProcessRunning = "Error.ProcessRunning";
+ string errorProcessPaused = "Error.ProcessPaused";
+ string errorCannotStepNoActiveFunction = "Threads.CannotStepNoActiveFunction";
+
+ public bool IsDebugging {
+ get {
+ return ServiceInitialized && debuggedProcess != null;
+ }
+ }
+
+ public bool IsAttached {
+ get {
+ return ServiceInitialized && attached;
+ }
+ }
+
+ public bool IsProcessRunning {
+ get {
+ return IsDebugging && debuggedProcess.IsRunning;
+ }
+ }
+
+ public void Start(ProcessStartInfo processStartInfo)
+ {
+ if (IsDebugging) {
+ MessageBox.Show(errorDebugging);
+ return;
+ }
+ if (!ServiceInitialized) {
+ InitializeService();
+ }
+
+ string version = debugger.GetProgramVersion(processStartInfo.FileName);
+
+ if (version.StartsWith("v1.0")) {
+ MessageBox.Show("Net10NotSupported");
+ } else if (version.StartsWith("v1.1")) {
+ MessageBox.Show("Net1.1NotSupported");
+// } else if (string.IsNullOrEmpty(version)) {
+// // Not a managed assembly
+// MessageBox.Show(".Error.BadAssembly}");
+ } else if (debugger.IsKernelDebuggerEnabled) {
+ MessageBox.Show("KernelDebuggerEnabled");
+ } else {
+ attached = false;
+ if (DebugStarting != null)
+ DebugStarting(this, EventArgs.Empty);
+
+ try {
+ Process process = debugger.Start(processStartInfo.FileName,
+ processStartInfo.WorkingDirectory,
+ processStartInfo.Arguments);
+ SelectProcess(process);
+ } catch (System.Exception e) {
+ // COMException: The request is not supported. (Exception from HRESULT: 0x80070032)
+ // COMException: The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log for more detail. (Exception from HRESULT: 0x800736B1)
+ // COMException: The requested operation requires elevation. (Exception from HRESULT: 0x800702E4)
+ // COMException: The directory name is invalid. (Exception from HRESULT: 0x8007010B)
+ // BadImageFormatException: is not a valid Win32 application. (Exception from HRESULT: 0x800700C1)
+ // UnauthorizedAccessException: Отказано в доступе. (Исключение из HRESULT: 0x80070005 (E_ACCESSDENIED))
+ if (e is COMException || e is BadImageFormatException || e is UnauthorizedAccessException) {
+ string msg = "CannotStartProcess";
+ msg += " " + e.Message;
+ // TODO: Remove
+ if (e is COMException && ((uint)((COMException)e).ErrorCode == 0x80070032)) {
+ msg += Environment.NewLine + Environment.NewLine;
+ msg += "64-bit debugging is not supported. Please set Project -> Project Options... -> Compiling -> Target CPU to 32bit.";
+ }
+ MessageBox.Show(msg);
+
+ if (DebugStopped != null)
+ DebugStopped(this, EventArgs.Empty);
+ } else {
+ throw;
+ }
+ }
+ }
+ }
+
+ public void Attach(System.Diagnostics.Process existingProcess)
+ {
+ if (existingProcess == null)
+ return;
+
+ if (IsDebugging) {
+ MessageBox.Show(errorDebugging);
+ return;
+ }
+ if (!ServiceInitialized) {
+ InitializeService();
+ }
+
+ string version = debugger.GetProgramVersion(existingProcess.MainModule.FileName);
+ if (version.StartsWith("v1.0")) {
+ MessageBox.Show("Net10NotSupported");
+ } else {
+ if (DebugStarting != null)
+ DebugStarting(this, EventArgs.Empty);
+
+ try {
+ Process process = debugger.Attach(existingProcess);
+ attached = true;
+ SelectProcess(process);
+ } catch (System.Exception e) {
+ // CORDBG_E_DEBUGGER_ALREADY_ATTACHED
+ if (e is COMException || e is UnauthorizedAccessException) {
+ string msg = "CannotAttachToProcess";
+ MessageBox.Show(msg + " " + e.Message);
+
+ if (DebugStopped != null)
+ DebugStopped(this, EventArgs.Empty);
+ } else {
+ throw;
+ }
+ }
+ }
+ }
+
+ public void Detach()
+ {
+ if (debuggedProcess == null)
+ return;
+
+ debugger.Detach();
+ }
+
+ public void StartWithoutDebugging(ProcessStartInfo processStartInfo)
+ {
+ System.Diagnostics.Process.Start(processStartInfo);
+ }
+
+ public void Stop()
+ {
+ if (!IsDebugging) {
+ MessageBox.Show(errorNotDebugging, "Stop");
+ return;
+ }
+ if (IsAttached) {
+ Detach();
+ } else {
+ debuggedProcess.Terminate();
+ }
+ }
+
+ // ExecutionControl:
+
+ public void Break()
+ {
+ if (!IsDebugging) {
+ MessageBox.Show(errorNotDebugging, "Break");
+ return;
+ }
+ if (!IsProcessRunning) {
+ MessageBox.Show(errorProcessPaused, "Break");
+ return;
+ }
+ debuggedProcess.Break();
+ }
+
+ public void Continue()
+ {
+ if (!IsDebugging) {
+ MessageBox.Show(errorNotDebugging, "Continue");
+ return;
+ }
+ if (IsProcessRunning) {
+ MessageBox.Show(errorProcessRunning, "Continue");
+ return;
+ }
+ debuggedProcess.AsyncContinue();
+ }
+
+ // Stepping:
+
+ public void StepInto()
+ {
+ if (!IsDebugging) {
+ MessageBox.Show(errorNotDebugging, "StepInto");
+ return;
+ }
+ if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
+ MessageBox.Show(errorCannotStepNoActiveFunction, "StepInto");
+ } else {
+ debuggedProcess.SelectedStackFrame.AsyncStepInto();
+ }
+ }
+
+ public void StepOver()
+ {
+ if (!IsDebugging) {
+ MessageBox.Show(errorNotDebugging, "StepOver");
+ return;
+ }
+ if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
+ MessageBox.Show(errorCannotStepNoActiveFunction, "StepOver");
+ } else {
+ debuggedProcess.SelectedStackFrame.AsyncStepOver();
+ }
+ }
+
+ public void StepOut()
+ {
+ if (!IsDebugging) {
+ MessageBox.Show(errorNotDebugging, "StepOut");
+ return;
+ }
+ if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
+ MessageBox.Show(errorCannotStepNoActiveFunction, "StepOut");
+ } else {
+ debuggedProcess.SelectedStackFrame.AsyncStepOut();
+ }
+ }
+
+ public event EventHandler DebugStarting;
+ public event EventHandler DebugStarted;
+ public event EventHandler DebugStopped;
+ public event EventHandler IsProcessRunningChanged;
+
+ protected virtual void OnIsProcessRunningChanged(EventArgs e)
+ {
+ if (IsProcessRunningChanged != null) {
+ IsProcessRunningChanged(this, e);
+ }
+ }
+
+ ///
+ /// Gets variable of given name.
+ /// Returns null if unsuccessful. Can throw GetValueException.
+ /// Thrown when evaluation fails. Exception message explains reason.
+ ///
+ public Value GetValueFromName(string variableName)
+ {
+ if (!CanEvaluate) {
+ return null;
+ }
+ return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, debuggedProcess.SelectedStackFrame);
+ }
+
+ ///
+ /// Gets Expression for given variable. Can throw GetValueException.
+ /// Thrown when getting expression fails. Exception message explains reason.
+ ///
+ public ICSharpCode.NRefactory.Ast.Expression GetExpression(string variableName)
+ {
+ if (!CanEvaluate) {
+ throw new GetValueException("Cannot evaluate now - debugged process is either null or running or has no selected stack frame");
+ }
+ return ExpressionEvaluator.ParseExpression(variableName, SupportedLanguage.CSharp);
+ }
+
+ public bool IsManaged(int processId)
+ {
+ corPublish = new CorpubPublishClass();
+ CorDbg.Interop.TrackedComObjects.Track(corPublish);
+
+ ICorPublishProcess process = corPublish.GetProcess((uint)processId);
+ if (process != null) {
+ return process.IsManaged() != 0;
+ }
+ return false;
+ }
+
+ ///
+ /// Gets the current value of the variable as string that can be displayed in tooltips.
+ /// Returns null if unsuccessful.
+ ///
+ public string GetValueAsString(string variableName)
+ {
+ try {
+ Value val = GetValueFromName(variableName);
+ if (val == null) return null;
+ return val.AsString();
+ } catch (GetValueException) {
+ return null;
+ }
+ }
+
+ bool CanEvaluate
+ {
+ get {
+ return debuggedProcess != null && !debuggedProcess.IsRunning && debuggedProcess.SelectedStackFrame != null;
+ }
+ }
+
+ ///
+ /// Gets the tooltip control that shows the value of given variable.
+ /// Return null if no tooltip is available.
+ ///
+ public object GetTooltipControl(Location logicalPosition, string variableName)
+ {
+ try {
+ var tooltipExpression = GetExpression(variableName);
+ string imageName;
+ var image = ExpressionNode.GetImageForLocalVariable(out imageName);
+ ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression);
+ expressionNode.ImageName = imageName;
+ return null;
+ // return new DebuggerTooltipControl(logicalPosition, expressionNode);
+ } catch (GetValueException) {
+ return null;
+ }
+ }
+
+ public ITreeNode GetNode(string variable, string currentImageName = null)
+ {
+ try {
+ var expression = GetExpression(variable);
+ string imageName;
+ ImageSource image;
+ if (string.IsNullOrEmpty(currentImageName)) {
+ image = ExpressionNode.GetImageForLocalVariable(out imageName);
+ }
+ else {
+ image = ImageService.GetImage(currentImageName);
+ imageName = currentImageName;
+ }
+ ExpressionNode expressionNode = new ExpressionNode(image, variable, expression);
+ expressionNode.ImageName = imageName;
+ return expressionNode;
+ } catch (GetValueException) {
+ return null;
+ }
+ }
+
+ public bool CanSetInstructionPointer(string filename, int line, int column)
+ {
+ if (debuggedProcess != null && debuggedProcess.IsPaused && debuggedProcess.SelectedStackFrame != null) {
+ SourcecodeSegment seg = debuggedProcess.SelectedStackFrame.CanSetIP(filename, line, column);
+ return seg != null;
+ } else {
+ return false;
+ }
+ }
+
+ public bool SetInstructionPointer(string filename, int line, int column)
+ {
+ if (CanSetInstructionPointer(filename, line, column)) {
+ SourcecodeSegment seg = debuggedProcess.SelectedStackFrame.SetIP(filename, line, column);
+ return seg != null;
+ } else {
+ return false;
+ }
+ }
+
+ public void Dispose()
+ {
+ Stop();
+ }
+
+ #endregion
+
+ public event EventHandler Initialize;
+
+ public void InitializeService()
+ {
+ if (useRemotingForThreadInterop) {
+ // This needs to be called before instance of NDebugger is created
+ string path = RemotingConfigurationHelpper.GetLoadedAssemblyPath("Debugger.Core.dll");
+ new RemotingConfigurationHelpper(path).Configure();
+ }
+
+ debugger = new NDebugger();
+
+ //debugger.Options = DebuggingOptions.Instance;
+
+ debugger.DebuggerTraceMessage += debugger_TraceMessage;
+ debugger.Processes.Added += debugger_ProcessStarted;
+ debugger.Processes.Removed += debugger_ProcessExited;
+
+ DebuggerService.BreakPointAdded += delegate (object sender, BreakpointBookmarkEventArgs e) {
+ AddBreakpoint(e.BreakpointBookmark);
+ };
+
+ foreach (BreakpointBookmark b in DebuggerService.Breakpoints) {
+ AddBreakpoint(b);
+ }
+
+ if (Initialize != null) {
+ Initialize(this, null);
+ }
+ }
+
+ bool Compare(byte[] a, byte[] b)
+ {
+ if (a.Length != b.Length) return false;
+ for(int i = 0; i < a.Length; i++) {
+ if (a[i] != b[i]) return false;
+ }
+ return true;
+ }
+
+ void AddBreakpoint(BreakpointBookmark bookmark)
+ {
+ Breakpoint breakpoint = debugger.Breakpoints.Add(bookmark.TypeName, null, bookmark.LineNumber, 0, bookmark.IsEnabled);
+// Action setBookmarkColor = delegate {
+// if (debugger.Processes.Count == 0) {
+// bookmark.IsHealthy = true;
+// bookmark.Tooltip = null;
+// } else if (!breakpoint.IsSet) {
+// bookmark.IsHealthy = false;
+// bookmark.Tooltip = "Breakpoint was not found in any loaded modules";
+// } else if (breakpoint.OriginalLocation.CheckSum == null) {
+// bookmark.IsHealthy = true;
+// bookmark.Tooltip = null;
+// } else {
+// byte[] fileMD5;
+// IEditable file = FileService.GetOpenFile(bookmark.FileName) as IEditable;
+// if (file != null) {
+// byte[] fileContent = Encoding.UTF8.GetBytesWithPreamble(file.Text);
+// fileMD5 = new MD5CryptoServiceProvider().ComputeHash(fileContent);
+// } else {
+// fileMD5 = new MD5CryptoServiceProvider().ComputeHash(File.ReadAllBytes(bookmark.FileName));
+// }
+// if (Compare(fileMD5, breakpoint.OriginalLocation.CheckSum)) {
+// bookmark.IsHealthy = true;
+// bookmark.Tooltip = null;
+// } else {
+// bookmark.IsHealthy = false;
+// bookmark.Tooltip = "Check sum or file does not match to the original";
+// }
+// }
+// };
+
+ // event handlers on bookmark and breakpoint don't need deregistration
+ bookmark.IsEnabledChanged += delegate {
+ breakpoint.Enabled = bookmark.IsEnabled;
+ };
+ breakpoint.Set += delegate {
+ //setBookmarkColor();
+ };
+
+ //setBookmarkColor();
+
+ EventHandler> bp_debugger_ProcessStarted = (sender, e) => {
+ //setBookmarkColor();
+ // User can change line number by inserting or deleting lines
+ breakpoint.Line = bookmark.LineNumber;
+ };
+ EventHandler> bp_debugger_ProcessExited = (sender, e) => {
+ //setBookmarkColor();
+ };
+
+ EventHandler bp_debugger_BreakpointHit =
+ new EventHandler(
+ delegate(object sender, BreakpointEventArgs e)
+ {
+ //LoggingService.Debug(bookmark.Action + " " + bookmark.ScriptLanguage + " " + bookmark.Condition);
+
+ switch (bookmark.Action) {
+ case BreakpointAction.Break:
+ break;
+ case BreakpointAction.Condition:
+// if (Evaluate(bookmark.Condition, bookmark.ScriptLanguage))
+// DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAtBecause}") + "\n", bookmark.LineNumber, bookmark.FileName, bookmark.Condition));
+// else
+// this.debuggedProcess.AsyncContinue();
+ break;
+ case BreakpointAction.Trace:
+ //DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAt}") + "\n", bookmark.LineNumber, bookmark.FileName));
+ break;
+ }
+ });
+
+ BookmarkEventHandler bp_bookmarkManager_Removed = null;
+ bp_bookmarkManager_Removed = (sender, e) => {
+ if (bookmark == e.Bookmark) {
+ debugger.Breakpoints.Remove(breakpoint);
+
+ // unregister the events
+ debugger.Processes.Added -= bp_debugger_ProcessStarted;
+ debugger.Processes.Removed -= bp_debugger_ProcessExited;
+ breakpoint.Hit -= bp_debugger_BreakpointHit;
+ BookmarkManager.Removed -= bp_bookmarkManager_Removed;
+ }
+ };
+ // register the events
+ debugger.Processes.Added += bp_debugger_ProcessStarted;
+ debugger.Processes.Removed += bp_debugger_ProcessExited;
+ breakpoint.Hit += bp_debugger_BreakpointHit;
+ BookmarkManager.Removed += bp_bookmarkManager_Removed;
+ }
+
+ bool Evaluate(string code, string language)
+ {
+ try {
+ SupportedLanguage supportedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), language, true);
+ Value val = ExpressionEvaluator.Evaluate(code, supportedLanguage, debuggedProcess.SelectedStackFrame);
+
+ if (val != null && val.Type.IsPrimitive && val.PrimitiveValue is bool)
+ return (bool)val.PrimitiveValue;
+ else
+ return false;
+ } catch (GetValueException e) {
+ string errorMessage = "Error while evaluating breakpoint condition " + code + ":\n" + e.Message + "\n";
+ //DebuggerService.PrintDebugMessage(errorMessage);
+ //WorkbenchSingleton.SafeThreadAsyncCall(MessageService.ShowWarning, errorMessage);
+ return true;
+ }
+ }
+
+ void LogMessage(object sender, MessageEventArgs e)
+ {
+ //DebuggerService.PrintDebugMessage(e.Message);
+ }
+
+ void debugger_TraceMessage(object sender, MessageEventArgs e)
+ {
+ //LoggingService.Debug("Debugger: " + e.Message);
+ }
+
+ void debugger_ProcessStarted(object sender, CollectionItemEventArgs e)
+ {
+ if (debugger.Processes.Count == 1) {
+ if (DebugStarted != null) {
+ DebugStarted(this, EventArgs.Empty);
+ }
+ }
+ e.Item.LogMessage += LogMessage;
+ }
+
+ void debugger_ProcessExited(object sender, CollectionItemEventArgs e)
+ {
+ if (debugger.Processes.Count == 0) {
+ if (DebugStopped != null) {
+ DebugStopped(this, e);
+ }
+ SelectProcess(null);
+ } else {
+ SelectProcess(debugger.Processes[0]);
+ }
+ }
+
+ public void SelectProcess(Process process)
+ {
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused -= debuggedProcess_DebuggingPaused;
+ debuggedProcess.ExceptionThrown -= debuggedProcess_ExceptionThrown;
+ debuggedProcess.Resumed -= debuggedProcess_DebuggingResumed;
+ }
+ debuggedProcess = process;
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused += debuggedProcess_DebuggingPaused;
+ debuggedProcess.ExceptionThrown += debuggedProcess_ExceptionThrown;
+ debuggedProcess.Resumed += debuggedProcess_DebuggingResumed;
+
+ debuggedProcess.BreakAtBeginning = BreakAtBeginning;
+ }
+ // reset
+ BreakAtBeginning = false;
+
+ JumpToCurrentLine();
+ OnProcessSelected(new ProcessEventArgs(process));
+ }
+
+ void debuggedProcess_DebuggingPaused(object sender, ProcessEventArgs e)
+ {
+ OnIsProcessRunningChanged(EventArgs.Empty);
+
+ //using(new PrintTimes("Jump to current line")) {
+ JumpToCurrentLine();
+ //}
+ // TODO update tooltip
+ /*if (currentTooltipRow != null && currentTooltipRow.IsShown) {
+ using(new PrintTimes("Update tooltip")) {
+ try {
+ Utils.DoEvents(debuggedProcess);
+ AbstractNode updatedNode = ValueNode.Create(currentTooltipExpression);
+ currentTooltipRow.SetContentRecursive(updatedNode);
+ } catch (AbortedBecauseDebuggeeResumedException) {
+ }
+ }
+ }*/
+ }
+
+ void debuggedProcess_DebuggingResumed(object sender, CorDbg.ProcessEventArgs e)
+ {
+ OnIsProcessRunningChanged(EventArgs.Empty);
+ DebuggerService.RemoveCurrentLineMarker();
+ }
+
+ void debuggedProcess_ExceptionThrown(object sender, CorDbg.ExceptionEventArgs e)
+ {
+ if (!e.IsUnhandled) {
+ // Ignore the exception
+ e.Process.AsyncContinue();
+ return;
+ }
+
+ JumpToCurrentLine();
+
+ StringBuilder stacktraceBuilder = new StringBuilder();
+
+ // Need to intercept now so that we can evaluate properties
+ if (e.Process.SelectedThread.InterceptCurrentException()) {
+ stacktraceBuilder.AppendLine(e.Exception.ToString());
+ string stackTrace;
+ try {
+ stackTrace = e.Exception.GetStackTrace("--- End of inner exception stack trace ---");
+ } catch (GetValueException) {
+ stackTrace = e.Process.SelectedThread.GetStackTrace("at {0} in {1}:line {2}", "at {0}");
+ }
+ stacktraceBuilder.Append(stackTrace);
+ } else {
+ // For example, happens on stack overflow
+ stacktraceBuilder.AppendLine("CannotInterceptException");
+ stacktraceBuilder.AppendLine(e.Exception.ToString());
+ stacktraceBuilder.Append(e.Process.SelectedThread.GetStackTrace("at {0} in {1}:line {2}", "at {0}"));
+ }
+
+ string title = e.IsUnhandled ? "Unhandled" : "Handled";
+ string message = string.Format("Message {0} {1}", e.Exception.Type, e.Exception.Message);
+
+ MessageBox.Show(message + stacktraceBuilder.ToString(), title);
+ }
+
+ public void JumpToCurrentLine()
+ {
+ DebuggerService.RemoveCurrentLineMarker();
+ if (debuggedProcess != null) {
+ SourcecodeSegment nextStatement = debuggedProcess.NextStatement;
+ if (nextStatement != null) {
+ DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
+ }
+ }
+ }
+
+ public void ShowAttachDialog()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Debugger/ILSpy.Debugger/Services/ImageService/ImageService.cs b/Debugger/ILSpy.Debugger/Services/ImageService/ImageService.cs
index 585e3f4b4..cb755c3f1 100644
--- a/Debugger/ILSpy.Debugger/Services/ImageService/ImageService.cs
+++ b/Debugger/ILSpy.Debugger/Services/ImageService/ImageService.cs
@@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
+using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ILSpy.Debugger.Services
@@ -32,5 +33,10 @@ namespace ILSpy.Debugger.Services
public static readonly BitmapImage Breakpoint = LoadBitmap("Breakpoint");
public static readonly BitmapImage CurrentLine = LoadBitmap("CurrentLine");
+
+ public static ImageSource GetImage(string imageName)
+ {
+ return LoadBitmap(imageName);
+ }
}
}
diff --git a/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs b/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs
index 2e23d2f32..d7c3d8cc5 100644
--- a/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs
+++ b/Debugger/ILSpy.Debugger/UI/AttachToProcessWindow.xaml.cs
@@ -25,6 +25,7 @@ using System.Linq;
using System.Windows;
using ILSpy.Debugger.Models;
+using ILSpy.Debugger.Services;
namespace ILSpy.Debugger.UI
{
@@ -33,6 +34,13 @@ namespace ILSpy.Debugger.UI
///
public partial class AttachToProcessWindow : Window
{
+ public static IDebugger Debugger { get; private set; }
+
+ static AttachToProcessWindow()
+ {
+ Debugger = new WindowsDebugger();
+ }
+
public AttachToProcessWindow()
{
InitializeComponent();
@@ -89,6 +97,8 @@ namespace ILSpy.Debugger.UI
// start attaching
var process = ((RunningProcess)this.RunningProcesses.SelectedItem).Process;
+ Debugger.Attach(process);
+ this.DialogResult = true;
}
void CancelButton_Click(object sender, RoutedEventArgs e)
diff --git a/ILSpy/Commands/RoutedUICommands.cs b/ILSpy/Commands/RoutedUICommands.cs
index 38886eb0b..91b431c09 100644
--- a/ILSpy/Commands/RoutedUICommands.cs
+++ b/ILSpy/Commands/RoutedUICommands.cs
@@ -26,8 +26,11 @@ namespace ICSharpCode.ILSpy.Commands
static RoutedUICommands()
{
AttachToProcess = new RoutedUICommand("Attach to running process...", "AttachToProcess", typeof(RoutedUICommands));
+ DetachFromProcess = new RoutedUICommand("Detach from process...", "DetachFromProcess", typeof(RoutedUICommands));
}
public static RoutedUICommand AttachToProcess { get; private set; }
+
+ public static RoutedUICommand DetachFromProcess { get; private set; }
}
}
diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml
index be979225a..ed4fcfe40 100644
--- a/ILSpy/MainWindow.xaml
+++ b/ILSpy/MainWindow.xaml
@@ -24,6 +24,9 @@
+
@@ -63,11 +66,12 @@