diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..fbfb41be87 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.cs text diff=csharp +*.sln text eol=crlf +*.csproj text eol=crlf diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs index c6b85ebf0c..3e037502bc 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/ParallelStacksViewCommands.cs @@ -1,82 +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.Windows.Controls; -using ICSharpCode.Core; - -namespace ICSharpCode.SharpDevelop.Gui.Pads -{ - public sealed class ShowZoomControlCommand : AbstractCheckableMenuCommand - { - ParallelStackPad pad; - - public override object Owner { - get { return base.Owner; } - set { - if (!(value is ParallelStackPad)) - throw new Exception("Owner has to be a ParallelStackPad"); - pad = value as ParallelStackPad; - base.Owner = value; - } - } - - public override bool IsChecked { - get { return pad.IsZoomControlVisible; } - set { pad.IsZoomControlVisible = value; } - } - } - - public sealed class ToggleMethodViewCommand : AbstractCheckableMenuCommand - { - ParallelStackPad pad; - - public override object Owner { - get { return base.Owner; } - set { - if (!(value is ParallelStackPad)) - throw new Exception("Owner has to be a AbstractConsolePad"); - pad = value as ParallelStackPad; - base.Owner = value; - } - } - - public override bool IsChecked { - get { return pad.IsMethodView; } - set { pad.IsMethodView = value; } - } - } - - public sealed class ParallelStacksViewCommand : AbstractComboBoxCommand - { - ParallelStackPad pad; - ComboBox box; - - protected override void OnOwnerChanged(EventArgs e) - { - this.pad = this.Owner as ParallelStackPad; - if (this.pad == null) - return; - - box = this.ComboBox as ComboBox; - - if (this.box == null) - return; - - foreach (var name in Enum.GetNames(typeof(ParallelStacksView))) - box.Items.Add(name); - - box.SelectedIndex = 0; - - base.OnOwnerChanged(e); - } - - public override void Run() - { - if (this.pad != null && this.box != null) { - pad.ParallelStacksView = (ParallelStacksView)Enum.Parse(typeof(ParallelStacksView), box.SelectedValue.ToString()); - } - base.Run(); - } - } +// 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.Controls; +using ICSharpCode.Core; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + public sealed class ShowZoomControlCommand : AbstractCheckableMenuCommand + { + ParallelStackPad pad; + + public override object Owner { + get { return base.Owner; } + set { + if (!(value is ParallelStackPad)) + throw new Exception("Owner has to be a ParallelStackPad"); + pad = value as ParallelStackPad; + base.Owner = value; + } + } + + public override bool IsChecked { + get { return pad.IsZoomControlVisible; } + set { pad.IsZoomControlVisible = value; } + } + } + + public sealed class ToggleMethodViewCommand : AbstractCheckableMenuCommand + { + ParallelStackPad pad; + + public override object Owner { + get { return base.Owner; } + set { + if (!(value is ParallelStackPad)) + throw new Exception("Owner has to be a AbstractConsolePad"); + pad = value as ParallelStackPad; + base.Owner = value; + } + } + + public override bool IsChecked { + get { return pad.IsMethodView; } + set { pad.IsMethodView = value; } + } + } + + public sealed class ParallelStacksViewCommand : AbstractComboBoxCommand + { + ParallelStackPad pad; + ComboBox box; + + protected override void OnOwnerChanged(EventArgs e) + { + this.pad = this.Owner as ParallelStackPad; + if (this.pad == null) + return; + + box = this.ComboBox as ComboBox; + + if (this.box == null) + return; + + foreach (var name in Enum.GetNames(typeof(ParallelStacksView))) + box.Items.Add(name); + + box.SelectedIndex = 0; + + base.OnOwnerChanged(e); + } + + public override void Run() + { + if (this.pad != null && this.box != null) { + pad.ParallelStacksView = (ParallelStacksView)Enum.Parse(typeof(ParallelStacksView), box.SelectedValue.ToString()); + } + base.Run(); + } + } } \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs index b117571b31..8cddba8ae9 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/SelectLanguageCommand.cs @@ -1,47 +1,47 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) - -using System; -using Debugger; -using Debugger.AddIn; -using ICSharpCode.Core; -using ICSharpCode.NRefactory; -using ICSharpCode.SharpDevelop.Debugging; -using ICSharpCode.SharpDevelop.Services; -using System.Windows.Controls; - -namespace ICSharpCode.SharpDevelop.Gui.Pads -{ - class SelectLanguageCommand : AbstractComboBoxCommand - { - ConsolePad pad; - ComboBox box; - - protected override void OnOwnerChanged(EventArgs e) - { - this.pad = this.Owner as ConsolePad; - if (this.pad == null) - return; - - box = this.ComboBox as ComboBox; - - if (this.box == null) - return; - - foreach (var name in Enum.GetNames(typeof(SupportedLanguage))) - box.Items.Add(name); - - box.SelectedIndex = 0; - - base.OnOwnerChanged(e); - } - - public override void Run() - { - if (this.pad != null && this.box != null) { - pad.SelectedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), box.SelectedValue.ToString()); - } - base.Run(); - } - } -} +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) + +using System; +using Debugger; +using Debugger.AddIn; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Services; +using System.Windows.Controls; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + class SelectLanguageCommand : AbstractComboBoxCommand + { + ConsolePad pad; + ComboBox box; + + protected override void OnOwnerChanged(EventArgs e) + { + this.pad = this.Owner as ConsolePad; + if (this.pad == null) + return; + + box = this.ComboBox as ComboBox; + + if (this.box == null) + return; + + foreach (var name in Enum.GetNames(typeof(SupportedLanguage))) + box.Items.Add(name); + + box.SelectedIndex = 0; + + base.OnOwnerChanged(e); + } + + public override void Run() + { + if (this.pad != null && this.box != null) { + pad.SelectedLanguage = (SupportedLanguage)Enum.Parse(typeof(SupportedLanguage), box.SelectedValue.ToString()); + } + base.Run(); + } + } +} diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs index 9e712e5176..01afc5418c 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/DebuggerPad.cs @@ -1,85 +1,85 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) - -using System.Windows.Controls; -using Debugger; -using ICSharpCode.SharpDevelop.Debugging; -using ICSharpCode.SharpDevelop.Services; - -namespace ICSharpCode.SharpDevelop.Gui.Pads -{ - public abstract class DebuggerPad : AbstractPadContent - { - protected DockPanel panel; - ToolBar toolbar; - protected WindowsDebugger debugger; - - public override object Control { - get { - return panel; - } - } - - public DebuggerPad() - { - // UI - this.panel = new DockPanel(); - this.toolbar = BuildToolBar(); - - if (this.toolbar != null) { - this.toolbar.SetValue(DockPanel.DockProperty, Dock.Top); - - this.panel.Children.Add(toolbar); - } - - // logic - debugger = (WindowsDebugger)DebuggerService.CurrentDebugger; - - InitializeComponents(); - - debugger.ProcessSelected += delegate(object sender, ProcessEventArgs e) { - SelectProcess(e.Process); - }; - SelectProcess(debugger.DebuggedProcess); - } - - protected virtual void InitializeComponents() - { - - } - - protected virtual void SelectProcess(Process process) - { - - } - - /// - /// Never call this directly. Always use InvalidatePad() - /// - protected virtual void RefreshPad() - { - - } - - bool invalidatePadEnqueued; - - public void InvalidatePad() - { - WorkbenchSingleton.AssertMainThread(); - if (invalidatePadEnqueued || WorkbenchSingleton.Workbench == null) - return; - invalidatePadEnqueued = true; - WorkbenchSingleton.SafeThreadAsyncCall( - delegate { - invalidatePadEnqueued = false; - RefreshPad(); - }); - - } - - protected virtual ToolBar BuildToolBar() - { - return null; - } - } -} +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) + +using System.Windows.Controls; +using Debugger; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Services; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + public abstract class DebuggerPad : AbstractPadContent + { + protected DockPanel panel; + ToolBar toolbar; + protected WindowsDebugger debugger; + + public override object Control { + get { + return panel; + } + } + + public DebuggerPad() + { + // UI + this.panel = new DockPanel(); + this.toolbar = BuildToolBar(); + + if (this.toolbar != null) { + this.toolbar.SetValue(DockPanel.DockProperty, Dock.Top); + + this.panel.Children.Add(toolbar); + } + + // logic + debugger = (WindowsDebugger)DebuggerService.CurrentDebugger; + + InitializeComponents(); + + debugger.ProcessSelected += delegate(object sender, ProcessEventArgs e) { + SelectProcess(e.Process); + }; + SelectProcess(debugger.DebuggedProcess); + } + + protected virtual void InitializeComponents() + { + + } + + protected virtual void SelectProcess(Process process) + { + + } + + /// + /// Never call this directly. Always use InvalidatePad() + /// + protected virtual void RefreshPad() + { + + } + + bool invalidatePadEnqueued; + + public void InvalidatePad() + { + WorkbenchSingleton.AssertMainThread(); + if (invalidatePadEnqueued || WorkbenchSingleton.Workbench == null) + return; + invalidatePadEnqueued = true; + WorkbenchSingleton.SafeThreadAsyncCall( + delegate { + invalidatePadEnqueued = false; + RefreshPad(); + }); + + } + + protected virtual ToolBar BuildToolBar() + { + return null; + } + } +} diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs index 1617201833..e220e6f336 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ObjectGraphPad.cs @@ -1,72 +1,72 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) - -using ICSharpCode.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using Debugger; -using Debugger.AddIn.Visualizers.Graph; - -namespace ICSharpCode.SharpDevelop.Gui.Pads -{ - /// - /// Description of ObjectGraphPad. - /// - public class ObjectGraphPad : DebuggerPad - { - Process debuggedProcess; - ObjectGraphControl objectGraphControl; - static ObjectGraphPad instance; - - public ObjectGraphPad() - { - instance = this; - } - - /// Always check if Instance is null, might be null if pad is not opened! - public static ObjectGraphPad Instance { - get { return instance; } - } - - protected override void InitializeComponents() - { - objectGraphControl = new ObjectGraphControl(); - panel.Children.Add(objectGraphControl); - } - - - protected override void RefreshPad() - { - // BUG: if pad window is undocked and floats standalone, IsVisible == false (so pad won't refresh) - // REQUEST: need to refresh when pad becomes visible -> VisibleChanged event? - if (!objectGraphControl.IsVisible) - { - return; - } - if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedStackFrame == null) { - this.objectGraphControl.Clear(); - return; - } - this.objectGraphControl.RefreshView(); - } - - protected override void SelectProcess(Process process) - { - if (debuggedProcess != null) { - debuggedProcess.Paused -= debuggedProcess_Paused; - } - debuggedProcess = process; - if (debuggedProcess != null) { - debuggedProcess.Paused += debuggedProcess_Paused; - } - InvalidatePad(); - } - - void debuggedProcess_Paused(object sender, ProcessEventArgs e) - { - InvalidatePad(); - } - } -} +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) + +using ICSharpCode.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using Debugger; +using Debugger.AddIn.Visualizers.Graph; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + /// + /// Description of ObjectGraphPad. + /// + public class ObjectGraphPad : DebuggerPad + { + Process debuggedProcess; + ObjectGraphControl objectGraphControl; + static ObjectGraphPad instance; + + public ObjectGraphPad() + { + instance = this; + } + + /// Always check if Instance is null, might be null if pad is not opened! + public static ObjectGraphPad Instance { + get { return instance; } + } + + protected override void InitializeComponents() + { + objectGraphControl = new ObjectGraphControl(); + panel.Children.Add(objectGraphControl); + } + + + protected override void RefreshPad() + { + // BUG: if pad window is undocked and floats standalone, IsVisible == false (so pad won't refresh) + // REQUEST: need to refresh when pad becomes visible -> VisibleChanged event? + if (!objectGraphControl.IsVisible) + { + return; + } + if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedStackFrame == null) { + this.objectGraphControl.Clear(); + return; + } + this.objectGraphControl.RefreshView(); + } + + protected override void SelectProcess(Process process) + { + if (debuggedProcess != null) { + debuggedProcess.Paused -= debuggedProcess_Paused; + } + debuggedProcess = process; + if (debuggedProcess != null) { + debuggedProcess.Paused += debuggedProcess_Paused; + } + InvalidatePad(); + } + + void debuggedProcess_Paused(object sender, ProcessEventArgs e) + { + InvalidatePad(); + } + } +} diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs index 22b9aa63fa..8b7600186a 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/DrawSurface.xaml.cs @@ -1,71 +1,71 @@ -// 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; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Shapes; - -using Microsoft.Windows.Themes; - -namespace Debugger.AddIn.Pads.ParallelPad -{ - public partial class DrawSurface : UserControl - { - private ScaleTransform zoom = new ScaleTransform(); - - public DrawSurface() - { - InitializeComponent(); - - ContentControl.LayoutTransform = zoom; - } - - public void SetGraph(ParallelStacksGraph graph) - { - this.ParallelStacksLayout.Graph = graph; - - if (graph == null) - this.ParallelStacksLayout.CancelLayout(); - else - this.ParallelStacksLayout.Relayout(); - } - - public bool IsZoomControlVisible { - get { return ZoomControl.Visibility == Visibility.Visible; } - set { - if (value) - ZoomControl.Visibility = Visibility.Visible; - else - ZoomControl.Visibility = Visibility.Hidden; - } - } - - #region Zoom - - void SliderControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) - { - if (e.OldValue == 0) - return; - - double value = (e.NewValue / 5d) * 100; - - this.PercentText.Text = string.Format("{0}%", value); - - // zoom canvas - zoom.ScaleX = e.NewValue / 5d; - zoom.ScaleY = e.NewValue / 5d; - zoom.CenterX = drawingSurface.ActualWidth / 2d; - zoom.CenterY = drawingSurface.ActualHeight / 2d; - } - - void Reset_Click(object sender, RoutedEventArgs e) - { - this.SliderControl.Value = 5; - } - - #endregion - } +// 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; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; + +using Microsoft.Windows.Themes; + +namespace Debugger.AddIn.Pads.ParallelPad +{ + public partial class DrawSurface : UserControl + { + private ScaleTransform zoom = new ScaleTransform(); + + public DrawSurface() + { + InitializeComponent(); + + ContentControl.LayoutTransform = zoom; + } + + public void SetGraph(ParallelStacksGraph graph) + { + this.ParallelStacksLayout.Graph = graph; + + if (graph == null) + this.ParallelStacksLayout.CancelLayout(); + else + this.ParallelStacksLayout.Relayout(); + } + + public bool IsZoomControlVisible { + get { return ZoomControl.Visibility == Visibility.Visible; } + set { + if (value) + ZoomControl.Visibility = Visibility.Visible; + else + ZoomControl.Visibility = Visibility.Hidden; + } + } + + #region Zoom + + void SliderControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + if (e.OldValue == 0) + return; + + double value = (e.NewValue / 5d) * 100; + + this.PercentText.Text = string.Format("{0}%", value); + + // zoom canvas + zoom.ScaleX = e.NewValue / 5d; + zoom.ScaleY = e.NewValue / 5d; + zoom.CenterX = drawingSurface.ActualWidth / 2d; + zoom.CenterY = drawingSurface.ActualHeight / 2d; + } + + void Reset_Click(object sender, RoutedEventArgs e) + { + this.SliderControl.Value = 5; + } + + #endregion + } } \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs index 8feecd7c33..6b5c0915ac 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ParallelStackPad.cs @@ -1,674 +1,674 @@ -// 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.Collections.ObjectModel; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Threading; -using Debugger; -using Debugger.AddIn.Pads.ParallelPad; -using Debugger.AddIn.TreeModel; -using ICSharpCode.AvalonEdit.Rendering; -using ICSharpCode.Core; -using ICSharpCode.Core.Presentation; -using ICSharpCode.NRefactory; -using ICSharpCode.SharpDevelop.Bookmarks; -using ICSharpCode.SharpDevelop.Debugging; -using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Gui.Pads; - -namespace ICSharpCode.SharpDevelop.Gui.Pads -{ - public enum ParallelStacksView - { - Threads, - Tasks - } - - public class ParallelStackPad : DebuggerPad - { - DrawSurface surface; - Process debuggedProcess; - ParallelStacksGraph graph; - List currentThreadStacks = new List(); - ParallelStacksView parallelStacksView; - StackFrame selectedFrame; - bool isMethodView; - - #region Overrides - - protected override void InitializeComponents() - { - surface = new DrawSurface(); - - panel.Children.Add(surface); - } - - protected override void SelectProcess(Process process) - { - if (debuggedProcess != null) { - debuggedProcess.Paused -= OnProcessPaused; - } - debuggedProcess = process; - if (debuggedProcess != null) { - debuggedProcess.Paused += OnProcessPaused; - } - - DebuggerService.DebugStarted += OnReset; - DebuggerService.DebugStopped += OnReset; - - InvalidatePad(); - } - - protected override void RefreshPad() - { - if (debuggedProcess == null || debuggedProcess.IsRunning) { - return; - } - - LoggingService.InfoFormatted("Start refresh: {0}" + Environment.NewLine, parallelStacksView); - - currentThreadStacks.Clear(); - - using(new PrintTimes("Create stacks")) { - try { - // create all simple ThreadStacks - foreach (Thread thread in debuggedProcess.Threads) { - var t = thread; - debuggedProcess.EnqueueWork(Dispatcher.CurrentDispatcher, () => CreateThreadStack(t)); - } - } - catch(AbortedBecauseDebuggeeResumedException) { } - catch(System.Exception) { - if (debuggedProcess == null || debuggedProcess.HasExited) { - // Process unexpectedly exited - } else { - throw; - } - } - } - using(new PrintTimes("Run algorithm")) { - if (isMethodView) - { - // build method view for threads - CreateMethodViewStacks(); - } - else - { - // normal view - CreateCommonStacks(); - } - } - - using(new PrintTimes("Graph refresh")) { - // paint the ThreadStaks - graph = new ParallelStacksGraph(); - foreach (var stack in this.currentThreadStacks.FindAll(ts => ts.ThreadStackParents == null )) - { - graph.AddVertex(stack); - - // add the children - AddChildren(stack); - } - - if (graph.VertexCount > 0) - surface.SetGraph(graph); - } - } - - protected override ToolBar BuildToolBar() - { - return ToolBarService.CreateToolBar(panel, this, "/SharpDevelop/Pads/ParallelStacksPad/ToolBar"); - } - - #endregion - - #region Public Properties - - public ParallelStacksView ParallelStacksView { - get { return parallelStacksView; } - set { - parallelStacksView = value; - InvalidatePad(); - } - } - - public bool IsMethodView { - get { return isMethodView; } - set { - isMethodView = value; - InvalidatePad(); - } - } - - public bool IsZoomControlVisible { - get { return surface.IsZoomControlVisible; } - set { surface.IsZoomControlVisible = value; } - } - - #endregion - - #region Private Methods - - void OnReset(object sender, EventArgs e) - { - currentThreadStacks.Clear(); - selectedFrame = null; - - // remove all - BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark); - } - - void OnProcessPaused(object sender, ProcessEventArgs e) - { - InvalidatePad(); - } - - void AddChildren(ThreadStack parent) - { - if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Count == 0) - return; - - foreach (var ts in parent.ThreadStackChildren) - { - if (ts == null) continue; - - graph.AddVertex(ts); - graph.AddEdge(new ParallelStacksEdge(parent, ts)); - - if (ts.ThreadStackChildren == null || ts.ThreadStackChildren.Count == 0) - continue; - - AddChildren(ts); - } - } - - void CreateCommonStacks() - { - // stack.ItemCollection order - // 0 -> top of stack = S.C - // 1 -> ............ = S.B - // ....................... - // n -> bottom of stack = [External Methods] - - // ThreadStacks with common frame - var commonFrameThreads = new Dictionary>(); - - bool isOver = false; - while (true) { - - for (int i = currentThreadStacks.Count - 1; i >=0; --i) { - var stack = currentThreadStacks[i]; - if (stack.ItemCollection.Count == 0) { - currentThreadStacks.Remove(stack); - continue; - } - } - //get all thread stacks with common start frame - foreach (var stack in currentThreadStacks) { - int count = stack.ItemCollection.Count; - ParallelStackFrameModel frame = stack.ItemCollection[count - 1]; - string fullname = frame.MethodName + stack.Level.ToString(); - - if (!commonFrameThreads.ContainsKey(fullname)) - commonFrameThreads.Add(fullname, new List()); - - if (!commonFrameThreads[fullname].Contains(stack)) - commonFrameThreads[fullname].Add(stack); - } - - // for every list of common stacks, find split place and add them to currentThreadStacks - foreach (var frameName in commonFrameThreads.Keys) { - var listOfCurrentStacks = commonFrameThreads[frameName]; - - if (listOfCurrentStacks.Count == 1) // just skip the parents - { - isOver = true; // we finish when all are pseodo-parents: no more spliting - continue; - } - - isOver = false; - - // find the frameIndex where we can split - int frameIndex = 0; - string fn = string.Empty; - bool canContinue = true; - - while(canContinue) { - for (int i = 0; i < listOfCurrentStacks.Count; ++i) { - var stack = listOfCurrentStacks[i]; - if (stack.ItemCollection.Count == frameIndex) - { - canContinue = false; - break; - } - - ParallelStackFrameModel item = stack.ItemCollection[stack.ItemCollection.Count - frameIndex - 1]; - - string currentName = item.MethodName; - - if (i == 0) { - fn = currentName; - continue; - } - - if (fn == currentName) - continue; - else { - canContinue = false; - break; - } - } - if (canContinue) - frameIndex++; - } - - // remove last [frameIndex] and create a new ThreadStack as the parent of what remained in the children - var threadIds = new List(); - var parentItems = new Stack(); - - while (frameIndex > 0) { - for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) { - var stack = listOfCurrentStacks[i]; - int indexToRemove = stack.ItemCollection.Count - 1; - - #if DEBUG - ParallelStackFrameModel d_item = stack.ItemCollection[indexToRemove]; - string name = d_item.MethodName; - #endif - if (i == 0) - parentItems.Push(stack.ItemCollection[indexToRemove]); - - stack.ItemCollection.RemoveAt(indexToRemove); - } - - frameIndex--; - } - - // update thread ids - for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) - threadIds.AddRange(listOfCurrentStacks[i].ThreadIds); - - // remove stacks with no items - for (int i = listOfCurrentStacks.Count - 1; i >= 0; --i) { - var stack = listOfCurrentStacks[i]; - if (stack.ItemCollection.Count == 0) - listOfCurrentStacks.Remove(stack); - } - - // increase the Level - for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) - listOfCurrentStacks[i].Level++; - - // create new parent stack - ThreadStack commonParent = new ThreadStack(); - commonParent.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, threadIds.ToArray()); - commonParent.ItemCollection = parentItems.ToObservable(); - commonParent.Process = debuggedProcess; - commonParent.StackSelected += OnThreadStackSelected; - commonParent.FrameSelected += OnFrameSelected; - commonParent.IsSelected = commonParent.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); - // add new children - foreach (var stack in listOfCurrentStacks) { - if (stack.ItemCollection.Count == 0) - { - currentThreadStacks.Remove(stack); - continue; - } - ParallelStackFrameModel item = stack.ItemCollection[stack.ItemCollection.Count - 1]; - // add the parent to the parent - if (stack.ThreadStackParents != null) { - // remove stack from it's parent because it will have the commonParent as parent - stack.ThreadStackParents[0].ThreadStackChildren.Remove(stack); - commonParent.ThreadStackParents = stack.ThreadStackParents.Clone(); - commonParent.ThreadStackParents[0].ThreadStackChildren.Add(commonParent); - // set level - commonParent.Level = stack.Level - 1; - } - else - stack.ThreadStackParents = new List(); - - // update thread ids - if (commonParent.ThreadStackParents != null) { - commonParent.ThreadStackParents[0].UpdateThreadIds( - parallelStacksView == ParallelStacksView.Tasks, - commonParent.ThreadIds.ToArray()); - } - - stack.ThreadStackParents.Clear(); - stack.ThreadStackParents.Add(commonParent); - string currentName = item.MethodName + stack.Level.ToString();; - - // add name or add to list - if (!commonFrameThreads.ContainsKey(currentName)) { - var newList = new List(); - newList.Add(stack); - commonFrameThreads.Add(currentName, newList); - } - else { - var list = commonFrameThreads[currentName]; - list.Add(stack); - } - } - - commonParent.ThreadStackChildren = listOfCurrentStacks.Clone(); - commonFrameThreads[frameName].Clear(); - commonFrameThreads[frameName].Add(commonParent); - currentThreadStacks.Add(commonParent); - - // exit and retry - break; - } - - if (isOver || currentThreadStacks.Count == 0) - break; - } - } - - void CreateMethodViewStacks() - { - var list = new List, ObservableCollection, List>>(); - - // find all threadstacks that contains the selected frame - for (int i = currentThreadStacks.Count - 1; i >= 0; --i) { - var tuple = currentThreadStacks[i].ItemCollection.SplitStack(selectedFrame, currentThreadStacks[i].ThreadIds); - if (tuple != null) - list.Add(tuple); - } - - currentThreadStacks.Clear(); - - // common - ThreadStack common = new ThreadStack(); - var observ = new ObservableCollection(); - bool dummy = false; - ParallelStackFrameModel obj = CreateItemForFrame(selectedFrame, ref dummy); - obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; - observ.Add(obj); - common.ItemCollection = observ; - common.StackSelected += OnThreadStackSelected; - common.FrameSelected += OnFrameSelected; - common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, selectedFrame.Thread.ID); - common.Process = debuggedProcess; - common.ThreadStackChildren = new List(); - common.ThreadStackParents = new List(); - - // for all thread stacks, split them in 2 : - // one that invokes the method frame, second that get's invoked by current method frame - foreach (var tuple in list) { - // add top - if (tuple.Item1.Count > 0) - { - ThreadStack topStack = new ThreadStack(); - topStack.ItemCollection = tuple.Item1; - topStack.StackSelected += OnThreadStackSelected; - topStack.FrameSelected += OnFrameSelected; - topStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); - topStack.Process = debuggedProcess; - topStack.ThreadStackParents = new List(); - topStack.ThreadStackParents.Add(common); - - currentThreadStacks.Add(topStack); - common.ThreadStackChildren.Add(topStack); - } - - // add bottom - if(tuple.Item2.Count > 0) - { - ThreadStack bottomStack = new ThreadStack(); - bottomStack.ItemCollection = tuple.Item2; - bottomStack.StackSelected += OnThreadStackSelected; - bottomStack.FrameSelected += OnFrameSelected; - bottomStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); - bottomStack.Process = debuggedProcess; - bottomStack.ThreadStackChildren = new List(); - bottomStack.ThreadStackChildren.Add(common); - common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); - common.ThreadStackParents.Add(bottomStack); - currentThreadStacks.Add(bottomStack); - } - } - - currentThreadStacks.Add(common); - common.IsSelected = true; - } - - void CreateThreadStack(Thread thread) - { - var items = CreateItems(thread); - if (items == null || items.Count == 0) - return; - - ThreadStack threadStack = new ThreadStack(); - threadStack.StackSelected += OnThreadStackSelected; - threadStack.FrameSelected += OnFrameSelected; - threadStack.Process = debuggedProcess; - threadStack.ItemCollection = items; - threadStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, thread.ID); - - if (debuggedProcess.SelectedThread != null) { - threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); - if (selectedFrame == null) - selectedFrame = debuggedProcess.SelectedStackFrame; - } - - currentThreadStacks.Add(threadStack); - } - - ObservableCollection CreateItems(Thread thread) - { - bool lastItemIsExternalMethod = false; - int noTasks = 0; - var result = new ObservableCollection(); - var callstack = thread.GetCallstack(100); - - if (parallelStacksView == ParallelStacksView.Threads) { - foreach (StackFrame frame in callstack) { - ParallelStackFrameModel obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod); - - if (obj != null) - result.Add(obj); - } - } else { - for (int i = 0 ; i < callstack.Length; ++i) { - StackFrame frame = callstack[i]; - ParallelStackFrameModel obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod); - - if (frame.MethodInfo.FullName.IndexOf("System.Threading.Tasks.Task.ExecuteEntry") != -1) { - noTasks++; - } - - if (noTasks == 1) { - if (frame.HasSymbols) { - // create thread stack for the items collected until now - ThreadStack threadStack = new ThreadStack(); - threadStack.StackSelected += OnThreadStackSelected; - threadStack.FrameSelected += OnFrameSelected; - threadStack.Process = debuggedProcess; - threadStack.ItemCollection = result.Clone(); - threadStack.UpdateThreadIds(true, frame.Thread.ID); - - if (debuggedProcess.SelectedThread != null) { - threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); - if (selectedFrame == null) - selectedFrame = debuggedProcess.SelectedStackFrame; - } - - currentThreadStacks.Add(threadStack); - // reset - result.Clear(); - noTasks = 0; - } - } - - if (obj != null) - result.Add(obj); - } - - // return null if we are dealing with a simple thread - return noTasks == 0 ? null : result; - } - - return result; - } - - ParallelStackFrameModel CreateItemForFrame(StackFrame frame, ref bool lastItemIsExternalMethod) - { - ParallelStackFrameModel model = new ParallelStackFrameModel(); - string fullName; - if (frame.HasSymbols) { - // Show the method in the list - fullName = frame.GetMethodName(); - lastItemIsExternalMethod = false; - model.FontWeight = FontWeights.Normal; - model.Foreground = Brushes.Black; - } else { - // Show [External methods] in the list - if (lastItemIsExternalMethod) return null; - fullName = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods").Trim(); - model.FontWeight = FontWeights.Normal; - model.Foreground = Brushes.Gray; - lastItemIsExternalMethod = true; - } - - if (frame.Thread.SelectedStackFrame != null && - frame.Thread.ID == debuggedProcess.SelectedThread.ID && - frame.Thread.SelectedStackFrame.IP == frame.IP && - frame.Thread.SelectedStackFrame.GetMethodName() == frame.GetMethodName()) { - model.Image = PresentationResourceService.GetImage("Bookmarks.CurrentLine").Source; - model.IsRunningStackFrame = true; - } else { - if (selectedFrame != null && frame.Thread.ID == selectedFrame.Thread.ID && - frame.GetMethodName() == selectedFrame.GetMethodName()) - model.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; - else - model.Image = null; - model.IsRunningStackFrame = false; - } - - model.MethodName = fullName; - - return model; - } - - void ToggleSelectedFrameBookmark(Location location) - { - // remove all - BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark); - - ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider; - if (provider != null) { - ITextEditor editor = provider.TextEditor; - BookmarkManager.AddMark(new SelectedFrameBookmark(editor.FileName, location)); - } - } - - void OnThreadStackSelected(object sender, EventArgs e) - { - foreach (var ts in this.currentThreadStacks) { - if (ts.IsSelected) - ts.IsSelected = false; - ts.ClearImages(); - } - } - - void OnFrameSelected(object sender, FrameSelectedEventArgs e) - { - selectedFrame = e.Item; - - ToggleSelectedFrameBookmark(e.Location); - - if (isMethodView) - InvalidatePad(); - } - - #endregion - } - - static class StackFrameExtensions - { - internal static string GetMethodName(this StackFrame frame) - { - if (frame == null) - return null; - - StringBuilder name = new StringBuilder(); - name.Append(frame.MethodInfo.DeclaringType.FullName); - name.Append('.'); - name.Append(frame.MethodInfo.Name); - - return name.ToString(); - } - } - - static class ParallelStackExtensions - { - internal static List Clone(this List listToClone) - { - if (listToClone == null) - return null; - - var result = new List(); - foreach (var item in listToClone) - result.Add(item); - - return result; - } - - internal static ObservableCollection Clone(this ObservableCollection collectionToClone) - { - if (collectionToClone == null) - return null; - - var result = new ObservableCollection(); - foreach (var item in collectionToClone) - result.Add(item); - - return result; - } - - internal static ObservableCollection ToObservable(this Stack stack) - { - if (stack == null) - throw new NullReferenceException("Stack is null!"); - - var result = new ObservableCollection(); - while (stack.Count > 0) - result.Add(stack.Pop()); - - return result; - } - - internal static Tuple, ObservableCollection, List> - SplitStack(this ObservableCollection source, StackFrame frame, List threadIds) - { - var bottom = new ObservableCollection(); - var top = new ObservableCollection(); - - int found = 0; - - foreach (dynamic item in source) { - if (item.MethodName == frame.GetMethodName()) - found = 1; - - if (found >= 1) { - if(found > 1) - bottom.Add(item); - - found++; - } - else - top.Add(item); - } - - var result = - new Tuple, ObservableCollection, List>(top, bottom, threadIds); - - return found > 1 ? result : null; - } - } -} +// 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.Collections.ObjectModel; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Threading; +using Debugger; +using Debugger.AddIn.Pads.ParallelPad; +using Debugger.AddIn.TreeModel; +using ICSharpCode.AvalonEdit.Rendering; +using ICSharpCode.Core; +using ICSharpCode.Core.Presentation; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop.Bookmarks; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Gui.Pads; + +namespace ICSharpCode.SharpDevelop.Gui.Pads +{ + public enum ParallelStacksView + { + Threads, + Tasks + } + + public class ParallelStackPad : DebuggerPad + { + DrawSurface surface; + Process debuggedProcess; + ParallelStacksGraph graph; + List currentThreadStacks = new List(); + ParallelStacksView parallelStacksView; + StackFrame selectedFrame; + bool isMethodView; + + #region Overrides + + protected override void InitializeComponents() + { + surface = new DrawSurface(); + + panel.Children.Add(surface); + } + + protected override void SelectProcess(Process process) + { + if (debuggedProcess != null) { + debuggedProcess.Paused -= OnProcessPaused; + } + debuggedProcess = process; + if (debuggedProcess != null) { + debuggedProcess.Paused += OnProcessPaused; + } + + DebuggerService.DebugStarted += OnReset; + DebuggerService.DebugStopped += OnReset; + + InvalidatePad(); + } + + protected override void RefreshPad() + { + if (debuggedProcess == null || debuggedProcess.IsRunning) { + return; + } + + LoggingService.InfoFormatted("Start refresh: {0}" + Environment.NewLine, parallelStacksView); + + currentThreadStacks.Clear(); + + using(new PrintTimes("Create stacks")) { + try { + // create all simple ThreadStacks + foreach (Thread thread in debuggedProcess.Threads) { + var t = thread; + debuggedProcess.EnqueueWork(Dispatcher.CurrentDispatcher, () => CreateThreadStack(t)); + } + } + catch(AbortedBecauseDebuggeeResumedException) { } + catch(System.Exception) { + if (debuggedProcess == null || debuggedProcess.HasExited) { + // Process unexpectedly exited + } else { + throw; + } + } + } + using(new PrintTimes("Run algorithm")) { + if (isMethodView) + { + // build method view for threads + CreateMethodViewStacks(); + } + else + { + // normal view + CreateCommonStacks(); + } + } + + using(new PrintTimes("Graph refresh")) { + // paint the ThreadStaks + graph = new ParallelStacksGraph(); + foreach (var stack in this.currentThreadStacks.FindAll(ts => ts.ThreadStackParents == null )) + { + graph.AddVertex(stack); + + // add the children + AddChildren(stack); + } + + if (graph.VertexCount > 0) + surface.SetGraph(graph); + } + } + + protected override ToolBar BuildToolBar() + { + return ToolBarService.CreateToolBar(panel, this, "/SharpDevelop/Pads/ParallelStacksPad/ToolBar"); + } + + #endregion + + #region Public Properties + + public ParallelStacksView ParallelStacksView { + get { return parallelStacksView; } + set { + parallelStacksView = value; + InvalidatePad(); + } + } + + public bool IsMethodView { + get { return isMethodView; } + set { + isMethodView = value; + InvalidatePad(); + } + } + + public bool IsZoomControlVisible { + get { return surface.IsZoomControlVisible; } + set { surface.IsZoomControlVisible = value; } + } + + #endregion + + #region Private Methods + + void OnReset(object sender, EventArgs e) + { + currentThreadStacks.Clear(); + selectedFrame = null; + + // remove all + BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark); + } + + void OnProcessPaused(object sender, ProcessEventArgs e) + { + InvalidatePad(); + } + + void AddChildren(ThreadStack parent) + { + if(parent.ThreadStackChildren == null || parent.ThreadStackChildren.Count == 0) + return; + + foreach (var ts in parent.ThreadStackChildren) + { + if (ts == null) continue; + + graph.AddVertex(ts); + graph.AddEdge(new ParallelStacksEdge(parent, ts)); + + if (ts.ThreadStackChildren == null || ts.ThreadStackChildren.Count == 0) + continue; + + AddChildren(ts); + } + } + + void CreateCommonStacks() + { + // stack.ItemCollection order + // 0 -> top of stack = S.C + // 1 -> ............ = S.B + // ....................... + // n -> bottom of stack = [External Methods] + + // ThreadStacks with common frame + var commonFrameThreads = new Dictionary>(); + + bool isOver = false; + while (true) { + + for (int i = currentThreadStacks.Count - 1; i >=0; --i) { + var stack = currentThreadStacks[i]; + if (stack.ItemCollection.Count == 0) { + currentThreadStacks.Remove(stack); + continue; + } + } + //get all thread stacks with common start frame + foreach (var stack in currentThreadStacks) { + int count = stack.ItemCollection.Count; + ParallelStackFrameModel frame = stack.ItemCollection[count - 1]; + string fullname = frame.MethodName + stack.Level.ToString(); + + if (!commonFrameThreads.ContainsKey(fullname)) + commonFrameThreads.Add(fullname, new List()); + + if (!commonFrameThreads[fullname].Contains(stack)) + commonFrameThreads[fullname].Add(stack); + } + + // for every list of common stacks, find split place and add them to currentThreadStacks + foreach (var frameName in commonFrameThreads.Keys) { + var listOfCurrentStacks = commonFrameThreads[frameName]; + + if (listOfCurrentStacks.Count == 1) // just skip the parents + { + isOver = true; // we finish when all are pseodo-parents: no more spliting + continue; + } + + isOver = false; + + // find the frameIndex where we can split + int frameIndex = 0; + string fn = string.Empty; + bool canContinue = true; + + while(canContinue) { + for (int i = 0; i < listOfCurrentStacks.Count; ++i) { + var stack = listOfCurrentStacks[i]; + if (stack.ItemCollection.Count == frameIndex) + { + canContinue = false; + break; + } + + ParallelStackFrameModel item = stack.ItemCollection[stack.ItemCollection.Count - frameIndex - 1]; + + string currentName = item.MethodName; + + if (i == 0) { + fn = currentName; + continue; + } + + if (fn == currentName) + continue; + else { + canContinue = false; + break; + } + } + if (canContinue) + frameIndex++; + } + + // remove last [frameIndex] and create a new ThreadStack as the parent of what remained in the children + var threadIds = new List(); + var parentItems = new Stack(); + + while (frameIndex > 0) { + for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) { + var stack = listOfCurrentStacks[i]; + int indexToRemove = stack.ItemCollection.Count - 1; + + #if DEBUG + ParallelStackFrameModel d_item = stack.ItemCollection[indexToRemove]; + string name = d_item.MethodName; + #endif + if (i == 0) + parentItems.Push(stack.ItemCollection[indexToRemove]); + + stack.ItemCollection.RemoveAt(indexToRemove); + } + + frameIndex--; + } + + // update thread ids + for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) + threadIds.AddRange(listOfCurrentStacks[i].ThreadIds); + + // remove stacks with no items + for (int i = listOfCurrentStacks.Count - 1; i >= 0; --i) { + var stack = listOfCurrentStacks[i]; + if (stack.ItemCollection.Count == 0) + listOfCurrentStacks.Remove(stack); + } + + // increase the Level + for (int i = 0 ; i < listOfCurrentStacks.Count; ++i) + listOfCurrentStacks[i].Level++; + + // create new parent stack + ThreadStack commonParent = new ThreadStack(); + commonParent.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, threadIds.ToArray()); + commonParent.ItemCollection = parentItems.ToObservable(); + commonParent.Process = debuggedProcess; + commonParent.StackSelected += OnThreadStackSelected; + commonParent.FrameSelected += OnFrameSelected; + commonParent.IsSelected = commonParent.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); + // add new children + foreach (var stack in listOfCurrentStacks) { + if (stack.ItemCollection.Count == 0) + { + currentThreadStacks.Remove(stack); + continue; + } + ParallelStackFrameModel item = stack.ItemCollection[stack.ItemCollection.Count - 1]; + // add the parent to the parent + if (stack.ThreadStackParents != null) { + // remove stack from it's parent because it will have the commonParent as parent + stack.ThreadStackParents[0].ThreadStackChildren.Remove(stack); + commonParent.ThreadStackParents = stack.ThreadStackParents.Clone(); + commonParent.ThreadStackParents[0].ThreadStackChildren.Add(commonParent); + // set level + commonParent.Level = stack.Level - 1; + } + else + stack.ThreadStackParents = new List(); + + // update thread ids + if (commonParent.ThreadStackParents != null) { + commonParent.ThreadStackParents[0].UpdateThreadIds( + parallelStacksView == ParallelStacksView.Tasks, + commonParent.ThreadIds.ToArray()); + } + + stack.ThreadStackParents.Clear(); + stack.ThreadStackParents.Add(commonParent); + string currentName = item.MethodName + stack.Level.ToString();; + + // add name or add to list + if (!commonFrameThreads.ContainsKey(currentName)) { + var newList = new List(); + newList.Add(stack); + commonFrameThreads.Add(currentName, newList); + } + else { + var list = commonFrameThreads[currentName]; + list.Add(stack); + } + } + + commonParent.ThreadStackChildren = listOfCurrentStacks.Clone(); + commonFrameThreads[frameName].Clear(); + commonFrameThreads[frameName].Add(commonParent); + currentThreadStacks.Add(commonParent); + + // exit and retry + break; + } + + if (isOver || currentThreadStacks.Count == 0) + break; + } + } + + void CreateMethodViewStacks() + { + var list = new List, ObservableCollection, List>>(); + + // find all threadstacks that contains the selected frame + for (int i = currentThreadStacks.Count - 1; i >= 0; --i) { + var tuple = currentThreadStacks[i].ItemCollection.SplitStack(selectedFrame, currentThreadStacks[i].ThreadIds); + if (tuple != null) + list.Add(tuple); + } + + currentThreadStacks.Clear(); + + // common + ThreadStack common = new ThreadStack(); + var observ = new ObservableCollection(); + bool dummy = false; + ParallelStackFrameModel obj = CreateItemForFrame(selectedFrame, ref dummy); + obj.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; + observ.Add(obj); + common.ItemCollection = observ; + common.StackSelected += OnThreadStackSelected; + common.FrameSelected += OnFrameSelected; + common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, selectedFrame.Thread.ID); + common.Process = debuggedProcess; + common.ThreadStackChildren = new List(); + common.ThreadStackParents = new List(); + + // for all thread stacks, split them in 2 : + // one that invokes the method frame, second that get's invoked by current method frame + foreach (var tuple in list) { + // add top + if (tuple.Item1.Count > 0) + { + ThreadStack topStack = new ThreadStack(); + topStack.ItemCollection = tuple.Item1; + topStack.StackSelected += OnThreadStackSelected; + topStack.FrameSelected += OnFrameSelected; + topStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); + topStack.Process = debuggedProcess; + topStack.ThreadStackParents = new List(); + topStack.ThreadStackParents.Add(common); + + currentThreadStacks.Add(topStack); + common.ThreadStackChildren.Add(topStack); + } + + // add bottom + if(tuple.Item2.Count > 0) + { + ThreadStack bottomStack = new ThreadStack(); + bottomStack.ItemCollection = tuple.Item2; + bottomStack.StackSelected += OnThreadStackSelected; + bottomStack.FrameSelected += OnFrameSelected; + bottomStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); + bottomStack.Process = debuggedProcess; + bottomStack.ThreadStackChildren = new List(); + bottomStack.ThreadStackChildren.Add(common); + common.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, tuple.Item3.ToArray()); + common.ThreadStackParents.Add(bottomStack); + currentThreadStacks.Add(bottomStack); + } + } + + currentThreadStacks.Add(common); + common.IsSelected = true; + } + + void CreateThreadStack(Thread thread) + { + var items = CreateItems(thread); + if (items == null || items.Count == 0) + return; + + ThreadStack threadStack = new ThreadStack(); + threadStack.StackSelected += OnThreadStackSelected; + threadStack.FrameSelected += OnFrameSelected; + threadStack.Process = debuggedProcess; + threadStack.ItemCollection = items; + threadStack.UpdateThreadIds(parallelStacksView == ParallelStacksView.Tasks, thread.ID); + + if (debuggedProcess.SelectedThread != null) { + threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); + if (selectedFrame == null) + selectedFrame = debuggedProcess.SelectedStackFrame; + } + + currentThreadStacks.Add(threadStack); + } + + ObservableCollection CreateItems(Thread thread) + { + bool lastItemIsExternalMethod = false; + int noTasks = 0; + var result = new ObservableCollection(); + var callstack = thread.GetCallstack(100); + + if (parallelStacksView == ParallelStacksView.Threads) { + foreach (StackFrame frame in callstack) { + ParallelStackFrameModel obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod); + + if (obj != null) + result.Add(obj); + } + } else { + for (int i = 0 ; i < callstack.Length; ++i) { + StackFrame frame = callstack[i]; + ParallelStackFrameModel obj = CreateItemForFrame(frame, ref lastItemIsExternalMethod); + + if (frame.MethodInfo.FullName.IndexOf("System.Threading.Tasks.Task.ExecuteEntry") != -1) { + noTasks++; + } + + if (noTasks == 1) { + if (frame.HasSymbols) { + // create thread stack for the items collected until now + ThreadStack threadStack = new ThreadStack(); + threadStack.StackSelected += OnThreadStackSelected; + threadStack.FrameSelected += OnFrameSelected; + threadStack.Process = debuggedProcess; + threadStack.ItemCollection = result.Clone(); + threadStack.UpdateThreadIds(true, frame.Thread.ID); + + if (debuggedProcess.SelectedThread != null) { + threadStack.IsSelected = threadStack.ThreadIds.Contains(debuggedProcess.SelectedThread.ID); + if (selectedFrame == null) + selectedFrame = debuggedProcess.SelectedStackFrame; + } + + currentThreadStacks.Add(threadStack); + // reset + result.Clear(); + noTasks = 0; + } + } + + if (obj != null) + result.Add(obj); + } + + // return null if we are dealing with a simple thread + return noTasks == 0 ? null : result; + } + + return result; + } + + ParallelStackFrameModel CreateItemForFrame(StackFrame frame, ref bool lastItemIsExternalMethod) + { + ParallelStackFrameModel model = new ParallelStackFrameModel(); + string fullName; + if (frame.HasSymbols) { + // Show the method in the list + fullName = frame.GetMethodName(); + lastItemIsExternalMethod = false; + model.FontWeight = FontWeights.Normal; + model.Foreground = Brushes.Black; + } else { + // Show [External methods] in the list + if (lastItemIsExternalMethod) return null; + fullName = ResourceService.GetString("MainWindow.Windows.Debug.CallStack.ExternalMethods").Trim(); + model.FontWeight = FontWeights.Normal; + model.Foreground = Brushes.Gray; + lastItemIsExternalMethod = true; + } + + if (frame.Thread.SelectedStackFrame != null && + frame.Thread.ID == debuggedProcess.SelectedThread.ID && + frame.Thread.SelectedStackFrame.IP == frame.IP && + frame.Thread.SelectedStackFrame.GetMethodName() == frame.GetMethodName()) { + model.Image = PresentationResourceService.GetImage("Bookmarks.CurrentLine").Source; + model.IsRunningStackFrame = true; + } else { + if (selectedFrame != null && frame.Thread.ID == selectedFrame.Thread.ID && + frame.GetMethodName() == selectedFrame.GetMethodName()) + model.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; + else + model.Image = null; + model.IsRunningStackFrame = false; + } + + model.MethodName = fullName; + + return model; + } + + void ToggleSelectedFrameBookmark(Location location) + { + // remove all + BookmarkManager.RemoveAll(b => b is SelectedFrameBookmark); + + ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveContent as ITextEditorProvider; + if (provider != null) { + ITextEditor editor = provider.TextEditor; + BookmarkManager.AddMark(new SelectedFrameBookmark(editor.FileName, location)); + } + } + + void OnThreadStackSelected(object sender, EventArgs e) + { + foreach (var ts in this.currentThreadStacks) { + if (ts.IsSelected) + ts.IsSelected = false; + ts.ClearImages(); + } + } + + void OnFrameSelected(object sender, FrameSelectedEventArgs e) + { + selectedFrame = e.Item; + + ToggleSelectedFrameBookmark(e.Location); + + if (isMethodView) + InvalidatePad(); + } + + #endregion + } + + static class StackFrameExtensions + { + internal static string GetMethodName(this StackFrame frame) + { + if (frame == null) + return null; + + StringBuilder name = new StringBuilder(); + name.Append(frame.MethodInfo.DeclaringType.FullName); + name.Append('.'); + name.Append(frame.MethodInfo.Name); + + return name.ToString(); + } + } + + static class ParallelStackExtensions + { + internal static List Clone(this List listToClone) + { + if (listToClone == null) + return null; + + var result = new List(); + foreach (var item in listToClone) + result.Add(item); + + return result; + } + + internal static ObservableCollection Clone(this ObservableCollection collectionToClone) + { + if (collectionToClone == null) + return null; + + var result = new ObservableCollection(); + foreach (var item in collectionToClone) + result.Add(item); + + return result; + } + + internal static ObservableCollection ToObservable(this Stack stack) + { + if (stack == null) + throw new NullReferenceException("Stack is null!"); + + var result = new ObservableCollection(); + while (stack.Count > 0) + result.Add(stack.Pop()); + + return result; + } + + internal static Tuple, ObservableCollection, List> + SplitStack(this ObservableCollection source, StackFrame frame, List threadIds) + { + var bottom = new ObservableCollection(); + var top = new ObservableCollection(); + + int found = 0; + + foreach (dynamic item in source) { + if (item.MethodName == frame.GetMethodName()) + found = 1; + + if (found >= 1) { + if(found > 1) + bottom.Add(item); + + found++; + } + else + top.Add(item); + } + + var result = + new Tuple, ObservableCollection, List>(top, bottom, threadIds); + + return found > 1 ? result : null; + } + } +} diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs index 35d1dd92b8..7f271cc0d5 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/ParallelPad/ThreadStack.xaml.cs @@ -1,331 +1,331 @@ -// 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.Collections.ObjectModel; -using System.Dynamic; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; - -using ICSharpCode.Core.Presentation; -using ICSharpCode.NRefactory; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Gui.Pads; - -namespace Debugger.AddIn.Pads.ParallelPad -{ - public class FrameSelectedEventArgs : EventArgs - { - public StackFrame Item { - get; - private set; - } - - public Location Location { - get; - private set; - } - - public FrameSelectedEventArgs(StackFrame item, Location location) - { - Item = item; - Location = location; - } - } - - public partial class ThreadStack : UserControl - { - public static SolidColorBrush SelectedBrush = new SolidColorBrush(Color.FromRgb(84, 169, 255)); - - public static readonly DependencyProperty IsSelectedProperty = - DependencyProperty.Register("IsSelected", typeof(bool), typeof(ThreadStack), - new FrameworkPropertyMetadata()); - - public event EventHandler StackSelected; - - public event EventHandler FrameSelected; - - ObservableCollection itemCollection = new ObservableCollection(); - - ToolTip toolTip = new ToolTip(); - List threadIds = new List(); - - public ThreadStack() - { - InitializeComponent(); - datagrid.ToolTip = toolTip; - datagrid.ToolTipOpening += OnToolTipOpening; - datagrid.PreviewMouseMove += new MouseEventHandler(datagrid_PreviewMouseMove); - datagrid.MouseLeave += delegate { toolTip.IsOpen = false; }; - } - - #region Public Properties - - public Process Process { get; set; } - - public int Level { get; set; } - - public bool IsSelected { - get { return (bool)GetValue(IsSelectedProperty); } - set { - if (value) { - BorderParent.BorderBrush = SelectedBrush; - BorderParent.BorderThickness = new Thickness(5); - } - else { - BorderParent.BorderBrush = Brushes.Black; - BorderParent.BorderThickness = new Thickness(3); - } - - SetValue(IsSelectedProperty, value); - - SelectParent(value); - } - } - - public List ThreadStackParents { get; set; } - - public List ThreadStackChildren { get; set; } - - public List ThreadIds { - get { return threadIds; } - } - - public ObservableCollection ItemCollection { - get { return itemCollection; } - set { - itemCollection = value; - this.datagrid.ItemsSource = itemCollection; - } - } - - #endregion - - #region Public Methods - - public void UpdateThreadIds(bool isTask, params uint[] threadIds) - { - var list = new List(); - foreach (uint id in threadIds) { - if (!this.threadIds.Contains(id)) { - list.Add(id); - } - } - this.threadIds.AddRange(list); - - if (this.threadIds.Count > 1) { - string suffix = isTask ? " Tasks" : " Threads"; - this.HeaderText.Text = this.threadIds.Count.ToString() + suffix; - } - else { - this.HeaderText.Text = isTask ? "1 Task" : "1 Thread"; - } - } - - public void ClearImages() - { - foreach (ParallelStackFrameModel item in itemCollection) { - if (!item.IsRunningStackFrame) - item.Image = null; - } - } - - #endregion - - #region Private Methods - - private void SelectParent(bool isSelected) - { - if (this.ThreadStackParents == null || this.ThreadStackParents.Count == 0) - return; - - foreach (var ts in this.ThreadStackParents) - if (ts != null) - ts.IsSelected = isSelected; - } - - void datagrid_PreviewMouseMove(object sender, MouseEventArgs e) - { - var result = VisualTreeHelper.HitTest(this, e.GetPosition(this)); - if (result != null) - { - var row = TryFindParent(result.VisualHit); - if (row != null) - { - datagrid.SelectedItem = row.DataContext; - e.Handled = true; - } - } - } - - private void Datagrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) - { - if (Process.IsRunning) return; - - ParallelStackFrameModel selectedItem = datagrid.SelectedItem as ParallelStackFrameModel; - if (selectedItem != null) { - if (ThreadIds.Count > 1) { - datagrid.ContextMenu = CreateContextMenu(selectedItem); - datagrid.ContextMenu.IsOpen = true; - } - else - { - SelectFrame(ThreadIds[0], selectedItem); - } - } - } - - private void SelectFrame(uint threadId, ParallelStackFrameModel selectedItem) - { - if (selectedItem == null) - return; - - var thread = Process.Threads.Find(t => t.ID == threadId); - if (thread == null) - return; - - if (StackSelected != null) - StackSelected(this, EventArgs.Empty); - - this.IsSelected = true; - - foreach(var frame in thread.Callstack) - { - if (frame.GetMethodName() == selectedItem.MethodName) - { - if (!selectedItem.IsRunningStackFrame) - selectedItem.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; - - SourcecodeSegment nextStatement = frame.NextStatement; - if (nextStatement != null) { - var location = new Location(nextStatement.StartColumn, nextStatement.StartLine); - FileService.JumpToFilePosition( - nextStatement.Filename, location.Line, location.Column); - - if (FrameSelected != null) - FrameSelected(this, new FrameSelectedEventArgs(frame, location)); - } - - break; - } - } - } - - private void Datagrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) - { - if (Process.IsRunning) return; - - ParallelStackFrameModel selectedItem = datagrid.SelectedItem as ParallelStackFrameModel; - if (selectedItem == null) - return; - - datagrid.ContextMenu = CreateContextMenu(selectedItem); - datagrid.ContextMenu.IsOpen = true; - } - - ContextMenu CreateContextMenu(ParallelStackFrameModel item) - { - var menu = new ContextMenu(); - - foreach (var id in ThreadIds) { - MenuItem m = new MenuItem(); - m.IsCheckable = true; - m.IsChecked = id == Process.SelectedThread.ID; - m.Click += delegate(object sender, RoutedEventArgs e) { - var menuItem = e.OriginalSource as MenuItem; - SelectFrame((uint)menuItem.Tag, item); - }; - m.Tag = id; - m.Header = id.ToString() + ":" + item.MethodName; - - menu.Items.Add(m); - } - - return menu; - } - - private void OnToolTipOpening(object sender, ToolTipEventArgs e) - { - if (Process.IsRunning) - return; - - StackPanel panel = new StackPanel(); - - ParallelStackFrameModel selectedItem = datagrid.SelectedItem as ParallelStackFrameModel; - if (selectedItem == null) { - panel.Children.Add(new TextBlock { Text = "No item selected" }); - this.toolTip.Content = panel; - return; - } - - foreach(var thread in Process.Threads) - { - if (ThreadIds.Contains(thread.ID)) - { - foreach (var frame in thread.Callstack) - { - if (selectedItem.MethodName == frame.GetMethodName()) - { - TextBlock tb = new TextBlock(); - tb.Text = thread.ID + ": " + CallStackPadContent.GetFullName(frame); - panel.Children.Add(tb); - } - } - } - } - - this.toolTip.Content = panel; - } - - #endregion - - #region Static Methods - - private static T TryFindParent(DependencyObject child) where T : DependencyObject - { - if (child is T) return child as T; - - DependencyObject parentObject = GetParentObject(child); - if (parentObject == null) return null; - - var parent = parentObject as T; - if (parent != null && parent is T) - { - return parent; - } - else - { - return TryFindParent(parentObject); - } - } - - private static DependencyObject GetParentObject(DependencyObject child) - { - if (child == null) return null; - - ContentElement contentElement = child as ContentElement; - if (contentElement != null) - { - DependencyObject parent = ContentOperations.GetParent(contentElement); - if (parent != null) return parent; - - FrameworkContentElement fce = contentElement as FrameworkContentElement; - return fce != null ? fce.Parent : null; - } - - FrameworkElement frameworkElement = child as FrameworkElement; - if (frameworkElement != null) - { - DependencyObject parent = frameworkElement.Parent; - if (parent != null) return parent; - } - - return VisualTreeHelper.GetParent(child); - } - - #endregion - } +// 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.Collections.ObjectModel; +using System.Dynamic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +using ICSharpCode.Core.Presentation; +using ICSharpCode.NRefactory; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui.Pads; + +namespace Debugger.AddIn.Pads.ParallelPad +{ + public class FrameSelectedEventArgs : EventArgs + { + public StackFrame Item { + get; + private set; + } + + public Location Location { + get; + private set; + } + + public FrameSelectedEventArgs(StackFrame item, Location location) + { + Item = item; + Location = location; + } + } + + public partial class ThreadStack : UserControl + { + public static SolidColorBrush SelectedBrush = new SolidColorBrush(Color.FromRgb(84, 169, 255)); + + public static readonly DependencyProperty IsSelectedProperty = + DependencyProperty.Register("IsSelected", typeof(bool), typeof(ThreadStack), + new FrameworkPropertyMetadata()); + + public event EventHandler StackSelected; + + public event EventHandler FrameSelected; + + ObservableCollection itemCollection = new ObservableCollection(); + + ToolTip toolTip = new ToolTip(); + List threadIds = new List(); + + public ThreadStack() + { + InitializeComponent(); + datagrid.ToolTip = toolTip; + datagrid.ToolTipOpening += OnToolTipOpening; + datagrid.PreviewMouseMove += new MouseEventHandler(datagrid_PreviewMouseMove); + datagrid.MouseLeave += delegate { toolTip.IsOpen = false; }; + } + + #region Public Properties + + public Process Process { get; set; } + + public int Level { get; set; } + + public bool IsSelected { + get { return (bool)GetValue(IsSelectedProperty); } + set { + if (value) { + BorderParent.BorderBrush = SelectedBrush; + BorderParent.BorderThickness = new Thickness(5); + } + else { + BorderParent.BorderBrush = Brushes.Black; + BorderParent.BorderThickness = new Thickness(3); + } + + SetValue(IsSelectedProperty, value); + + SelectParent(value); + } + } + + public List ThreadStackParents { get; set; } + + public List ThreadStackChildren { get; set; } + + public List ThreadIds { + get { return threadIds; } + } + + public ObservableCollection ItemCollection { + get { return itemCollection; } + set { + itemCollection = value; + this.datagrid.ItemsSource = itemCollection; + } + } + + #endregion + + #region Public Methods + + public void UpdateThreadIds(bool isTask, params uint[] threadIds) + { + var list = new List(); + foreach (uint id in threadIds) { + if (!this.threadIds.Contains(id)) { + list.Add(id); + } + } + this.threadIds.AddRange(list); + + if (this.threadIds.Count > 1) { + string suffix = isTask ? " Tasks" : " Threads"; + this.HeaderText.Text = this.threadIds.Count.ToString() + suffix; + } + else { + this.HeaderText.Text = isTask ? "1 Task" : "1 Thread"; + } + } + + public void ClearImages() + { + foreach (ParallelStackFrameModel item in itemCollection) { + if (!item.IsRunningStackFrame) + item.Image = null; + } + } + + #endregion + + #region Private Methods + + private void SelectParent(bool isSelected) + { + if (this.ThreadStackParents == null || this.ThreadStackParents.Count == 0) + return; + + foreach (var ts in this.ThreadStackParents) + if (ts != null) + ts.IsSelected = isSelected; + } + + void datagrid_PreviewMouseMove(object sender, MouseEventArgs e) + { + var result = VisualTreeHelper.HitTest(this, e.GetPosition(this)); + if (result != null) + { + var row = TryFindParent(result.VisualHit); + if (row != null) + { + datagrid.SelectedItem = row.DataContext; + e.Handled = true; + } + } + } + + private void Datagrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (Process.IsRunning) return; + + ParallelStackFrameModel selectedItem = datagrid.SelectedItem as ParallelStackFrameModel; + if (selectedItem != null) { + if (ThreadIds.Count > 1) { + datagrid.ContextMenu = CreateContextMenu(selectedItem); + datagrid.ContextMenu.IsOpen = true; + } + else + { + SelectFrame(ThreadIds[0], selectedItem); + } + } + } + + private void SelectFrame(uint threadId, ParallelStackFrameModel selectedItem) + { + if (selectedItem == null) + return; + + var thread = Process.Threads.Find(t => t.ID == threadId); + if (thread == null) + return; + + if (StackSelected != null) + StackSelected(this, EventArgs.Empty); + + this.IsSelected = true; + + foreach(var frame in thread.Callstack) + { + if (frame.GetMethodName() == selectedItem.MethodName) + { + if (!selectedItem.IsRunningStackFrame) + selectedItem.Image = PresentationResourceService.GetImage("Icons.48x48.CurrentFrame").Source; + + SourcecodeSegment nextStatement = frame.NextStatement; + if (nextStatement != null) { + var location = new Location(nextStatement.StartColumn, nextStatement.StartLine); + FileService.JumpToFilePosition( + nextStatement.Filename, location.Line, location.Column); + + if (FrameSelected != null) + FrameSelected(this, new FrameSelectedEventArgs(frame, location)); + } + + break; + } + } + } + + private void Datagrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) + { + if (Process.IsRunning) return; + + ParallelStackFrameModel selectedItem = datagrid.SelectedItem as ParallelStackFrameModel; + if (selectedItem == null) + return; + + datagrid.ContextMenu = CreateContextMenu(selectedItem); + datagrid.ContextMenu.IsOpen = true; + } + + ContextMenu CreateContextMenu(ParallelStackFrameModel item) + { + var menu = new ContextMenu(); + + foreach (var id in ThreadIds) { + MenuItem m = new MenuItem(); + m.IsCheckable = true; + m.IsChecked = id == Process.SelectedThread.ID; + m.Click += delegate(object sender, RoutedEventArgs e) { + var menuItem = e.OriginalSource as MenuItem; + SelectFrame((uint)menuItem.Tag, item); + }; + m.Tag = id; + m.Header = id.ToString() + ":" + item.MethodName; + + menu.Items.Add(m); + } + + return menu; + } + + private void OnToolTipOpening(object sender, ToolTipEventArgs e) + { + if (Process.IsRunning) + return; + + StackPanel panel = new StackPanel(); + + ParallelStackFrameModel selectedItem = datagrid.SelectedItem as ParallelStackFrameModel; + if (selectedItem == null) { + panel.Children.Add(new TextBlock { Text = "No item selected" }); + this.toolTip.Content = panel; + return; + } + + foreach(var thread in Process.Threads) + { + if (ThreadIds.Contains(thread.ID)) + { + foreach (var frame in thread.Callstack) + { + if (selectedItem.MethodName == frame.GetMethodName()) + { + TextBlock tb = new TextBlock(); + tb.Text = thread.ID + ": " + CallStackPadContent.GetFullName(frame); + panel.Children.Add(tb); + } + } + } + } + + this.toolTip.Content = panel; + } + + #endregion + + #region Static Methods + + private static T TryFindParent(DependencyObject child) where T : DependencyObject + { + if (child is T) return child as T; + + DependencyObject parentObject = GetParentObject(child); + if (parentObject == null) return null; + + var parent = parentObject as T; + if (parent != null && parent is T) + { + return parent; + } + else + { + return TryFindParent(parentObject); + } + } + + private static DependencyObject GetParentObject(DependencyObject child) + { + if (child == null) return null; + + ContentElement contentElement = child as ContentElement; + if (contentElement != null) + { + DependencyObject parent = ContentOperations.GetParent(contentElement); + if (parent != null) return parent; + + FrameworkContentElement fce = contentElement as FrameworkContentElement; + return fce != null ? fce.Parent : null; + } + + FrameworkElement frameworkElement = child as FrameworkElement; + if (frameworkElement != null) + { + DependencyObject parent = frameworkElement.Parent; + if (parent != null) return parent; + } + + return VisualTreeHelper.GetParent(child); + } + + #endregion + } } \ No newline at end of file diff --git a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs index e498cfc91d..c575fb84cf 100644 --- a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs +++ b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs @@ -1,995 +1,995 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) - -using System; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Security.Cryptography; -using System.Text; -using System.Windows.Forms; - -using Debugger; -using Debugger.AddIn.Tooltips; -using Debugger.AddIn.TreeModel; -using Debugger.Interop.CorPublish; -using Debugger.MetaData; -using ICSharpCode.Core; -using ICSharpCode.Core.WinForms; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.Ast; -using ICSharpCode.NRefactory.Visitors; -using ICSharpCode.SharpDevelop.Bookmarks; -using ICSharpCode.SharpDevelop.Debugging; -using ICSharpCode.SharpDevelop.Gui; -using ICSharpCode.SharpDevelop.Gui.OptionPanels; -using ICSharpCode.SharpDevelop.Project; -using Mono.Cecil; -using Process = Debugger.Process; - -namespace ICSharpCode.SharpDevelop.Services -{ - public class WindowsDebugger : IDebugger - { - enum StopAttachedProcessDialogResult { - Detach = 0, - Terminate = 1, - Cancel = 2 - } - - bool useRemotingForThreadInterop = false; - bool attached; - - NDebugger debugger; - - ICorPublish corPublish; - - Process debuggedProcess; - - internal IDebuggerDecompilerService debuggerDecompilerService; - - //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 = "${res:XML.MainMenu.DebugMenu.Error.Debugging}"; - string errorNotDebugging = "${res:XML.MainMenu.DebugMenu.Error.NotDebugging}"; - string errorProcessRunning = "${res:XML.MainMenu.DebugMenu.Error.ProcessRunning}"; - string errorProcessPaused = "${res:XML.MainMenu.DebugMenu.Error.ProcessPaused}"; - string errorCannotStepNoActiveFunction = "${res:MainWindow.Windows.Debug.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 bool CanDebug(IProject project) - { - return true; - } - - public void Start(ProcessStartInfo processStartInfo) - { - if (IsDebugging) { - MessageService.ShowMessage(errorDebugging); - return; - } - if (!ServiceInitialized) { - InitializeService(); - } - - string version = debugger.GetProgramVersion(processStartInfo.FileName); - - if (version.StartsWith("v1.0")) { - MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.Net10NotSupported}"); - } else if (version.StartsWith("v1.1")) { - MessageService.ShowMessage(StringParser.Parse("${res:XML.MainMenu.DebugMenu.Error.Net10NotSupported}").Replace("1.0", "1.1")); -// } else if (string.IsNullOrEmpty(version)) { -// // Not a managed assembly -// MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.BadAssembly}"); - } else if (debugger.IsKernelDebuggerEnabled) { - MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.KernelDebuggerEnabled}"); - } else { - attached = false; - if (DebugStarting != null) - DebugStarting(this, EventArgs.Empty); - - try { - // set the JIT flag for evaluating optimized code - Process.DebugMode = DebugModeFlag.Debug; - 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 = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Error.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."; - } - MessageService.ShowMessage(msg); - - if (DebugStopped != null) - DebugStopped(this, EventArgs.Empty); - } else { - throw; - } - } - } - } - - public void ShowAttachDialog() - { - using (AttachToProcessForm attachForm = new AttachToProcessForm()) { - if (attachForm.ShowDialog(WorkbenchSingleton.MainWin32Window) == DialogResult.OK) { - Attach(attachForm.Process); - } - } - } - - public void Attach(System.Diagnostics.Process existingProcess) - { - if (existingProcess == null) - return; - - if (IsDebugging) { - MessageService.ShowMessage(errorDebugging); - return; - } - if (!ServiceInitialized) { - InitializeService(); - } - - string version = debugger.GetProgramVersion(existingProcess.MainModule.FileName); - if (version.StartsWith("v1.0")) { - MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.Net10NotSupported}"); - } else { - if (DebugStarting != null) - DebugStarting(this, EventArgs.Empty); - - try { - // set the JIT flag for evaluating optimized code - Process.DebugMode = DebugModeFlag.Debug; - Process process = debugger.Attach(existingProcess); - attached = true; - SelectProcess(process); - - process.Modules.Added += process_Modules_Added; - } catch (System.Exception e) { - // CORDBG_E_DEBUGGER_ALREADY_ATTACHED - if (e is COMException || e is UnauthorizedAccessException) { - string msg = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Error.CannotAttachToProcess}"); - MessageService.ShowMessage(msg + " " + e.Message); - - if (DebugStopped != null) - DebugStopped(this, EventArgs.Empty); - } else { - throw; - } - } - } - } - - public void Detach() - { - debugger.Detach(); - } - - public void StartWithoutDebugging(ProcessStartInfo processStartInfo) - { - System.Diagnostics.Process.Start(processStartInfo); - } - - public void Stop() - { - if (!IsDebugging) { - MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.Stop}"); - return; - } - if (IsAttached) { - StopAttachedProcessDialogResult result = ShowStopAttachedProcessDialog(); - switch (result) { - case StopAttachedProcessDialogResult.Terminate: - debuggedProcess.Terminate(); - attached = false; - break; - case StopAttachedProcessDialogResult.Detach: - Detach(); - attached = false; - break; - } - } else { - debuggedProcess.Terminate(); - } - } - - // ExecutionControl: - - public void Break() - { - if (!IsDebugging) { - MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.Break}"); - return; - } - if (!IsProcessRunning) { - MessageService.ShowMessage(errorProcessPaused, "${res:XML.MainMenu.DebugMenu.Break}"); - return; - } - debuggedProcess.Break(); - } - - public void Continue() - { - if (!IsDebugging) { - MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.Continue}"); - return; - } - if (IsProcessRunning) { - MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.Continue}"); - return; - } - debuggedProcess.AsyncContinue(); - } - - // Stepping: - Debugger.StackFrame GetStackFrame() - { - bool isMatch = false; - int line = -1; - int[] ilRange = null; - - var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - int typeToken = frame.MethodInfo.DeclaringType.MetadataToken; - int methodToken = frame.MethodInfo.MetadataToken; - - // get the mapped instruction from the current line marker or the next one - if (!debuggerDecompilerService.GetILAndLineNumber(typeToken, methodToken, frame.IP, out ilRange, out line, out isMatch)){ - frame.SourceCodeLine = 0; - frame.ILRanges = new [] { 0, 1 }; - } else { - frame.SourceCodeLine = line; - frame.ILRanges = ilRange; - } - - return frame; - } - - public void StepInto() - { - if (!IsDebugging) { - MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepInto}"); - return; - } - - if (debuggedProcess.IsRunning) { - MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.StepInto}"); - return; - } - - var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - if (frame == null) { - MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}"); - } else { - if (!frame.HasSymbols) { - // get frame info from external code mappings - frame = GetStackFrame(); - } - - frame.AsyncStepInto(); - } - } - - public void StepOver() - { - if (!IsDebugging) { - MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOver}"); - return; - } - - if (debuggedProcess.IsRunning) { - MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.StepOver}"); - return; - } - - var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - if (frame == null) { - MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOver}"); - } else { - if (!frame.HasSymbols) { - // get frame info from external code mappings - frame = GetStackFrame(); - } - - frame.AsyncStepOver(); - } - } - - public void StepOut() - { - if (!IsDebugging) { - MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOut}"); - return; - } - - if (debuggedProcess.IsRunning) { - MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.StepOut}"); - return; - } - - var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - if (frame == null) { - MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}"); - } else { - if (!frame.HasSymbols) { - // get frame info from external code mappings - frame = GetStackFrame(); - } - - frame.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; - } - - var frame = debuggedProcess.GetCurrentExecutingFrame(); - if (frame == null) - return null; - object data = debuggerDecompilerService.GetLocalVariableIndex(frame.MethodInfo.DeclaringType.MetadataToken, - frame.MethodInfo.MetadataToken, - variableName); - // evaluate expression - return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, frame, data); - } - - /// - /// Gets Expression for given variable. Can throw GetValueException. - /// Thrown when getting expression fails. Exception message explains reason. - /// - public 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(); - Debugger.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 || debuggedProcess.SelectedThread.MostRecentStackFrame != 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(null, image, variableName, tooltipExpression); - expressionNode.ImageName = imageName; - return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = debuggedProcess.GetCurrentExecutingFrame().HasSymbols }; - } catch (System.Exception ex) { - LoggingService.Error("Error on GetTooltipControl: " + ex.Message); - return null; - } - } - - public ITreeNode GetNode(string variable, string currentImageName = null) - { - try { - var expression = GetExpression(variable); - string imageName; - IImage image; - if (string.IsNullOrEmpty(currentImageName)) { - image = ExpressionNode.GetImageForLocalVariable(out imageName); - } - else { - image = new ResourceServiceImage(currentImageName); - imageName = currentImageName; - } - ExpressionNode expressionNode = new ExpressionNode(null, 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(); - } - - // get decompiler service - var items = AddInTree.BuildItems("/SharpDevelop/Services/DebuggerDecompilerService", null, false); - if (items.Count > 0) - debuggerDecompilerService = items[0]; - - // init NDebugger - 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 = null; - - if (bookmark is DecompiledBreakpointBookmark) { - try { - if (debuggerDecompilerService == null) { - LoggingService.Warn("No IDebuggerDecompilerService found!"); - return; - } - var dbb = (DecompiledBreakpointBookmark)bookmark; - MemberReference memberReference = null; - - string assemblyFile, typeName; - if (DecompiledBreakpointBookmark.GetAssemblyAndType(dbb.FileName, out assemblyFile, out typeName)) { - memberReference = dbb.GetMemberReference(debuggerDecompilerService.GetAssemblyResolver(assemblyFile)); - } - - int token = memberReference.MetadataToken.ToInt32(); - if (!debuggerDecompilerService.CheckMappings(token)) - debuggerDecompilerService.DecompileOnDemand(memberReference as TypeDefinition); - - int[] ilRanges; - int methodToken; - if (debuggerDecompilerService.GetILAndTokenByLineNumber(token, dbb.LineNumber, out ilRanges, out methodToken)) { - // create BP - breakpoint = new ILBreakpoint( - debugger, - memberReference.FullName, - dbb.LineNumber, - memberReference.MetadataToken.ToInt32(), - methodToken, - ilRanges[0], - dbb.IsEnabled); - - debugger.Breakpoints.Add(breakpoint); - } - } catch (System.Exception ex) { - LoggingService.Error("Error on DecompiledBreakpointBookmark: " + ex.Message); - } - } else { - breakpoint = debugger.Breakpoints.Add(bookmark.FileName, null, bookmark.LineNumber, 0, bookmark.IsEnabled); - } - - if (breakpoint == null) { - LoggingService.Warn(string.Format("unable to create breakpoint: {0}", bookmark.ToString())); - return; - } - - MethodInvoker 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 == null || breakpoint.OriginalLocation.CheckSum == null) { - bookmark.IsHealthy = true; - bookmark.Tooltip = null; - } else { - if (!File.Exists(bookmark.FileName)) - return; - - 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.ModulesAdded -= debuggedProcess_ModulesAdded; - } - debuggedProcess = process; - if (debuggedProcess != null) { - debuggedProcess.Paused += debuggedProcess_DebuggingPaused; - debuggedProcess.ExceptionThrown += debuggedProcess_ExceptionThrown; - debuggedProcess.Resumed += debuggedProcess_DebuggingResumed; - debuggedProcess.ModulesAdded += debuggedProcess_ModulesAdded; - - debuggedProcess.BreakAtBeginning = BreakAtBeginning; - } - // reset - BreakAtBeginning = false; - - JumpToCurrentLine(); - OnProcessSelected(new ProcessEventArgs(process)); - } - - void debuggedProcess_ModulesAdded(object sender, ModuleEventArgs e) - { - var currentModuleTypes = e.Module.GetNamesOfDefinedTypes(); - foreach (var bookmark in DebuggerService.Breakpoints.OfType()) { - var breakpoint = debugger.Breakpoints.FirstOrDefault( - b => b is ILBreakpoint && b.Line == bookmark.LineNumber && - ((ILBreakpoint)b).MetadataToken == bookmark.MemberReference.MetadataToken.ToInt32()); - if (breakpoint == null) - continue; - // set the breakpoint only if the module contains the type - if (!currentModuleTypes.Contains(breakpoint.TypeName)) - continue; - - breakpoint.SetBreakpoint(e.Module); - } - } - - 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, ProcessEventArgs e) - { - OnIsProcessRunningChanged(EventArgs.Empty); - DebuggerService.RemoveCurrentLineMarker(); - } - - void debuggedProcess_ExceptionThrown(object sender, ExceptionEventArgs e) - { - JumpToCurrentLine(); - - StringBuilder stacktraceBuilder = new StringBuilder(); - - if (e.IsUnhandled) { - // Need to intercept now so that we can evaluate properties - if (e.Process.SelectedThread.InterceptException(e.Exception)) { - stacktraceBuilder.AppendLine(e.Exception.ToString()); - string stackTrace; - try { - stackTrace = e.Exception.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.EndOfInnerException}")); - } catch (GetValueException) { - stackTrace = e.Process.SelectedThread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}")); - } - stacktraceBuilder.Append(stackTrace); - } else { - // For example, happens on stack overflow - stacktraceBuilder.AppendLine(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptException}")); - stacktraceBuilder.AppendLine(e.Exception.ToString()); - stacktraceBuilder.Append(e.Process.SelectedThread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}"))); - } - } else { - stacktraceBuilder.AppendLine(e.Exception.ToString()); - stacktraceBuilder.Append(e.Process.SelectedThread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}"))); - } - - string title = e.IsUnhandled ? StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Unhandled}") : StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Handled}"); - string message = string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Message}"), e.Exception.Type); - Bitmap icon = WinFormsResourceService.GetBitmap(e.IsUnhandled ? "Icons.32x32.Error" : "Icons.32x32.Warning"); - - DebuggeeExceptionForm.Show(debuggedProcess, title, message, stacktraceBuilder.ToString(), icon, e.IsUnhandled, e.Exception); - } - - public bool BreakAndInterceptHandledException(Debugger.Exception exception) - { - if (!debuggedProcess.SelectedThread.InterceptException(exception)) { - MessageService.ShowError("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptHandledException}"); - return false; - } - JumpToCurrentLine(); - return true; - } - - public void JumpToCurrentLine() - { - if (debuggedProcess == null || debuggedProcess.SelectedThread == null) - return; - - WorkbenchSingleton.MainWindow.Activate(); - - if (debuggedProcess.IsSelectedFrameForced()) { - if (debuggedProcess.SelectedStackFrame != null && debuggedProcess.SelectedStackFrame.HasSymbols) { - JumpToSourceCode(); - } else { - JumpToDecompiledCode(debuggedProcess.SelectedStackFrame); - } - } else { - var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; - // other pause reasons - if (frame != null && frame.HasSymbols) { - JumpToSourceCode(); - } else { - // use most recent stack frame because we don't have the symbols - JumpToDecompiledCode(debuggedProcess.SelectedThread.MostRecentStackFrame); - } - } - } - - void JumpToSourceCode() - { - if (debuggedProcess == null || debuggedProcess.SelectedThread == null) - return; - - SourcecodeSegment nextStatement = debuggedProcess.NextStatement; - if (nextStatement != null) { - DebuggerService.RemoveCurrentLineMarker(); - DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); - } - } - - void JumpToDecompiledCode(Debugger.StackFrame frame) - { - if (frame == null) { - LoggingService.Error("No stack frame!"); - return; - } - - if (debuggerDecompilerService == null) { - LoggingService.Warn("No IDebuggerDecompilerService found!"); - return; - } - - // check for options - if these options are enabled, debugging decompiled code should not continue - if (!debuggedProcess.Options.DecompileCodeWithoutSymbols) { - LoggingService.Info("Decompiled code debugging is disabled!"); - return; - } - DebuggerService.RemoveCurrentLineMarker(); - // get external data - int typeToken = frame.MethodInfo.DeclaringType.MetadataToken; - int methodToken = frame.MethodInfo.MetadataToken; - int ilOffset = frame.IP; - int[] ilRanges = null; - int line = -1; - bool isMatch = false; - var debugType = (DebugType)frame.MethodInfo.DeclaringType; - debuggerDecompilerService.DebugStepInformation = Tuple.Create(methodToken, ilOffset); - - if (debuggerDecompilerService.GetILAndLineNumber(typeToken, methodToken, ilOffset, out ilRanges, out line, out isMatch)) { - // update marker & navigate to line - NavigationService.NavigateTo(debugType.DebugModule.FullPath, - debugType.FullNameWithoutGenericArguments, - IDStringProvider.GetIDString(frame.MethodInfo), - line); - } else { - // no line => do decompilation - NavigationService.NavigateTo(debugType.DebugModule.FullPath, - debugType.FullNameWithoutGenericArguments, - IDStringProvider.GetIDString(frame.MethodInfo)); - - } - } - - StopAttachedProcessDialogResult ShowStopAttachedProcessDialog() - { - string caption = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Stop}"); - string message = StringParser.Parse("${res:MainWindow.Windows.Debug.StopProcessDialog.Message}"); - string[] buttonLabels = new string[] { StringParser.Parse("${res:XML.MainMenu.DebugMenu.Detach}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Terminate}"), StringParser.Parse("${res:Global.CancelButtonText}") }; - return (StopAttachedProcessDialogResult)MessageService.ShowCustomDialog(caption, message, (int)StopAttachedProcessDialogResult.Detach, (int)StopAttachedProcessDialogResult.Cancel, buttonLabels); - } - - void process_Modules_Added(object sender, CollectionItemEventArgs e) - { - if (ProjectService.OpenSolution == null) - return; - - ProjectService.OpenSolution.Projects - .Where(p => e.Item.Name.IndexOf(p.Name) >= 0) - .ForEach(p => e.Item.LoadSymbolsFromDisk(new []{ Path.GetDirectoryName(p.OutputAssemblyFullPath) })); - } - } -} +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) + +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Windows.Forms; + +using Debugger; +using Debugger.AddIn.Tooltips; +using Debugger.AddIn.TreeModel; +using Debugger.Interop.CorPublish; +using Debugger.MetaData; +using ICSharpCode.Core; +using ICSharpCode.Core.WinForms; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.Visitors; +using ICSharpCode.SharpDevelop.Bookmarks; +using ICSharpCode.SharpDevelop.Debugging; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Gui.OptionPanels; +using ICSharpCode.SharpDevelop.Project; +using Mono.Cecil; +using Process = Debugger.Process; + +namespace ICSharpCode.SharpDevelop.Services +{ + public class WindowsDebugger : IDebugger + { + enum StopAttachedProcessDialogResult { + Detach = 0, + Terminate = 1, + Cancel = 2 + } + + bool useRemotingForThreadInterop = false; + bool attached; + + NDebugger debugger; + + ICorPublish corPublish; + + Process debuggedProcess; + + internal IDebuggerDecompilerService debuggerDecompilerService; + + //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 = "${res:XML.MainMenu.DebugMenu.Error.Debugging}"; + string errorNotDebugging = "${res:XML.MainMenu.DebugMenu.Error.NotDebugging}"; + string errorProcessRunning = "${res:XML.MainMenu.DebugMenu.Error.ProcessRunning}"; + string errorProcessPaused = "${res:XML.MainMenu.DebugMenu.Error.ProcessPaused}"; + string errorCannotStepNoActiveFunction = "${res:MainWindow.Windows.Debug.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 bool CanDebug(IProject project) + { + return true; + } + + public void Start(ProcessStartInfo processStartInfo) + { + if (IsDebugging) { + MessageService.ShowMessage(errorDebugging); + return; + } + if (!ServiceInitialized) { + InitializeService(); + } + + string version = debugger.GetProgramVersion(processStartInfo.FileName); + + if (version.StartsWith("v1.0")) { + MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.Net10NotSupported}"); + } else if (version.StartsWith("v1.1")) { + MessageService.ShowMessage(StringParser.Parse("${res:XML.MainMenu.DebugMenu.Error.Net10NotSupported}").Replace("1.0", "1.1")); +// } else if (string.IsNullOrEmpty(version)) { +// // Not a managed assembly +// MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.BadAssembly}"); + } else if (debugger.IsKernelDebuggerEnabled) { + MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.KernelDebuggerEnabled}"); + } else { + attached = false; + if (DebugStarting != null) + DebugStarting(this, EventArgs.Empty); + + try { + // set the JIT flag for evaluating optimized code + Process.DebugMode = DebugModeFlag.Debug; + 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 = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Error.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."; + } + MessageService.ShowMessage(msg); + + if (DebugStopped != null) + DebugStopped(this, EventArgs.Empty); + } else { + throw; + } + } + } + } + + public void ShowAttachDialog() + { + using (AttachToProcessForm attachForm = new AttachToProcessForm()) { + if (attachForm.ShowDialog(WorkbenchSingleton.MainWin32Window) == DialogResult.OK) { + Attach(attachForm.Process); + } + } + } + + public void Attach(System.Diagnostics.Process existingProcess) + { + if (existingProcess == null) + return; + + if (IsDebugging) { + MessageService.ShowMessage(errorDebugging); + return; + } + if (!ServiceInitialized) { + InitializeService(); + } + + string version = debugger.GetProgramVersion(existingProcess.MainModule.FileName); + if (version.StartsWith("v1.0")) { + MessageService.ShowMessage("${res:XML.MainMenu.DebugMenu.Error.Net10NotSupported}"); + } else { + if (DebugStarting != null) + DebugStarting(this, EventArgs.Empty); + + try { + // set the JIT flag for evaluating optimized code + Process.DebugMode = DebugModeFlag.Debug; + Process process = debugger.Attach(existingProcess); + attached = true; + SelectProcess(process); + + process.Modules.Added += process_Modules_Added; + } catch (System.Exception e) { + // CORDBG_E_DEBUGGER_ALREADY_ATTACHED + if (e is COMException || e is UnauthorizedAccessException) { + string msg = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Error.CannotAttachToProcess}"); + MessageService.ShowMessage(msg + " " + e.Message); + + if (DebugStopped != null) + DebugStopped(this, EventArgs.Empty); + } else { + throw; + } + } + } + } + + public void Detach() + { + debugger.Detach(); + } + + public void StartWithoutDebugging(ProcessStartInfo processStartInfo) + { + System.Diagnostics.Process.Start(processStartInfo); + } + + public void Stop() + { + if (!IsDebugging) { + MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.Stop}"); + return; + } + if (IsAttached) { + StopAttachedProcessDialogResult result = ShowStopAttachedProcessDialog(); + switch (result) { + case StopAttachedProcessDialogResult.Terminate: + debuggedProcess.Terminate(); + attached = false; + break; + case StopAttachedProcessDialogResult.Detach: + Detach(); + attached = false; + break; + } + } else { + debuggedProcess.Terminate(); + } + } + + // ExecutionControl: + + public void Break() + { + if (!IsDebugging) { + MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.Break}"); + return; + } + if (!IsProcessRunning) { + MessageService.ShowMessage(errorProcessPaused, "${res:XML.MainMenu.DebugMenu.Break}"); + return; + } + debuggedProcess.Break(); + } + + public void Continue() + { + if (!IsDebugging) { + MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.Continue}"); + return; + } + if (IsProcessRunning) { + MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.Continue}"); + return; + } + debuggedProcess.AsyncContinue(); + } + + // Stepping: + Debugger.StackFrame GetStackFrame() + { + bool isMatch = false; + int line = -1; + int[] ilRange = null; + + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + int typeToken = frame.MethodInfo.DeclaringType.MetadataToken; + int methodToken = frame.MethodInfo.MetadataToken; + + // get the mapped instruction from the current line marker or the next one + if (!debuggerDecompilerService.GetILAndLineNumber(typeToken, methodToken, frame.IP, out ilRange, out line, out isMatch)){ + frame.SourceCodeLine = 0; + frame.ILRanges = new [] { 0, 1 }; + } else { + frame.SourceCodeLine = line; + frame.ILRanges = ilRange; + } + + return frame; + } + + public void StepInto() + { + if (!IsDebugging) { + MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepInto}"); + return; + } + + if (debuggedProcess.IsRunning) { + MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.StepInto}"); + return; + } + + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + if (frame == null) { + MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}"); + } else { + if (!frame.HasSymbols) { + // get frame info from external code mappings + frame = GetStackFrame(); + } + + frame.AsyncStepInto(); + } + } + + public void StepOver() + { + if (!IsDebugging) { + MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOver}"); + return; + } + + if (debuggedProcess.IsRunning) { + MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.StepOver}"); + return; + } + + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + if (frame == null) { + MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOver}"); + } else { + if (!frame.HasSymbols) { + // get frame info from external code mappings + frame = GetStackFrame(); + } + + frame.AsyncStepOver(); + } + } + + public void StepOut() + { + if (!IsDebugging) { + MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOut}"); + return; + } + + if (debuggedProcess.IsRunning) { + MessageService.ShowMessage(errorProcessRunning, "${res:XML.MainMenu.DebugMenu.StepOut}"); + return; + } + + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + if (frame == null) { + MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}"); + } else { + if (!frame.HasSymbols) { + // get frame info from external code mappings + frame = GetStackFrame(); + } + + frame.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; + } + + var frame = debuggedProcess.GetCurrentExecutingFrame(); + if (frame == null) + return null; + object data = debuggerDecompilerService.GetLocalVariableIndex(frame.MethodInfo.DeclaringType.MetadataToken, + frame.MethodInfo.MetadataToken, + variableName); + // evaluate expression + return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, frame, data); + } + + /// + /// Gets Expression for given variable. Can throw GetValueException. + /// Thrown when getting expression fails. Exception message explains reason. + /// + public 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(); + Debugger.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 || debuggedProcess.SelectedThread.MostRecentStackFrame != 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(null, image, variableName, tooltipExpression); + expressionNode.ImageName = imageName; + return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = debuggedProcess.GetCurrentExecutingFrame().HasSymbols }; + } catch (System.Exception ex) { + LoggingService.Error("Error on GetTooltipControl: " + ex.Message); + return null; + } + } + + public ITreeNode GetNode(string variable, string currentImageName = null) + { + try { + var expression = GetExpression(variable); + string imageName; + IImage image; + if (string.IsNullOrEmpty(currentImageName)) { + image = ExpressionNode.GetImageForLocalVariable(out imageName); + } + else { + image = new ResourceServiceImage(currentImageName); + imageName = currentImageName; + } + ExpressionNode expressionNode = new ExpressionNode(null, 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(); + } + + // get decompiler service + var items = AddInTree.BuildItems("/SharpDevelop/Services/DebuggerDecompilerService", null, false); + if (items.Count > 0) + debuggerDecompilerService = items[0]; + + // init NDebugger + 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 = null; + + if (bookmark is DecompiledBreakpointBookmark) { + try { + if (debuggerDecompilerService == null) { + LoggingService.Warn("No IDebuggerDecompilerService found!"); + return; + } + var dbb = (DecompiledBreakpointBookmark)bookmark; + MemberReference memberReference = null; + + string assemblyFile, typeName; + if (DecompiledBreakpointBookmark.GetAssemblyAndType(dbb.FileName, out assemblyFile, out typeName)) { + memberReference = dbb.GetMemberReference(debuggerDecompilerService.GetAssemblyResolver(assemblyFile)); + } + + int token = memberReference.MetadataToken.ToInt32(); + if (!debuggerDecompilerService.CheckMappings(token)) + debuggerDecompilerService.DecompileOnDemand(memberReference as TypeDefinition); + + int[] ilRanges; + int methodToken; + if (debuggerDecompilerService.GetILAndTokenByLineNumber(token, dbb.LineNumber, out ilRanges, out methodToken)) { + // create BP + breakpoint = new ILBreakpoint( + debugger, + memberReference.FullName, + dbb.LineNumber, + memberReference.MetadataToken.ToInt32(), + methodToken, + ilRanges[0], + dbb.IsEnabled); + + debugger.Breakpoints.Add(breakpoint); + } + } catch (System.Exception ex) { + LoggingService.Error("Error on DecompiledBreakpointBookmark: " + ex.Message); + } + } else { + breakpoint = debugger.Breakpoints.Add(bookmark.FileName, null, bookmark.LineNumber, 0, bookmark.IsEnabled); + } + + if (breakpoint == null) { + LoggingService.Warn(string.Format("unable to create breakpoint: {0}", bookmark.ToString())); + return; + } + + MethodInvoker 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 == null || breakpoint.OriginalLocation.CheckSum == null) { + bookmark.IsHealthy = true; + bookmark.Tooltip = null; + } else { + if (!File.Exists(bookmark.FileName)) + return; + + 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.ModulesAdded -= debuggedProcess_ModulesAdded; + } + debuggedProcess = process; + if (debuggedProcess != null) { + debuggedProcess.Paused += debuggedProcess_DebuggingPaused; + debuggedProcess.ExceptionThrown += debuggedProcess_ExceptionThrown; + debuggedProcess.Resumed += debuggedProcess_DebuggingResumed; + debuggedProcess.ModulesAdded += debuggedProcess_ModulesAdded; + + debuggedProcess.BreakAtBeginning = BreakAtBeginning; + } + // reset + BreakAtBeginning = false; + + JumpToCurrentLine(); + OnProcessSelected(new ProcessEventArgs(process)); + } + + void debuggedProcess_ModulesAdded(object sender, ModuleEventArgs e) + { + var currentModuleTypes = e.Module.GetNamesOfDefinedTypes(); + foreach (var bookmark in DebuggerService.Breakpoints.OfType()) { + var breakpoint = debugger.Breakpoints.FirstOrDefault( + b => b is ILBreakpoint && b.Line == bookmark.LineNumber && + ((ILBreakpoint)b).MetadataToken == bookmark.MemberReference.MetadataToken.ToInt32()); + if (breakpoint == null) + continue; + // set the breakpoint only if the module contains the type + if (!currentModuleTypes.Contains(breakpoint.TypeName)) + continue; + + breakpoint.SetBreakpoint(e.Module); + } + } + + 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, ProcessEventArgs e) + { + OnIsProcessRunningChanged(EventArgs.Empty); + DebuggerService.RemoveCurrentLineMarker(); + } + + void debuggedProcess_ExceptionThrown(object sender, ExceptionEventArgs e) + { + JumpToCurrentLine(); + + StringBuilder stacktraceBuilder = new StringBuilder(); + + if (e.IsUnhandled) { + // Need to intercept now so that we can evaluate properties + if (e.Process.SelectedThread.InterceptException(e.Exception)) { + stacktraceBuilder.AppendLine(e.Exception.ToString()); + string stackTrace; + try { + stackTrace = e.Exception.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.EndOfInnerException}")); + } catch (GetValueException) { + stackTrace = e.Process.SelectedThread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}")); + } + stacktraceBuilder.Append(stackTrace); + } else { + // For example, happens on stack overflow + stacktraceBuilder.AppendLine(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptException}")); + stacktraceBuilder.AppendLine(e.Exception.ToString()); + stacktraceBuilder.Append(e.Process.SelectedThread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}"))); + } + } else { + stacktraceBuilder.AppendLine(e.Exception.ToString()); + stacktraceBuilder.Append(e.Process.SelectedThread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}"))); + } + + string title = e.IsUnhandled ? StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Unhandled}") : StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Handled}"); + string message = string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Message}"), e.Exception.Type); + Bitmap icon = WinFormsResourceService.GetBitmap(e.IsUnhandled ? "Icons.32x32.Error" : "Icons.32x32.Warning"); + + DebuggeeExceptionForm.Show(debuggedProcess, title, message, stacktraceBuilder.ToString(), icon, e.IsUnhandled, e.Exception); + } + + public bool BreakAndInterceptHandledException(Debugger.Exception exception) + { + if (!debuggedProcess.SelectedThread.InterceptException(exception)) { + MessageService.ShowError("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptHandledException}"); + return false; + } + JumpToCurrentLine(); + return true; + } + + public void JumpToCurrentLine() + { + if (debuggedProcess == null || debuggedProcess.SelectedThread == null) + return; + + WorkbenchSingleton.MainWindow.Activate(); + + if (debuggedProcess.IsSelectedFrameForced()) { + if (debuggedProcess.SelectedStackFrame != null && debuggedProcess.SelectedStackFrame.HasSymbols) { + JumpToSourceCode(); + } else { + JumpToDecompiledCode(debuggedProcess.SelectedStackFrame); + } + } else { + var frame = debuggedProcess.SelectedThread.MostRecentStackFrame; + // other pause reasons + if (frame != null && frame.HasSymbols) { + JumpToSourceCode(); + } else { + // use most recent stack frame because we don't have the symbols + JumpToDecompiledCode(debuggedProcess.SelectedThread.MostRecentStackFrame); + } + } + } + + void JumpToSourceCode() + { + if (debuggedProcess == null || debuggedProcess.SelectedThread == null) + return; + + SourcecodeSegment nextStatement = debuggedProcess.NextStatement; + if (nextStatement != null) { + DebuggerService.RemoveCurrentLineMarker(); + DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); + } + } + + void JumpToDecompiledCode(Debugger.StackFrame frame) + { + if (frame == null) { + LoggingService.Error("No stack frame!"); + return; + } + + if (debuggerDecompilerService == null) { + LoggingService.Warn("No IDebuggerDecompilerService found!"); + return; + } + + // check for options - if these options are enabled, debugging decompiled code should not continue + if (!debuggedProcess.Options.DecompileCodeWithoutSymbols) { + LoggingService.Info("Decompiled code debugging is disabled!"); + return; + } + DebuggerService.RemoveCurrentLineMarker(); + // get external data + int typeToken = frame.MethodInfo.DeclaringType.MetadataToken; + int methodToken = frame.MethodInfo.MetadataToken; + int ilOffset = frame.IP; + int[] ilRanges = null; + int line = -1; + bool isMatch = false; + var debugType = (DebugType)frame.MethodInfo.DeclaringType; + debuggerDecompilerService.DebugStepInformation = Tuple.Create(methodToken, ilOffset); + + if (debuggerDecompilerService.GetILAndLineNumber(typeToken, methodToken, ilOffset, out ilRanges, out line, out isMatch)) { + // update marker & navigate to line + NavigationService.NavigateTo(debugType.DebugModule.FullPath, + debugType.FullNameWithoutGenericArguments, + IDStringProvider.GetIDString(frame.MethodInfo), + line); + } else { + // no line => do decompilation + NavigationService.NavigateTo(debugType.DebugModule.FullPath, + debugType.FullNameWithoutGenericArguments, + IDStringProvider.GetIDString(frame.MethodInfo)); + + } + } + + StopAttachedProcessDialogResult ShowStopAttachedProcessDialog() + { + string caption = StringParser.Parse("${res:XML.MainMenu.DebugMenu.Stop}"); + string message = StringParser.Parse("${res:MainWindow.Windows.Debug.StopProcessDialog.Message}"); + string[] buttonLabels = new string[] { StringParser.Parse("${res:XML.MainMenu.DebugMenu.Detach}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Terminate}"), StringParser.Parse("${res:Global.CancelButtonText}") }; + return (StopAttachedProcessDialogResult)MessageService.ShowCustomDialog(caption, message, (int)StopAttachedProcessDialogResult.Detach, (int)StopAttachedProcessDialogResult.Cancel, buttonLabels); + } + + void process_Modules_Added(object sender, CollectionItemEventArgs e) + { + if (ProjectService.OpenSolution == null) + return; + + ProjectService.OpenSolution.Projects + .Where(p => e.Item.Name.IndexOf(p.Name) >= 0) + .ForEach(p => e.Item.LoadSymbolsFromDisk(new []{ Path.GetDirectoryName(p.OutputAssemblyFullPath) })); + } + } +} diff --git a/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs b/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs index bca887514f..30fc6fa15b 100644 --- a/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs +++ b/src/AddIns/Debugger/Debugger.Core/Breakpoint.cs @@ -1,228 +1,228 @@ -// 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.Runtime.InteropServices; - -using Debugger.Interop.CorDebug; - -namespace Debugger -{ - public class Breakpoint: DebuggerObject - { - NDebugger debugger; - - string fileName; - byte[] checkSum; - int line; - int column; - bool enabled; - - SourcecodeSegment originalLocation; - - protected List corBreakpoints = new List(); - - public event EventHandler Hit; - public event EventHandler Set; - - [Debugger.Tests.Ignore] - public NDebugger Debugger { - get { return debugger; } - protected set { debugger = value; } - } - - public string FileName { - get { return fileName; } - } - - public byte[] CheckSum { - get { return checkSum; } - } - - public int Line { - get { return line; } - set { line = value; } - } - - public int Column { - get { return column; } - } - - public bool Enabled { - get { return enabled; } - set { - enabled = value; - foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) { - corBreakpoint.Activate(enabled ? 1 : 0); - } - } - } - - public SourcecodeSegment OriginalLocation { - get { return originalLocation; } - } - - public bool IsSet { - get { - return corBreakpoints.Count > 0; - } - } - - public string TypeName { - get; protected set; - } - - protected virtual void OnHit(BreakpointEventArgs e) - { - if (Hit != null) { - Hit(this, e); - } - } - - internal void NotifyHit() - { - OnHit(new BreakpointEventArgs(this)); - debugger.Breakpoints.OnHit(this); - } - - protected virtual void OnSet(BreakpointEventArgs e) - { - if (Set != null) { - Set(this, e); - } - } - - public Breakpoint() { } - - public Breakpoint(NDebugger debugger, ICorDebugFunctionBreakpoint corBreakpoint) - { - this.debugger = debugger; - this.corBreakpoints.Add(corBreakpoint); - } - - public Breakpoint(NDebugger debugger, string fileName, byte[] checkSum, int line, int column, bool enabled) - { - this.debugger = debugger; - this.fileName = fileName; - this.checkSum = checkSum; - this.line = line; - this.column = column; - this.enabled = enabled; - } - - internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint) - { - foreach(ICorDebugFunctionBreakpoint corFunBreakpoint in corBreakpoints) { - if (((ICorDebugBreakpoint)corFunBreakpoint).Equals(breakpoint)) return true; - } - return false; - } - - internal void Deactivate() - { - foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) { - #if DEBUG - // Get repro - corBreakpoint.Activate(0); - #else - try { - corBreakpoint.Activate(0); - } catch(COMException e) { - // Sometimes happens, but we had not repro yet. - // 0x80131301: Process was terminated. - if ((uint)e.ErrorCode == 0x80131301) - continue; - throw; - } - #endif - } - corBreakpoints.Clear(); - } - - internal void MarkAsDeactivated() - { - corBreakpoints.Clear(); - } - - public virtual bool SetBreakpoint(Module module) - { - if (this.fileName == null) - return false; - - SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, CheckSum, Line, Column); - if (segment == null) return false; - - originalLocation = segment; - - ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart); - corBreakpoint.Activate(enabled ? 1 : 0); - - corBreakpoints.Add(corBreakpoint); - - OnSet(new BreakpointEventArgs(this)); - - return true; - } - - /// Remove this breakpoint - public void Remove() - { - debugger.Breakpoints.Remove(this); - } - } - - public class ILBreakpoint : Breakpoint - { - public ILBreakpoint(NDebugger debugger, string typeName, int line, int metadataToken, int memberToken, int offset, bool enabled) - { - this.Debugger = debugger; - this.Line = line; - this.TypeName = typeName; - this.MetadataToken = metadataToken; - this.MemberMetadataToken = memberToken; - this.ILOffset = offset; - this.Enabled = enabled; - } - - public int MetadataToken { get; private set; } - - public int MemberMetadataToken { get; private set; } - - public int ILOffset { get; private set; } - - public override bool SetBreakpoint(Module module) - { - SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, MemberMetadataToken, ILOffset); - if (segment == null) - return false; - try { - ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart); - corBreakpoint.Activate(Enabled ? 1 : 0); - corBreakpoints.Add(corBreakpoint); - - OnSet(new BreakpointEventArgs(this)); - return true; - } catch - #if DEBUG - (System.Exception) - #endif - { - return false; - } - } - } - - [Serializable] - public class BreakpointEventArgs : DebuggerEventArgs - { - public Breakpoint Breakpoint { - get; private set; - } - - public BreakpointEventArgs(Breakpoint breakpoint): base(breakpoint.Debugger) - { - this.Breakpoint = breakpoint; - } - } -} +// 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.Runtime.InteropServices; + +using Debugger.Interop.CorDebug; + +namespace Debugger +{ + public class Breakpoint: DebuggerObject + { + NDebugger debugger; + + string fileName; + byte[] checkSum; + int line; + int column; + bool enabled; + + SourcecodeSegment originalLocation; + + protected List corBreakpoints = new List(); + + public event EventHandler Hit; + public event EventHandler Set; + + [Debugger.Tests.Ignore] + public NDebugger Debugger { + get { return debugger; } + protected set { debugger = value; } + } + + public string FileName { + get { return fileName; } + } + + public byte[] CheckSum { + get { return checkSum; } + } + + public int Line { + get { return line; } + set { line = value; } + } + + public int Column { + get { return column; } + } + + public bool Enabled { + get { return enabled; } + set { + enabled = value; + foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) { + corBreakpoint.Activate(enabled ? 1 : 0); + } + } + } + + public SourcecodeSegment OriginalLocation { + get { return originalLocation; } + } + + public bool IsSet { + get { + return corBreakpoints.Count > 0; + } + } + + public string TypeName { + get; protected set; + } + + protected virtual void OnHit(BreakpointEventArgs e) + { + if (Hit != null) { + Hit(this, e); + } + } + + internal void NotifyHit() + { + OnHit(new BreakpointEventArgs(this)); + debugger.Breakpoints.OnHit(this); + } + + protected virtual void OnSet(BreakpointEventArgs e) + { + if (Set != null) { + Set(this, e); + } + } + + public Breakpoint() { } + + public Breakpoint(NDebugger debugger, ICorDebugFunctionBreakpoint corBreakpoint) + { + this.debugger = debugger; + this.corBreakpoints.Add(corBreakpoint); + } + + public Breakpoint(NDebugger debugger, string fileName, byte[] checkSum, int line, int column, bool enabled) + { + this.debugger = debugger; + this.fileName = fileName; + this.checkSum = checkSum; + this.line = line; + this.column = column; + this.enabled = enabled; + } + + internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint) + { + foreach(ICorDebugFunctionBreakpoint corFunBreakpoint in corBreakpoints) { + if (((ICorDebugBreakpoint)corFunBreakpoint).Equals(breakpoint)) return true; + } + return false; + } + + internal void Deactivate() + { + foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) { + #if DEBUG + // Get repro + corBreakpoint.Activate(0); + #else + try { + corBreakpoint.Activate(0); + } catch(COMException e) { + // Sometimes happens, but we had not repro yet. + // 0x80131301: Process was terminated. + if ((uint)e.ErrorCode == 0x80131301) + continue; + throw; + } + #endif + } + corBreakpoints.Clear(); + } + + internal void MarkAsDeactivated() + { + corBreakpoints.Clear(); + } + + public virtual bool SetBreakpoint(Module module) + { + if (this.fileName == null) + return false; + + SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, CheckSum, Line, Column); + if (segment == null) return false; + + originalLocation = segment; + + ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart); + corBreakpoint.Activate(enabled ? 1 : 0); + + corBreakpoints.Add(corBreakpoint); + + OnSet(new BreakpointEventArgs(this)); + + return true; + } + + /// Remove this breakpoint + public void Remove() + { + debugger.Breakpoints.Remove(this); + } + } + + public class ILBreakpoint : Breakpoint + { + public ILBreakpoint(NDebugger debugger, string typeName, int line, int metadataToken, int memberToken, int offset, bool enabled) + { + this.Debugger = debugger; + this.Line = line; + this.TypeName = typeName; + this.MetadataToken = metadataToken; + this.MemberMetadataToken = memberToken; + this.ILOffset = offset; + this.Enabled = enabled; + } + + public int MetadataToken { get; private set; } + + public int MemberMetadataToken { get; private set; } + + public int ILOffset { get; private set; } + + public override bool SetBreakpoint(Module module) + { + SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, MemberMetadataToken, ILOffset); + if (segment == null) + return false; + try { + ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart); + corBreakpoint.Activate(Enabled ? 1 : 0); + corBreakpoints.Add(corBreakpoint); + + OnSet(new BreakpointEventArgs(this)); + return true; + } catch + #if DEBUG + (System.Exception) + #endif + { + return false; + } + } + } + + [Serializable] + public class BreakpointEventArgs : DebuggerEventArgs + { + public Breakpoint Breakpoint { + get; private set; + } + + public BreakpointEventArgs(Breakpoint breakpoint): base(breakpoint.Debugger) + { + this.Breakpoint = breakpoint; + } + } +} diff --git a/src/AddIns/Debugger/Debugger.Core/Interop/MetaData.cs b/src/AddIns/Debugger/Debugger.Core/Interop/MetaData.cs index b0900b29c1..f424b99f55 100644 --- a/src/AddIns/Debugger/Debugger.Core/Interop/MetaData.cs +++ b/src/AddIns/Debugger/Debugger.Core/Interop/MetaData.cs @@ -1,6 +1,6 @@ -// 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) - +// 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) + #pragma warning disable 108, 1591 using System; @@ -14,7 +14,7 @@ namespace Debugger.Interop.MetaData { public uint ridOfField; public uint ulOffset; - } + } [System.Flags()] public enum ClassFieldAttribute: uint { @@ -46,7 +46,7 @@ namespace Debugger.Interop.MetaData fdHasFieldMarshal = 0x1000, // Field has marshalling information. fdHasDefault = 0x8000, // Field has default. fdHasFieldRVA = 0x0100, // Field has RVA. - } + } public enum CorCallingConvention: uint { IMAGE_CEE_CS_CALLCONV_DEFAULT = 0x0, @@ -63,7 +63,7 @@ namespace Debugger.Interop.MetaData IMAGE_CEE_CS_CALLCONV_MASK = 0x0f, // Calling convention is bottom 4 bits IMAGE_CEE_CS_CALLCONV_HASTHIS = 0x20, // Top bit indicates a 'this' parameter IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS = 0x40, // This parameter is explicitly in the signature - } + } public enum CorMethodAttr: uint { // member access mask - Use this mask to retrieve accessibility information. @@ -104,7 +104,7 @@ namespace Debugger.Interop.MetaData mdHasSecurity = 0x4000, // Method has security associate with it. mdRequireSecObject = 0x8000, // Method calls another method containing security code. - } + } public enum CorTokenType: uint { Module = 0x00000000, // @@ -275,4 +275,4 @@ namespace Debugger.Interop.MetaData } } -#pragma warning restore 108, 1591 +#pragma warning restore 108, 1591 diff --git a/src/AddIns/Debugger/Debugger.Core/Process.cs b/src/AddIns/Debugger/Debugger.Core/Process.cs index 268bd63f3f..7d2f245410 100644 --- a/src/AddIns/Debugger/Debugger.Core/Process.cs +++ b/src/AddIns/Debugger/Debugger.Core/Process.cs @@ -1,754 +1,754 @@ -// 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.Runtime.InteropServices; - -using Debugger.Interop.CorDebug; -using Debugger.Interop.CorSym; -using ICSharpCode.NRefactory.Ast; -using ICSharpCode.NRefactory.Visitors; - -namespace Debugger -{ - internal enum DebuggeeStateAction { Keep, Clear } - - /// - /// Debug Mode Flags. - /// - public enum DebugModeFlag - { - /// - /// Run in the same mode as without debugger. - /// - Default, - /// - /// Run in forced optimized mode. - /// - Optimized, - /// - /// Run in debug mode (easy inspection) but slower. - /// - Debug, - /// - /// Run in ENC mode (ENC possible) but even slower than debug - /// - Enc - } - - public class Process: DebuggerObject - { - NDebugger debugger; - - ICorDebugProcess corProcess; - ManagedCallback callbackInterface; - - EvalCollection activeEvals; - ModuleCollection modules; - ThreadCollection threads; - AppDomainCollection appDomains; - - string workingDirectory; - - - public NDebugger Debugger { - get { return debugger; } - } - - internal ICorDebugProcess CorProcess { - get { return corProcess; } - } - - public Options Options { - get { return debugger.Options; } - } - - public string DebuggeeVersion { - get { return debugger.DebuggeeVersion; } - } - - internal ManagedCallback CallbackInterface { - get { return callbackInterface; } - } - - public EvalCollection ActiveEvals { - get { return activeEvals; } - } - - internal bool Evaluating { - get { return activeEvals.Count > 0; } - } - - public ModuleCollection Modules { - get { return modules; } - } - - public ThreadCollection Threads { - get { return threads; } - } - - public Thread SelectedThread { - get { return this.Threads.Selected; } - set { this.Threads.Selected = value; } - } - - public StackFrame SelectedStackFrame { - get { - if (SelectedThread == null) { - return null; - } else { - return SelectedThread.SelectedStackFrame; - } - } - } - - public SourcecodeSegment NextStatement { - get { - if (SelectedStackFrame == null || IsRunning) { - return null; - } else { - return SelectedStackFrame.NextStatement; - } - } - } - - public bool BreakAtBeginning { - get; - set; - } - - public AppDomainCollection AppDomains { - get { return appDomains; } - } - - List steppers = new List(); - - internal List Steppers { - get { return steppers; } - } - - public string WorkingDirectory { - get { return workingDirectory; } - } - - public static DebugModeFlag DebugMode { get; set; } - - internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory) - { - this.debugger = debugger; - this.corProcess = corProcess; - this.workingDirectory = workingDirectory; - - this.callbackInterface = new ManagedCallback(this); - - activeEvals = new EvalCollection(debugger); - modules = new ModuleCollection(debugger); - modules.Added += OnModulesAdded; - threads = new ThreadCollection(debugger); - appDomains = new AppDomainCollection(debugger); - } - - static unsafe public Process CreateProcess(NDebugger debugger, string filename, string workingDirectory, string arguments) - { - debugger.TraceMessage("Executing " + filename + " " + arguments); - - uint[] processStartupInfo = new uint[17]; - processStartupInfo[0] = sizeof(uint) * 17; - uint[] processInfo = new uint[4]; - - ICorDebugProcess outProcess; - - if (workingDirectory == null || workingDirectory == "") { - workingDirectory = System.IO.Path.GetDirectoryName(filename); - } - - _SECURITY_ATTRIBUTES secAttr = new _SECURITY_ATTRIBUTES(); - secAttr.bInheritHandle = 0; - secAttr.lpSecurityDescriptor = IntPtr.Zero; - secAttr.nLength = (uint)sizeof(_SECURITY_ATTRIBUTES); - - fixed (uint* pprocessStartupInfo = processStartupInfo) - fixed (uint* pprocessInfo = processInfo) - outProcess = - debugger.CorDebug.CreateProcess( - filename, // lpApplicationName - // If we do not prepend " ", the first argument migh just get lost - " " + arguments, // lpCommandLine - ref secAttr, // lpProcessAttributes - ref secAttr, // lpThreadAttributes - 1,//TRUE // bInheritHandles - 0x00000010 /*CREATE_NEW_CONSOLE*/, // dwCreationFlags - IntPtr.Zero, // lpEnvironment - workingDirectory, // lpCurrentDirectory - (uint)pprocessStartupInfo, // lpStartupInfo - (uint)pprocessInfo, // lpProcessInformation, - CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS // debuggingFlags - ); - - return new Process(debugger, outProcess, workingDirectory); - } - - /// Fired when System.Diagnostics.Trace.WriteLine() is called in debuged process - public event EventHandler LogMessage; - - protected internal virtual void OnLogMessage(MessageEventArgs arg) - { - TraceMessage ("Debugger event: OnLogMessage"); - if (LogMessage != null) { - LogMessage(this, arg); - } - } - - public void TraceMessage(string message, params object[] args) - { - if (args.Length > 0) - message = string.Format(message, args); - System.Diagnostics.Debug.WriteLine("Debugger:" + message); - debugger.OnDebuggerTraceMessage(new MessageEventArgs(this, message)); - } - - /// Read the specified amount of memory at the given memory address - /// The content of the memory. The amount of the read memory may be less then requested. - public unsafe byte[] ReadMemory(ulong address, int size) - { - byte[] buffer = new byte[size]; - int readCount; - fixed(byte* pBuffer = buffer) { - readCount = (int)corProcess.ReadMemory(address, (uint)size, new IntPtr(pBuffer)); - } - if (readCount != size) Array.Resize(ref buffer, readCount); - return buffer; - } - - /// Writes the given buffer at the specified memory address - /// The number of bytes written - public unsafe int WriteMemory(ulong address, byte[] buffer) - { - if (buffer.Length == 0) return 0; - int written; - fixed(byte* pBuffer = buffer) { - written = (int)corProcess.WriteMemory(address, (uint)buffer.Length, new IntPtr(pBuffer)); - } - return written; - } - - internal Thread GetThread(ICorDebugThread corThread) - { - foreach(Thread thread in this.Threads) { - if (thread.CorThread == corThread) { - return thread; - } - } - Thread t = new Thread(this, corThread); - this.Threads.Add(t); - return t; - } - - #region Exceptions - - public event EventHandler ExceptionThrown; - - protected internal virtual void OnExceptionThrown(ExceptionEventArgs e) - { - TraceMessage ("Debugger event: OnExceptionThrown()"); - if (ExceptionThrown != null) { - ExceptionThrown(this, e); - } - } - - #endregion - - // State control for the process - - internal bool TerminateCommandIssued = false; - internal Queue BreakpointHitEventQueue = new Queue(); - internal Dictionary ExpressionsCache = new Dictionary(); - - #region Events - - public event EventHandler Paused; - public event EventHandler Resumed; - - // HACK: public - public virtual void OnPaused() - { - AssertPaused(); - // No real purpose - just additional check - if (callbackInterface.IsInCallback) throw new DebuggerException("Can not raise event within callback."); - TraceMessage ("Debugger event: OnPaused()"); - if (Paused != null) { - foreach(Delegate d in Paused.GetInvocationList()) { - if (IsRunning) { - TraceMessage ("Skipping OnPaused delegate because process has resumed"); - break; - } - if (this.TerminateCommandIssued || this.HasExited) { - TraceMessage ("Skipping OnPaused delegate because process has exited"); - break; - } - d.DynamicInvoke(this, new ProcessEventArgs(this)); - } - } - } - - protected virtual void OnResumed() - { - AssertRunning(); - if (callbackInterface.IsInCallback) - throw new DebuggerException("Can not raise event within callback."); - TraceMessage ("Debugger event: OnResumed()"); - if (Resumed != null) { - Resumed(this, new ProcessEventArgs(this)); - } - } - - #endregion - - #region PauseSession & DebugeeState - - PauseSession pauseSession; - DebuggeeState debuggeeState; - - /// - /// Indentification of the current debugger session. This value changes whenever debugger is continued - /// - public PauseSession PauseSession { - get { return pauseSession; } - } - - /// - /// Indentification of the state of the debugee. This value changes whenever the state of the debugee significatntly changes - /// - public DebuggeeState DebuggeeState { - get { return debuggeeState; } - } - - /// Puts the process into a paused state - internal void NotifyPaused(PausedReason pauseReason) - { - AssertRunning(); - pauseSession = new PauseSession(this, pauseReason); - if (debuggeeState == null) { - debuggeeState = new DebuggeeState(this); - } - } - - /// Puts the process into a resumed state - internal void NotifyResumed(DebuggeeStateAction action) - { - AssertPaused(); - pauseSession = null; - if (action == DebuggeeStateAction.Clear) { - if (debuggeeState == null) throw new DebuggerException("Debugee state already cleared"); - debuggeeState = null; - this.ExpressionsCache.Clear(); - } - } - - /// Sets up the eviroment and raises user events - internal void RaisePausedEvents() - { - AssertPaused(); - DisableAllSteppers(); - CheckSelectedStackFrames(); - SelectMostRecentStackFrameWithLoadedSymbols(); - - // if CurrentException is set an exception has occurred. - if (SelectedThread.CurrentException != null) { - ExceptionEventArgs args = new ExceptionEventArgs(this, this.SelectedThread.CurrentException, this.SelectedThread.CurrentExceptionType, this.SelectedThread.CurrentExceptionIsUnhandled); - OnExceptionThrown(args); - // clear exception, it is being processed by the debugger. - this.SelectedThread.CurrentException = null; - // The event could have resumed or killed the process - if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; - } - - while(BreakpointHitEventQueue.Count > 0) { - Breakpoint breakpoint = BreakpointHitEventQueue.Dequeue(); - breakpoint.NotifyHit(); - // The event could have resumed or killed the process - if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; - } - - OnPaused(); - // The event could have resumed the process - if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; - } - - #endregion - - internal void AssertPaused() - { - if (IsRunning) { - throw new DebuggerException("Process is not paused."); - } - } - - internal void AssertRunning() - { - if (IsPaused) { - throw new DebuggerException("Process is not running."); - } - } - - public bool IsRunning { - get { return pauseSession == null; } - } - - public uint Id { - get { return corProcess.GetID(); } - } - - public bool IsPaused { - get { return !IsRunning; } - } - - bool hasExited = false; - - public event EventHandler Exited; - - public bool HasExited { - get { - return hasExited; - } - } - - internal void NotifyHasExited() - { - if(!hasExited) { - hasExited = true; - if (Exited != null) { - Exited(this, new ProcessEventArgs(this)); - } - // Expire pause seesion first - if (IsPaused) { - NotifyResumed(DebuggeeStateAction.Clear); - } - debugger.Processes.Remove(this); - } - } - - public void Break() - { - AssertRunning(); - - corProcess.Stop(uint.MaxValue); // Infinite; ignored anyway - - NotifyPaused(PausedReason.ForcedBreak); - RaisePausedEvents(); - } - - public void Detach() - { - if (IsRunning) { - corProcess.Stop(uint.MaxValue); - NotifyPaused(PausedReason.ForcedBreak); - } - - // This is necessary for detach - foreach(Stepper s in this.Steppers) { - if (s.CorStepper.IsActive() == 1) { - s.CorStepper.Deactivate(); - } - } - this.Steppers.Clear(); - - corProcess.Detach(); - - // modules - foreach(Module m in this.Modules) - { - m.Dispose(); - } - - this.modules.Clear(); - - // threads - this.threads.Clear(); - - NotifyHasExited(); - } - - public void Continue() - { - AsyncContinue(); - WaitForPause(); - } - - internal Thread[] UnsuspendedThreads { - get { - List unsuspendedThreads = new List(this.Threads.Count); - foreach(Thread t in this.Threads) { - if (!t.Suspended) - unsuspendedThreads.Add(t); - } - return unsuspendedThreads.ToArray(); - } - } - - /// - /// Resume execution and run all threads not marked by the user as susspended. - /// - public void AsyncContinue() - { - AsyncContinue(DebuggeeStateAction.Clear, this.UnsuspendedThreads, CorDebugThreadState.THREAD_RUN); - } - - internal CorDebugThreadState NewThreadState = CorDebugThreadState.THREAD_RUN; - - /// Null to keep current setting - /// What happens to created threads. Null to keep current setting - internal void AsyncContinue(DebuggeeStateAction action, Thread[] threadsToRun, CorDebugThreadState? newThreadState) - { - AssertPaused(); - - if (threadsToRun != null) { -// corProcess.SetAllThreadsDebugState(CorDebugThreadState.THREAD_SUSPEND, null); -// Note: There is unreported thread, stopping it prevents the debugee from exiting -// It is not corProcess.GetHelperThreadID -// ICorDebugThread[] ts = new ICorDebugThread[corProcess.EnumerateThreads().GetCount()]; -// corProcess.EnumerateThreads().Next((uint)ts.Length, ts); - foreach(Thread t in this.Threads) { - CorDebugThreadState state = Array.IndexOf(threadsToRun, t) == -1 ? CorDebugThreadState.THREAD_SUSPEND : CorDebugThreadState.THREAD_RUN; - try { - t.CorThread.SetDebugState(state); - } catch (COMException e) { - // The state of the thread is invalid. (Exception from HRESULT: 0x8013132D) - // It can happen for example when thread has not started yet - if ((uint)e.ErrorCode == 0x8013132D) { - // TraceMessage("Can not suspend thread - The state of the thread is invalid. Thread ID = " + t.CorThread.GetID()); - } else { - throw; - } - } - } - } - - if (newThreadState != null) { - this.NewThreadState = newThreadState.Value; - } - - NotifyResumed(action); - corProcess.Continue(0); - if (this.Options.Verbose) { - this.TraceMessage("Continue"); - } - - if (action == DebuggeeStateAction.Clear) { - OnResumed(); - } - } - - /// Terminates the execution of the process - public void Terminate() - { - AsyncTerminate(); - // Wait until ExitProcess callback is received - WaitForExit(); - } - - /// Terminates the execution of the process - public void AsyncTerminate() - { - // Resume stoped tread - if (this.IsPaused) { - // We might get more callbacks so we should maintain consistent sate - //AsyncContinue(); // Continue the process to get remaining callbacks - } - - // Expose race condition - drain callback queue - System.Threading.Thread.Sleep(0); - - // Stop&terminate - both must be called - corProcess.Stop(uint.MaxValue); - corProcess.Terminate(0); - this.TerminateCommandIssued = true; - - // Do not mark the process as exited - // This is done once ExitProcess callback is received - } - - /// - /// Clears the internal Expression cache used too speed up Expression evaluation. - /// Use this if your code evaluates expressions in a way which would cause - /// the cache to grow too large. The cache holds PermanentReferences so it - /// shouldn't grow larger than a few hundred items. - /// - public void ClearExpressionCache() - { - if (this.ExpressionsCache != null ){ - this.ExpressionsCache.Clear(); - } - } - - void SelectSomeThread() - { - if (this.SelectedThread != null && !this.SelectedThread.IsInValidState) { - this.SelectedThread = null; - } - if (this.SelectedThread == null) { - foreach(Thread thread in this.Threads) { - if (thread.IsInValidState) { - this.SelectedThread = thread; - break; - } - } - } - } - - internal void CheckSelectedStackFrames() - { - foreach(Thread thread in this.Threads) { - if (thread.IsInValidState) { - if (thread.SelectedStackFrame != null && thread.SelectedStackFrame.IsInvalid) { - thread.SelectedStackFrame = null; - } - } else { - thread.SelectedStackFrame = null; - } - } - } - - internal void SelectMostRecentStackFrameWithLoadedSymbols() - { - SelectSomeThread(); - if (this.SelectedThread != null) { - this.SelectedThread.SelectedStackFrame = null; - foreach (StackFrame stackFrame in this.SelectedThread.Callstack) { - if (stackFrame.HasSymbols) { - if (this.Options.StepOverDebuggerAttributes && stackFrame.MethodInfo.IsNonUserCode) - continue; - this.SelectedThread.SelectedStackFrame = stackFrame; - break; - } - } - } - } - - internal Stepper GetStepper(ICorDebugStepper corStepper) - { - foreach(Stepper stepper in this.Steppers) { - if (stepper.IsCorStepper(corStepper)) { - return stepper; - } - } - throw new DebuggerException("Stepper is not in collection"); - } - - internal void DisableAllSteppers() - { - foreach(Thread thread in this.Threads) { - thread.CurrentStepIn = null; - } - foreach(Stepper stepper in this.Steppers) { - stepper.Ignore = true; - } - } - - /// - /// Waits until the debugger pauses unless it is already paused. - /// Use PausedReason to find out why it paused. - /// - public void WaitForPause() - { - while(this.IsRunning && !this.HasExited) { - debugger.MTA2STA.WaitForCall(); - debugger.MTA2STA.PerformAllCalls(); - } - if (this.HasExited) throw new ProcessExitedException(); - } - - public void WaitForPause(TimeSpan timeout) - { - System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); - watch.Start(); - while(this.IsRunning && !this.HasExited) { - TimeSpan timeLeft = timeout - watch.Elapsed; - if (timeLeft <= TimeSpan.FromMilliseconds(10)) break; - //this.TraceMessage("Time left: " + timeLeft.TotalMilliseconds); - debugger.MTA2STA.WaitForCall(timeLeft); - debugger.MTA2STA.PerformAllCalls(); - } - if (this.HasExited) throw new ProcessExitedException(); - } - - /// - /// Waits until the precesses exits. - /// - public void WaitForExit() - { - while(!this.HasExited) { - debugger.MTA2STA.WaitForCall(); - debugger.MTA2STA.PerformAllCalls(); - } - } - - #region Break at begining - - private void OnModulesAdded(object sender, CollectionItemEventArgs e) - { - if (BreakAtBeginning) { - if (e.Item.SymReader == null) return; // No symbols - - try { - // create a BP at entry point - uint entryPoint = e.Item.SymReader.GetUserEntryPoint(); - if (entryPoint == 0) return; // no EP - var mainFunction = e.Item.CorModule.GetFunctionFromToken(entryPoint); - var corBreakpoint = mainFunction.CreateBreakpoint(); - corBreakpoint.Activate(1); - - // create a SD BP - var breakpoint = new Breakpoint(this.debugger, corBreakpoint); - this.debugger.Breakpoints.Add(breakpoint); - breakpoint.Hit += delegate { - if (breakpoint != null) - breakpoint.Remove(); - breakpoint = null; - }; - } catch { - // the app does not have an entry point - COM exception - } - BreakAtBeginning = false; - } - - if (ModulesAdded != null) - ModulesAdded(this, new ModuleEventArgs(e.Item)); - } - - #endregion - - public event EventHandler ModulesAdded; - - public StackFrame GetCurrentExecutingFrame() - { - if (IsRunning || SelectedThread == null) - return null; - - if (IsSelectedFrameForced()) { - return SelectedStackFrame; // selected from callstack or threads pads - } - - if (SelectedStackFrame != null) { - if (SelectedThread.MostRecentStackFrame != null) { - if (SelectedStackFrame.HasSymbols && SelectedThread.MostRecentStackFrame.HasSymbols) - return SelectedStackFrame; - else - return SelectedThread.MostRecentStackFrame; - } else { - return SelectedThread.MostRecentStackFrame; - } - } else { - return SelectedThread.MostRecentStackFrame; - } - } - - public bool IsSelectedFrameForced() - { - return pauseSession.PausedReason == PausedReason.CurrentFunctionChanged || - pauseSession.PausedReason == PausedReason.CurrentThreadChanged || - pauseSession.PausedReason == PausedReason.EvalComplete; - } - } -} +// 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.Runtime.InteropServices; + +using Debugger.Interop.CorDebug; +using Debugger.Interop.CorSym; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.NRefactory.Visitors; + +namespace Debugger +{ + internal enum DebuggeeStateAction { Keep, Clear } + + /// + /// Debug Mode Flags. + /// + public enum DebugModeFlag + { + /// + /// Run in the same mode as without debugger. + /// + Default, + /// + /// Run in forced optimized mode. + /// + Optimized, + /// + /// Run in debug mode (easy inspection) but slower. + /// + Debug, + /// + /// Run in ENC mode (ENC possible) but even slower than debug + /// + Enc + } + + public class Process: DebuggerObject + { + NDebugger debugger; + + ICorDebugProcess corProcess; + ManagedCallback callbackInterface; + + EvalCollection activeEvals; + ModuleCollection modules; + ThreadCollection threads; + AppDomainCollection appDomains; + + string workingDirectory; + + + public NDebugger Debugger { + get { return debugger; } + } + + internal ICorDebugProcess CorProcess { + get { return corProcess; } + } + + public Options Options { + get { return debugger.Options; } + } + + public string DebuggeeVersion { + get { return debugger.DebuggeeVersion; } + } + + internal ManagedCallback CallbackInterface { + get { return callbackInterface; } + } + + public EvalCollection ActiveEvals { + get { return activeEvals; } + } + + internal bool Evaluating { + get { return activeEvals.Count > 0; } + } + + public ModuleCollection Modules { + get { return modules; } + } + + public ThreadCollection Threads { + get { return threads; } + } + + public Thread SelectedThread { + get { return this.Threads.Selected; } + set { this.Threads.Selected = value; } + } + + public StackFrame SelectedStackFrame { + get { + if (SelectedThread == null) { + return null; + } else { + return SelectedThread.SelectedStackFrame; + } + } + } + + public SourcecodeSegment NextStatement { + get { + if (SelectedStackFrame == null || IsRunning) { + return null; + } else { + return SelectedStackFrame.NextStatement; + } + } + } + + public bool BreakAtBeginning { + get; + set; + } + + public AppDomainCollection AppDomains { + get { return appDomains; } + } + + List steppers = new List(); + + internal List Steppers { + get { return steppers; } + } + + public string WorkingDirectory { + get { return workingDirectory; } + } + + public static DebugModeFlag DebugMode { get; set; } + + internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory) + { + this.debugger = debugger; + this.corProcess = corProcess; + this.workingDirectory = workingDirectory; + + this.callbackInterface = new ManagedCallback(this); + + activeEvals = new EvalCollection(debugger); + modules = new ModuleCollection(debugger); + modules.Added += OnModulesAdded; + threads = new ThreadCollection(debugger); + appDomains = new AppDomainCollection(debugger); + } + + static unsafe public Process CreateProcess(NDebugger debugger, string filename, string workingDirectory, string arguments) + { + debugger.TraceMessage("Executing " + filename + " " + arguments); + + uint[] processStartupInfo = new uint[17]; + processStartupInfo[0] = sizeof(uint) * 17; + uint[] processInfo = new uint[4]; + + ICorDebugProcess outProcess; + + if (workingDirectory == null || workingDirectory == "") { + workingDirectory = System.IO.Path.GetDirectoryName(filename); + } + + _SECURITY_ATTRIBUTES secAttr = new _SECURITY_ATTRIBUTES(); + secAttr.bInheritHandle = 0; + secAttr.lpSecurityDescriptor = IntPtr.Zero; + secAttr.nLength = (uint)sizeof(_SECURITY_ATTRIBUTES); + + fixed (uint* pprocessStartupInfo = processStartupInfo) + fixed (uint* pprocessInfo = processInfo) + outProcess = + debugger.CorDebug.CreateProcess( + filename, // lpApplicationName + // If we do not prepend " ", the first argument migh just get lost + " " + arguments, // lpCommandLine + ref secAttr, // lpProcessAttributes + ref secAttr, // lpThreadAttributes + 1,//TRUE // bInheritHandles + 0x00000010 /*CREATE_NEW_CONSOLE*/, // dwCreationFlags + IntPtr.Zero, // lpEnvironment + workingDirectory, // lpCurrentDirectory + (uint)pprocessStartupInfo, // lpStartupInfo + (uint)pprocessInfo, // lpProcessInformation, + CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS // debuggingFlags + ); + + return new Process(debugger, outProcess, workingDirectory); + } + + /// Fired when System.Diagnostics.Trace.WriteLine() is called in debuged process + public event EventHandler LogMessage; + + protected internal virtual void OnLogMessage(MessageEventArgs arg) + { + TraceMessage ("Debugger event: OnLogMessage"); + if (LogMessage != null) { + LogMessage(this, arg); + } + } + + public void TraceMessage(string message, params object[] args) + { + if (args.Length > 0) + message = string.Format(message, args); + System.Diagnostics.Debug.WriteLine("Debugger:" + message); + debugger.OnDebuggerTraceMessage(new MessageEventArgs(this, message)); + } + + /// Read the specified amount of memory at the given memory address + /// The content of the memory. The amount of the read memory may be less then requested. + public unsafe byte[] ReadMemory(ulong address, int size) + { + byte[] buffer = new byte[size]; + int readCount; + fixed(byte* pBuffer = buffer) { + readCount = (int)corProcess.ReadMemory(address, (uint)size, new IntPtr(pBuffer)); + } + if (readCount != size) Array.Resize(ref buffer, readCount); + return buffer; + } + + /// Writes the given buffer at the specified memory address + /// The number of bytes written + public unsafe int WriteMemory(ulong address, byte[] buffer) + { + if (buffer.Length == 0) return 0; + int written; + fixed(byte* pBuffer = buffer) { + written = (int)corProcess.WriteMemory(address, (uint)buffer.Length, new IntPtr(pBuffer)); + } + return written; + } + + internal Thread GetThread(ICorDebugThread corThread) + { + foreach(Thread thread in this.Threads) { + if (thread.CorThread == corThread) { + return thread; + } + } + Thread t = new Thread(this, corThread); + this.Threads.Add(t); + return t; + } + + #region Exceptions + + public event EventHandler ExceptionThrown; + + protected internal virtual void OnExceptionThrown(ExceptionEventArgs e) + { + TraceMessage ("Debugger event: OnExceptionThrown()"); + if (ExceptionThrown != null) { + ExceptionThrown(this, e); + } + } + + #endregion + + // State control for the process + + internal bool TerminateCommandIssued = false; + internal Queue BreakpointHitEventQueue = new Queue(); + internal Dictionary ExpressionsCache = new Dictionary(); + + #region Events + + public event EventHandler Paused; + public event EventHandler Resumed; + + // HACK: public + public virtual void OnPaused() + { + AssertPaused(); + // No real purpose - just additional check + if (callbackInterface.IsInCallback) throw new DebuggerException("Can not raise event within callback."); + TraceMessage ("Debugger event: OnPaused()"); + if (Paused != null) { + foreach(Delegate d in Paused.GetInvocationList()) { + if (IsRunning) { + TraceMessage ("Skipping OnPaused delegate because process has resumed"); + break; + } + if (this.TerminateCommandIssued || this.HasExited) { + TraceMessage ("Skipping OnPaused delegate because process has exited"); + break; + } + d.DynamicInvoke(this, new ProcessEventArgs(this)); + } + } + } + + protected virtual void OnResumed() + { + AssertRunning(); + if (callbackInterface.IsInCallback) + throw new DebuggerException("Can not raise event within callback."); + TraceMessage ("Debugger event: OnResumed()"); + if (Resumed != null) { + Resumed(this, new ProcessEventArgs(this)); + } + } + + #endregion + + #region PauseSession & DebugeeState + + PauseSession pauseSession; + DebuggeeState debuggeeState; + + /// + /// Indentification of the current debugger session. This value changes whenever debugger is continued + /// + public PauseSession PauseSession { + get { return pauseSession; } + } + + /// + /// Indentification of the state of the debugee. This value changes whenever the state of the debugee significatntly changes + /// + public DebuggeeState DebuggeeState { + get { return debuggeeState; } + } + + /// Puts the process into a paused state + internal void NotifyPaused(PausedReason pauseReason) + { + AssertRunning(); + pauseSession = new PauseSession(this, pauseReason); + if (debuggeeState == null) { + debuggeeState = new DebuggeeState(this); + } + } + + /// Puts the process into a resumed state + internal void NotifyResumed(DebuggeeStateAction action) + { + AssertPaused(); + pauseSession = null; + if (action == DebuggeeStateAction.Clear) { + if (debuggeeState == null) throw new DebuggerException("Debugee state already cleared"); + debuggeeState = null; + this.ExpressionsCache.Clear(); + } + } + + /// Sets up the eviroment and raises user events + internal void RaisePausedEvents() + { + AssertPaused(); + DisableAllSteppers(); + CheckSelectedStackFrames(); + SelectMostRecentStackFrameWithLoadedSymbols(); + + // if CurrentException is set an exception has occurred. + if (SelectedThread.CurrentException != null) { + ExceptionEventArgs args = new ExceptionEventArgs(this, this.SelectedThread.CurrentException, this.SelectedThread.CurrentExceptionType, this.SelectedThread.CurrentExceptionIsUnhandled); + OnExceptionThrown(args); + // clear exception, it is being processed by the debugger. + this.SelectedThread.CurrentException = null; + // The event could have resumed or killed the process + if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; + } + + while(BreakpointHitEventQueue.Count > 0) { + Breakpoint breakpoint = BreakpointHitEventQueue.Dequeue(); + breakpoint.NotifyHit(); + // The event could have resumed or killed the process + if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; + } + + OnPaused(); + // The event could have resumed the process + if (this.IsRunning || this.TerminateCommandIssued || this.HasExited) return; + } + + #endregion + + internal void AssertPaused() + { + if (IsRunning) { + throw new DebuggerException("Process is not paused."); + } + } + + internal void AssertRunning() + { + if (IsPaused) { + throw new DebuggerException("Process is not running."); + } + } + + public bool IsRunning { + get { return pauseSession == null; } + } + + public uint Id { + get { return corProcess.GetID(); } + } + + public bool IsPaused { + get { return !IsRunning; } + } + + bool hasExited = false; + + public event EventHandler Exited; + + public bool HasExited { + get { + return hasExited; + } + } + + internal void NotifyHasExited() + { + if(!hasExited) { + hasExited = true; + if (Exited != null) { + Exited(this, new ProcessEventArgs(this)); + } + // Expire pause seesion first + if (IsPaused) { + NotifyResumed(DebuggeeStateAction.Clear); + } + debugger.Processes.Remove(this); + } + } + + public void Break() + { + AssertRunning(); + + corProcess.Stop(uint.MaxValue); // Infinite; ignored anyway + + NotifyPaused(PausedReason.ForcedBreak); + RaisePausedEvents(); + } + + public void Detach() + { + if (IsRunning) { + corProcess.Stop(uint.MaxValue); + NotifyPaused(PausedReason.ForcedBreak); + } + + // This is necessary for detach + foreach(Stepper s in this.Steppers) { + if (s.CorStepper.IsActive() == 1) { + s.CorStepper.Deactivate(); + } + } + this.Steppers.Clear(); + + corProcess.Detach(); + + // modules + foreach(Module m in this.Modules) + { + m.Dispose(); + } + + this.modules.Clear(); + + // threads + this.threads.Clear(); + + NotifyHasExited(); + } + + public void Continue() + { + AsyncContinue(); + WaitForPause(); + } + + internal Thread[] UnsuspendedThreads { + get { + List unsuspendedThreads = new List(this.Threads.Count); + foreach(Thread t in this.Threads) { + if (!t.Suspended) + unsuspendedThreads.Add(t); + } + return unsuspendedThreads.ToArray(); + } + } + + /// + /// Resume execution and run all threads not marked by the user as susspended. + /// + public void AsyncContinue() + { + AsyncContinue(DebuggeeStateAction.Clear, this.UnsuspendedThreads, CorDebugThreadState.THREAD_RUN); + } + + internal CorDebugThreadState NewThreadState = CorDebugThreadState.THREAD_RUN; + + /// Null to keep current setting + /// What happens to created threads. Null to keep current setting + internal void AsyncContinue(DebuggeeStateAction action, Thread[] threadsToRun, CorDebugThreadState? newThreadState) + { + AssertPaused(); + + if (threadsToRun != null) { +// corProcess.SetAllThreadsDebugState(CorDebugThreadState.THREAD_SUSPEND, null); +// Note: There is unreported thread, stopping it prevents the debugee from exiting +// It is not corProcess.GetHelperThreadID +// ICorDebugThread[] ts = new ICorDebugThread[corProcess.EnumerateThreads().GetCount()]; +// corProcess.EnumerateThreads().Next((uint)ts.Length, ts); + foreach(Thread t in this.Threads) { + CorDebugThreadState state = Array.IndexOf(threadsToRun, t) == -1 ? CorDebugThreadState.THREAD_SUSPEND : CorDebugThreadState.THREAD_RUN; + try { + t.CorThread.SetDebugState(state); + } catch (COMException e) { + // The state of the thread is invalid. (Exception from HRESULT: 0x8013132D) + // It can happen for example when thread has not started yet + if ((uint)e.ErrorCode == 0x8013132D) { + // TraceMessage("Can not suspend thread - The state of the thread is invalid. Thread ID = " + t.CorThread.GetID()); + } else { + throw; + } + } + } + } + + if (newThreadState != null) { + this.NewThreadState = newThreadState.Value; + } + + NotifyResumed(action); + corProcess.Continue(0); + if (this.Options.Verbose) { + this.TraceMessage("Continue"); + } + + if (action == DebuggeeStateAction.Clear) { + OnResumed(); + } + } + + /// Terminates the execution of the process + public void Terminate() + { + AsyncTerminate(); + // Wait until ExitProcess callback is received + WaitForExit(); + } + + /// Terminates the execution of the process + public void AsyncTerminate() + { + // Resume stoped tread + if (this.IsPaused) { + // We might get more callbacks so we should maintain consistent sate + //AsyncContinue(); // Continue the process to get remaining callbacks + } + + // Expose race condition - drain callback queue + System.Threading.Thread.Sleep(0); + + // Stop&terminate - both must be called + corProcess.Stop(uint.MaxValue); + corProcess.Terminate(0); + this.TerminateCommandIssued = true; + + // Do not mark the process as exited + // This is done once ExitProcess callback is received + } + + /// + /// Clears the internal Expression cache used too speed up Expression evaluation. + /// Use this if your code evaluates expressions in a way which would cause + /// the cache to grow too large. The cache holds PermanentReferences so it + /// shouldn't grow larger than a few hundred items. + /// + public void ClearExpressionCache() + { + if (this.ExpressionsCache != null ){ + this.ExpressionsCache.Clear(); + } + } + + void SelectSomeThread() + { + if (this.SelectedThread != null && !this.SelectedThread.IsInValidState) { + this.SelectedThread = null; + } + if (this.SelectedThread == null) { + foreach(Thread thread in this.Threads) { + if (thread.IsInValidState) { + this.SelectedThread = thread; + break; + } + } + } + } + + internal void CheckSelectedStackFrames() + { + foreach(Thread thread in this.Threads) { + if (thread.IsInValidState) { + if (thread.SelectedStackFrame != null && thread.SelectedStackFrame.IsInvalid) { + thread.SelectedStackFrame = null; + } + } else { + thread.SelectedStackFrame = null; + } + } + } + + internal void SelectMostRecentStackFrameWithLoadedSymbols() + { + SelectSomeThread(); + if (this.SelectedThread != null) { + this.SelectedThread.SelectedStackFrame = null; + foreach (StackFrame stackFrame in this.SelectedThread.Callstack) { + if (stackFrame.HasSymbols) { + if (this.Options.StepOverDebuggerAttributes && stackFrame.MethodInfo.IsNonUserCode) + continue; + this.SelectedThread.SelectedStackFrame = stackFrame; + break; + } + } + } + } + + internal Stepper GetStepper(ICorDebugStepper corStepper) + { + foreach(Stepper stepper in this.Steppers) { + if (stepper.IsCorStepper(corStepper)) { + return stepper; + } + } + throw new DebuggerException("Stepper is not in collection"); + } + + internal void DisableAllSteppers() + { + foreach(Thread thread in this.Threads) { + thread.CurrentStepIn = null; + } + foreach(Stepper stepper in this.Steppers) { + stepper.Ignore = true; + } + } + + /// + /// Waits until the debugger pauses unless it is already paused. + /// Use PausedReason to find out why it paused. + /// + public void WaitForPause() + { + while(this.IsRunning && !this.HasExited) { + debugger.MTA2STA.WaitForCall(); + debugger.MTA2STA.PerformAllCalls(); + } + if (this.HasExited) throw new ProcessExitedException(); + } + + public void WaitForPause(TimeSpan timeout) + { + System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); + watch.Start(); + while(this.IsRunning && !this.HasExited) { + TimeSpan timeLeft = timeout - watch.Elapsed; + if (timeLeft <= TimeSpan.FromMilliseconds(10)) break; + //this.TraceMessage("Time left: " + timeLeft.TotalMilliseconds); + debugger.MTA2STA.WaitForCall(timeLeft); + debugger.MTA2STA.PerformAllCalls(); + } + if (this.HasExited) throw new ProcessExitedException(); + } + + /// + /// Waits until the precesses exits. + /// + public void WaitForExit() + { + while(!this.HasExited) { + debugger.MTA2STA.WaitForCall(); + debugger.MTA2STA.PerformAllCalls(); + } + } + + #region Break at begining + + private void OnModulesAdded(object sender, CollectionItemEventArgs e) + { + if (BreakAtBeginning) { + if (e.Item.SymReader == null) return; // No symbols + + try { + // create a BP at entry point + uint entryPoint = e.Item.SymReader.GetUserEntryPoint(); + if (entryPoint == 0) return; // no EP + var mainFunction = e.Item.CorModule.GetFunctionFromToken(entryPoint); + var corBreakpoint = mainFunction.CreateBreakpoint(); + corBreakpoint.Activate(1); + + // create a SD BP + var breakpoint = new Breakpoint(this.debugger, corBreakpoint); + this.debugger.Breakpoints.Add(breakpoint); + breakpoint.Hit += delegate { + if (breakpoint != null) + breakpoint.Remove(); + breakpoint = null; + }; + } catch { + // the app does not have an entry point - COM exception + } + BreakAtBeginning = false; + } + + if (ModulesAdded != null) + ModulesAdded(this, new ModuleEventArgs(e.Item)); + } + + #endregion + + public event EventHandler ModulesAdded; + + public StackFrame GetCurrentExecutingFrame() + { + if (IsRunning || SelectedThread == null) + return null; + + if (IsSelectedFrameForced()) { + return SelectedStackFrame; // selected from callstack or threads pads + } + + if (SelectedStackFrame != null) { + if (SelectedThread.MostRecentStackFrame != null) { + if (SelectedStackFrame.HasSymbols && SelectedThread.MostRecentStackFrame.HasSymbols) + return SelectedStackFrame; + else + return SelectedThread.MostRecentStackFrame; + } else { + return SelectedThread.MostRecentStackFrame; + } + } else { + return SelectedThread.MostRecentStackFrame; + } + } + + public bool IsSelectedFrameForced() + { + return pauseSession.PausedReason == PausedReason.CurrentFunctionChanged || + pauseSession.PausedReason == PausedReason.CurrentThreadChanged || + pauseSession.PausedReason == PausedReason.EvalComplete; + } + } +} diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj index a57f66e918..7253edd3b6 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj @@ -1,250 +1,250 @@ - - - - {0162E499-42D0-409B-AA25-EED21F75336B} - Debug - AnyCPU - Library - ICSharpCode.AvalonEdit.AddIn - ICSharpCode.AvalonEdit.AddIn - v4.0 - C:\Users\Daniel\AppData\Roaming\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis - ..\..\..\..\AddIns\DisplayBindings\AvalonEdit\ - False - False - 4 - false - False - -Microsoft.Design#CA1014;-Microsoft.Design#CA2210 - v4.0 - - - - - true - Full - False - True - DEBUG;TRACE - - - False - None - True - False - TRACE - - - False - Auto - 4194304 - AnyCPU - 4096 - - - - - - 3.0 - - - 3.0 - - - 3.0 - - - - 3.5 - - - - 3.0 - - - - - - 3.5 - - - 3.0 - - - 3.0 - - - - - - - - - Always - - - Configuration\GlobalAssemblyInfo.cs - - - - - - - - - - - ChooseEncodingDialog.xaml - Code - - - - - - - - - DiffControl.xaml - Code - - - - - HiddenDefinitionControl.xaml - Code - - - - - - - - - - SortOptionsDialog.xaml - Code - - - - - - - - - - - - BehaviorOptions.xaml - Code - - - - - GeneralEditorOptions.xaml - Code - - - - HighlightingOptions.xaml - Code - - - - - - TextViewOptions.xaml - Code - - - - - - - - - - - Code - - - - Code - - - - - - - - - - - - - - - SharpDevelopCompletionWindow.cs - - - - {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} - ICSharpCode.AvalonEdit - False - - - {D68133BD-1E63-496E-9EDE-4FBDBF77B486} - Mono.Cecil - False - - - {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} - NRefactory - False - - - {2748AD25-9C63-4E12-877B-4DCE96FBED54} - ICSharpCode.SharpDevelop - False - - - {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} - ICSharpCode.Core - False - - - {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} - ICSharpCode.Core.Presentation - False - - - {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} - ICSharpCode.Core.WinForms - False - - - {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} - ICSharpCode.SharpDevelop.Dom - False - - - - - - - - - SnippetOptionPanel.cs - - - QuickClassBrowser.cs - - - {8035765F-D51F-4A0C-A746-2FD100E19419} - ICSharpCode.SharpDevelop.Widgets - False - - - - - + + + + {0162E499-42D0-409B-AA25-EED21F75336B} + Debug + AnyCPU + Library + ICSharpCode.AvalonEdit.AddIn + ICSharpCode.AvalonEdit.AddIn + v4.0 + C:\Users\Daniel\AppData\Roaming\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis + ..\..\..\..\AddIns\DisplayBindings\AvalonEdit\ + False + False + 4 + false + False + -Microsoft.Design#CA1014;-Microsoft.Design#CA2210 + v4.0 + + + + + true + Full + False + True + DEBUG;TRACE + + + False + None + True + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + 3.0 + + + 3.0 + + + 3.0 + + + + 3.5 + + + + 3.0 + + + + + + 3.5 + + + 3.0 + + + 3.0 + + + + + + + + + Always + + + Configuration\GlobalAssemblyInfo.cs + + + + + + + + + + + ChooseEncodingDialog.xaml + Code + + + + + + + + + DiffControl.xaml + Code + + + + + HiddenDefinitionControl.xaml + Code + + + + + + + + + + SortOptionsDialog.xaml + Code + + + + + + + + + + + + BehaviorOptions.xaml + Code + + + + + GeneralEditorOptions.xaml + Code + + + + HighlightingOptions.xaml + Code + + + + + + TextViewOptions.xaml + Code + + + + + + + + + + + Code + + + + Code + + + + + + + + + + + + + + + SharpDevelopCompletionWindow.cs + + + + {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} + ICSharpCode.AvalonEdit + False + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + False + + + {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} + NRefactory + False + + + {2748AD25-9C63-4E12-877B-4DCE96FBED54} + ICSharpCode.SharpDevelop + False + + + {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} + ICSharpCode.Core + False + + + {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} + ICSharpCode.Core.Presentation + False + + + {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} + ICSharpCode.Core.WinForms + False + + + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} + ICSharpCode.SharpDevelop.Dom + False + + + + + + + + + SnippetOptionPanel.cs + + + QuickClassBrowser.cs + + + {8035765F-D51F-4A0C-A746-2FD100E19419} + ICSharpCode.SharpDevelop.Widgets + False + + + + + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionControl.xaml.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionControl.xaml.cs index cfe08d924e..f6c1170016 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionControl.xaml.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionControl.xaml.cs @@ -1,27 +1,27 @@ -// 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; -using System.Windows.Controls; -using System.Windows.Media; - -using ICSharpCode.AvalonEdit.AddIn.Options; - -namespace ICSharpCode.AvalonEdit.AddIn.HiddenDefinition -{ - public partial class HiddenDefinitionControl : UserControl - { - public HiddenDefinitionControl() - { - InitializeComponent(); - DefinitionTextBlock.FontFamily = new FontFamily(CodeEditorOptions.Instance.FontFamily); - DefinitionTextBlock.FontSize = CodeEditorOptions.Instance.FontSize; - } - - public string DefinitionText { - get { return this.DefinitionTextBlock.Text; } - set { this.DefinitionTextBlock.Text = value; } - } - } +// 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; +using System.Windows.Controls; +using System.Windows.Media; + +using ICSharpCode.AvalonEdit.AddIn.Options; + +namespace ICSharpCode.AvalonEdit.AddIn.HiddenDefinition +{ + public partial class HiddenDefinitionControl : UserControl + { + public HiddenDefinitionControl() + { + InitializeComponent(); + DefinitionTextBlock.FontFamily = new FontFamily(CodeEditorOptions.Instance.FontFamily); + DefinitionTextBlock.FontSize = CodeEditorOptions.Instance.FontSize; + } + + public string DefinitionText { + get { return this.DefinitionTextBlock.Text; } + set { this.DefinitionTextBlock.Text = value; } + } + } } \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionRenderer.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionRenderer.cs index 31222afb19..879b7b06fc 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionRenderer.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/HiddenDefinition/HiddenDefinitionRenderer.cs @@ -1,102 +1,102 @@ -// 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.Controls.Primitives; -using ICSharpCode.Core.Presentation; -using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Gui; - -namespace ICSharpCode.AvalonEdit.AddIn.HiddenDefinition -{ - public class HiddenDefinitionRenderer : IDisposable - { - private CodeEditorView editor; - private ExtendedPopup popup = new ExtendedPopup(); - private HiddenDefinitionControl control; - - public HiddenDefinitionRenderer(CodeEditorView editorView) - { - this.editor = editorView; - control = new HiddenDefinitionControl(); - WorkbenchSingleton.Workbench.ActiveContentChanged += WorkbenchSingleton_Workbench_ActiveContentChanged; - } - - public BracketSearchResult BracketSearchResult { get; set; } - - public void Dispose() - { - WorkbenchSingleton.Workbench.ActiveContentChanged -= WorkbenchSingleton_Workbench_ActiveContentChanged; - ClosePopup(); - popup = null; - } - - public void ClosePopup() - { - if (popup != null && popup.IsOpen) - popup.IsOpen = false; - } - - public void Show() - { - ClosePopup(); - - if (BracketSearchResult == null) return; - - // verify if we have a open bracket - if (this.editor.Document.GetCharAt(BracketSearchResult.OpeningBracketOffset) != '{') - return; - - var line = GetLineText(BracketSearchResult.OpeningBracketOffset); - if(line == null) return; - - control.DefinitionText = line; - popup.Child = control; - popup.HorizontalOffset = 70; - popup.Placement = PlacementMode.Relative; - popup.PlacementTarget = editor.TextArea; - popup.IsOpen = true; - } - - /// - /// Gets the line text near the offset. - /// - /// Offset. - /// - private string GetLineText(int offset) - { - if (!editor.TextArea.TextView.VisualLinesValid) - return null; - - // get line - var documentLine = editor.Document.GetLineByOffset(offset); - string documentText = editor.Document.Text; - string lineText = string.Empty; - int off, length; - - do { - if (documentLine == null || documentLine.IsDeleted) return null; - off = documentLine.Offset; - length = documentLine.Length; - lineText = documentText.Substring(off, length).Trim(); - - documentLine = documentLine.PreviousLine; - } - while (lineText == "{" || string.IsNullOrEmpty(lineText) || - lineText.StartsWith("//") || lineText.StartsWith("/*") || - lineText.StartsWith("*") || lineText.StartsWith("'")); - - // check whether the line is visible - if (editor.TextArea.TextView.VisualLines[0].StartOffset > off) { - return this.editor.TextArea.TextView.Document.GetText(off, length); - } - - return null; - } - - private void WorkbenchSingleton_Workbench_ActiveContentChanged(object sender, EventArgs e) - { - ClosePopup(); - } - } +// 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.Controls.Primitives; +using ICSharpCode.Core.Presentation; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Gui; + +namespace ICSharpCode.AvalonEdit.AddIn.HiddenDefinition +{ + public class HiddenDefinitionRenderer : IDisposable + { + private CodeEditorView editor; + private ExtendedPopup popup = new ExtendedPopup(); + private HiddenDefinitionControl control; + + public HiddenDefinitionRenderer(CodeEditorView editorView) + { + this.editor = editorView; + control = new HiddenDefinitionControl(); + WorkbenchSingleton.Workbench.ActiveContentChanged += WorkbenchSingleton_Workbench_ActiveContentChanged; + } + + public BracketSearchResult BracketSearchResult { get; set; } + + public void Dispose() + { + WorkbenchSingleton.Workbench.ActiveContentChanged -= WorkbenchSingleton_Workbench_ActiveContentChanged; + ClosePopup(); + popup = null; + } + + public void ClosePopup() + { + if (popup != null && popup.IsOpen) + popup.IsOpen = false; + } + + public void Show() + { + ClosePopup(); + + if (BracketSearchResult == null) return; + + // verify if we have a open bracket + if (this.editor.Document.GetCharAt(BracketSearchResult.OpeningBracketOffset) != '{') + return; + + var line = GetLineText(BracketSearchResult.OpeningBracketOffset); + if(line == null) return; + + control.DefinitionText = line; + popup.Child = control; + popup.HorizontalOffset = 70; + popup.Placement = PlacementMode.Relative; + popup.PlacementTarget = editor.TextArea; + popup.IsOpen = true; + } + + /// + /// Gets the line text near the offset. + /// + /// Offset. + /// + private string GetLineText(int offset) + { + if (!editor.TextArea.TextView.VisualLinesValid) + return null; + + // get line + var documentLine = editor.Document.GetLineByOffset(offset); + string documentText = editor.Document.Text; + string lineText = string.Empty; + int off, length; + + do { + if (documentLine == null || documentLine.IsDeleted) return null; + off = documentLine.Offset; + length = documentLine.Length; + lineText = documentText.Substring(off, length).Trim(); + + documentLine = documentLine.PreviousLine; + } + while (lineText == "{" || string.IsNullOrEmpty(lineText) || + lineText.StartsWith("//") || lineText.StartsWith("/*") || + lineText.StartsWith("*") || lineText.StartsWith("'")); + + // check whether the line is visible + if (editor.TextArea.TextView.VisualLines[0].StartOffset > off) { + return this.editor.TextArea.TextView.Document.GetText(off, length); + } + + return null; + } + + private void WorkbenchSingleton_Workbench_ActiveContentChanged(object sender, EventArgs e) + { + ClosePopup(); + } + } } \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/ThumbnailViewPad.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/ThumbnailViewPad.cs index 18d2fcbacf..13289e6425 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/ThumbnailViewPad.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/Src/ThumbnailViewPad.cs @@ -1,55 +1,55 @@ -// 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; -using System.Windows.Controls; - -using ICSharpCode.Core; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Gui; -using ICSharpCode.WpfDesign.Designer.ThumbnailView; - -namespace ICSharpCode.WpfDesign.AddIn -{ - public class ThumbnailViewPad : AbstractPadContent - { - ContentPresenter contentControl = new ContentPresenter(); - - ThumbnailView thumbnailView = new ThumbnailView(); - - TextBlock notAvailableTextBlock = new TextBlock { - Text = StringParser.Parse("${res:ICSharpCode.SharpDevelop.Gui.OutlinePad.NotAvailable}"), - TextWrapping = TextWrapping.Wrap - }; - - public ThumbnailViewPad() - { - WorkbenchSingleton.Workbench.ActiveViewContentChanged += WorkbenchActiveViewContentChanged; - WorkbenchActiveViewContentChanged(null, null); - } - - void WorkbenchActiveViewContentChanged(object sender, EventArgs e) - { - WpfViewContent wpfView = WorkbenchSingleton.Workbench.ActiveViewContent as WpfViewContent; - if (wpfView != null) - { - thumbnailView.DesignSurface = wpfView.DesignSurface; - contentControl.SetContent(thumbnailView); - } - else - { - contentControl.SetContent(notAvailableTextBlock); - } - } - - /// - /// The representing the pad - /// - public override object Control { - get { - return contentControl; - } - } - } -} +// 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; +using System.Windows.Controls; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.WpfDesign.Designer.ThumbnailView; + +namespace ICSharpCode.WpfDesign.AddIn +{ + public class ThumbnailViewPad : AbstractPadContent + { + ContentPresenter contentControl = new ContentPresenter(); + + ThumbnailView thumbnailView = new ThumbnailView(); + + TextBlock notAvailableTextBlock = new TextBlock { + Text = StringParser.Parse("${res:ICSharpCode.SharpDevelop.Gui.OutlinePad.NotAvailable}"), + TextWrapping = TextWrapping.Wrap + }; + + public ThumbnailViewPad() + { + WorkbenchSingleton.Workbench.ActiveViewContentChanged += WorkbenchActiveViewContentChanged; + WorkbenchActiveViewContentChanged(null, null); + } + + void WorkbenchActiveViewContentChanged(object sender, EventArgs e) + { + WpfViewContent wpfView = WorkbenchSingleton.Workbench.ActiveViewContent as WpfViewContent; + if (wpfView != null) + { + thumbnailView.DesignSurface = wpfView.DesignSurface; + contentControl.SetContent(thumbnailView); + } + else + { + contentControl.SetContent(notAvailableTextBlock); + } + } + + /// + /// The representing the pad + /// + public override object Control { + get { + return contentControl; + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj index eeaea66008..fa65574b6d 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.AddIn/WpfDesign.AddIn.csproj @@ -1,158 +1,158 @@ - - - - {9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865} - Debug - AnyCPU - Library - ICSharpCode.WpfDesign.AddIn - ICSharpCode.WpfDesign.AddIn - ..\..\..\..\..\AddIns\DisplayBindings\WpfDesign\ - False - False - 4 - false - v4.0 - - - - - true - Full - True - DEBUG;TRACE - False - - - False - None - False - TRACE - - - False - Auto - 4194304 - AnyCPU - 4096 - - - - - - - - 3.5 - - - - - - 4.0 - - - - - - - - Always - - - Configuration\GlobalAssemblyInfo.cs - - - - - - - - - - - - - - - ChooseImageDialog.xaml - Code - - - ImageSourceEditor.xaml - Code - - - - ObjectEditor.xaml - - - - - - - - WpfDocumentError.xaml - Code - - - - - - - - - {2748AD25-9C63-4E12-877B-4DCE96FBED54} - ICSharpCode.SharpDevelop - False - - - {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} - ICSharpCode.Core - False - - - {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} - ICSharpCode.Core.Presentation - False - - - {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} - ICSharpCode.Core.WinForms - False - - - {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} - ICSharpCode.SharpDevelop.Dom - False - - - {8035765F-D51F-4A0C-A746-2FD100E19419} - ICSharpCode.SharpDevelop.Widgets - False - - - {7D7E92DF-ACEB-4B69-92C8-8AC7A703CD57} - FormsDesigner - False - - - {78CC29AC-CC79-4355-B1F2-97936DF198AC} - WpfDesign.Designer - False - - - {88DA149F-21B2-48AB-82C4-28FB6BDFD783} - WpfDesign.XamlDom - False - - - {66A378A1-E9F4-4AD5-8946-D0EC06C2902F} - WpfDesign - False - - - - - - + + + + {9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865} + Debug + AnyCPU + Library + ICSharpCode.WpfDesign.AddIn + ICSharpCode.WpfDesign.AddIn + ..\..\..\..\..\AddIns\DisplayBindings\WpfDesign\ + False + False + 4 + false + v4.0 + + + + + true + Full + True + DEBUG;TRACE + False + + + False + None + False + TRACE + + + False + Auto + 4194304 + AnyCPU + 4096 + + + + + + + + 3.5 + + + + + + 4.0 + + + + + + + + Always + + + Configuration\GlobalAssemblyInfo.cs + + + + + + + + + + + + + + + ChooseImageDialog.xaml + Code + + + ImageSourceEditor.xaml + Code + + + + ObjectEditor.xaml + + + + + + + + WpfDocumentError.xaml + Code + + + + + + + + + {2748AD25-9C63-4E12-877B-4DCE96FBED54} + ICSharpCode.SharpDevelop + False + + + {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} + ICSharpCode.Core + False + + + {7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} + ICSharpCode.Core.Presentation + False + + + {857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} + ICSharpCode.Core.WinForms + False + + + {924EE450-603D-49C1-A8E5-4AFAA31CE6F3} + ICSharpCode.SharpDevelop.Dom + False + + + {8035765F-D51F-4A0C-A746-2FD100E19419} + ICSharpCode.SharpDevelop.Widgets + False + + + {7D7E92DF-ACEB-4B69-92C8-8AC7A703CD57} + FormsDesigner + False + + + {78CC29AC-CC79-4355-B1F2-97936DF198AC} + WpfDesign.Designer + False + + + {88DA149F-21B2-48AB-82C4-28FB6BDFD783} + WpfDesign.XamlDom + False + + + {66A378A1-E9F4-4AD5-8946-D0EC06C2902F} + WpfDesign + False + + + + + + \ No newline at end of file diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/PropertyGrid/PropertyGrid.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/PropertyGrid/PropertyGrid.cs index ed35981f07..ad077f6373 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/PropertyGrid/PropertyGrid.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/PropertyGrid/PropertyGrid.cs @@ -1,328 +1,328 @@ -// 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.Linq; -using System.Text; -using System.ComponentModel; -using System.Collections.ObjectModel; -using System.Threading; -using System.Globalization; -using ICSharpCode.WpfDesign.PropertyGrid; -using System.Windows.Threading; -using System.Diagnostics; -using System.Windows.Media; -using System.Windows; - -namespace ICSharpCode.WpfDesign.Designer.PropertyGrid -{ - public class PropertyGrid : INotifyPropertyChanged - { - public PropertyGrid() - { - Categories = new CategoriesCollection(); - Categories.Add(specialCategory); - Categories.Add(popularCategory); - Categories.Add(otherCategory); - Categories.Add(attachedCategory); - - Events = new PropertyNodeCollection(); - } - - Category specialCategory = new Category("Special"); - Category popularCategory = new Category("Popular"); - Category otherCategory = new Category("Other"); - Category attachedCategory = new Category("Attached"); - - Dictionary nodeFromDescriptor = new Dictionary(); - - public CategoriesCollection Categories { get; private set; } - public PropertyNodeCollection Events { get; private set; } - - private PropertyGridGroupMode _groupMode; - - public PropertyGridGroupMode GroupMode - { - get { return _groupMode; } - set - { - if (_groupMode != value) - { - _groupMode = value; - - RaisePropertyChanged("GroupMode"); - - Reload(); - } - } - } - - PropertyGridTab currentTab; - - public PropertyGridTab CurrentTab { - get { - return currentTab; - } - set { - currentTab = value; - RaisePropertyChanged("CurrentTab"); - RaisePropertyChanged("NameBackground"); - } - } - - string filter; - - public string Filter { - get { - return filter; - } - set { - filter = value; - Reload(); - RaisePropertyChanged("Filter"); - } - } - - DesignItem singleItem; - - public DesignItem SingleItem { - get { - return singleItem; - } - private set { - if (singleItem != null) { - singleItem.NameChanged -= singleItem_NameChanged; - } - singleItem = value; - if (singleItem != null) { - singleItem.NameChanged += singleItem_NameChanged; - } - RaisePropertyChanged("SingleItem"); - RaisePropertyChanged("Name"); - RaisePropertyChanged("IsNameEnabled"); - IsNameCorrect = true; - } - } - - void singleItem_NameChanged(object sender, EventArgs e) - { - RaisePropertyChanged("Name"); - } - - public string OldName { - get; private set; - } - - public string Name { - get { - if (SingleItem != null) { - return SingleItem.Name; - } - return null; - } - set { - if (SingleItem != null) { - try { - if (string.IsNullOrEmpty(value)) { - OldName = null; - SingleItem.Name = null; - } else { - OldName = SingleItem.Name; - SingleItem.Name = value; - } - IsNameCorrect = true; - } catch { - IsNameCorrect = false; - } - RaisePropertyChanged("Name"); - } - } - } - - bool isNameCorrect = true; - - public bool IsNameCorrect { - get { - return isNameCorrect; - } - set { - isNameCorrect = value; - RaisePropertyChanged("IsNameCorrect"); - } - } - - public bool IsNameEnabled { - get { - return SingleItem != null; - } - } - - IEnumerable selectedItems; - - public IEnumerable SelectedItems { - get { - return selectedItems; - } - set { - selectedItems = value; - RaisePropertyChanged("SelectedItems"); - Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Action( - delegate { - Reload(); - })); - } - } - - public void ClearFilter() - { - Filter = null; - } - - void Reload() - { - Clear(); - - if (SelectedItems == null || SelectedItems.Count() == 0) return; - if (SelectedItems.Count() == 1) SingleItem = SelectedItems.First(); - - foreach (var md in GetDescriptors()) { - if (PassesFilter(md.Name)) - AddNode(md); - } - } - - void Clear() - { - foreach (var c in Categories) { - c.IsVisible = false; - foreach (var p in c.Properties) { - p.IsVisible = false; - } - } - - foreach (var e in Events) { - e.IsVisible = false; - } - - SingleItem = null; - } - - List GetDescriptors() - { - List list = new List(); - - if (SelectedItems.Count() == 1) { - foreach (MemberDescriptor d in TypeHelper.GetAvailableProperties(SingleItem.Component)) { - list.Add(d); - } - foreach (MemberDescriptor d in TypeHelper.GetAvailableEvents(SingleItem.ComponentType)) { - list.Add(d); - } - } else { - foreach (MemberDescriptor d in TypeHelper.GetCommonAvailableProperties(SelectedItems.Select(t => t.Component))) { - list.Add(d); - } - } - - return list; - } - - bool PassesFilter(string name) - { - if (string.IsNullOrEmpty(Filter)) return true; - for (int i = 0; i < name.Length; i++) { - if (i == 0 || char.IsUpper(name[i])) { - if (string.Compare(name, i, Filter, 0, Filter.Length, true) == 0) { - return true; - } - } - } - return false; - } - - void AddNode(MemberDescriptor md) - { - var designProperties = SelectedItems.Select(item => item.Properties.GetProperty(md)).ToArray(); - if (!Metadata.IsBrowsable(designProperties[0])) return; - - PropertyNode node; - if (nodeFromDescriptor.TryGetValue(md, out node)) { - node.Load(designProperties); - } else { - node = new PropertyNode(); - node.Load(designProperties); - if (node.IsEvent) { - Events.AddSorted(node); - } else { - var cat = PickCategory(node); - cat.Properties.AddSorted(node); - node.Category = cat; - } - nodeFromDescriptor[md] = node; - } - node.IsVisible = true; - if (node.Category != null) - node.Category.IsVisible = true; - } - - Category PickCategory(PropertyNode node) - { - if (Metadata.IsPopularProperty(node.FirstProperty)) return popularCategory; - if (node.FirstProperty.IsAttachedDependencyProperty()) return attachedCategory; - var typeName = node.FirstProperty.DeclaringType.FullName; - if (typeName.StartsWith("System.Windows.") || typeName.StartsWith("ICSharpCode.WpfDesign.Designer.Controls.")) - return otherCategory; - return specialCategory; - } - - #region INotifyPropertyChanged Members - - public event PropertyChangedEventHandler PropertyChanged; - - void RaisePropertyChanged(string name) - { - if (PropertyChanged != null) { - PropertyChanged(this, new PropertyChangedEventArgs(name)); - } - } - - #endregion - - //class CategoryNameComparer : IComparer - //{ - // public static CategoryNameComparer Instance = new CategoryNameComparer(); - - // public int Compare(string x, string y) - // { - // int i1 = Array.IndexOf(Metadata.CategoryOrder, x); - // if (i1 == -1) i1 = int.MaxValue; - // int i2 = Array.IndexOf(Metadata.CategoryOrder, y); - // if (i2 == -1) i2 = int.MaxValue; - // if (i1 == i2) return x.CompareTo(y); - // return i1.CompareTo(i2); - // } - //} - } - - public class CategoriesCollection : SortedObservableCollection - { - public CategoriesCollection() - : base(n => n.Name) - { - } - } - - public enum PropertyGridGroupMode - { - GroupByPopularCategorys, - GroupByCategorys, - Ungrouped, - } - - public enum PropertyGridTab - { - Properties, - Events - } -} +// 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.Linq; +using System.Text; +using System.ComponentModel; +using System.Collections.ObjectModel; +using System.Threading; +using System.Globalization; +using ICSharpCode.WpfDesign.PropertyGrid; +using System.Windows.Threading; +using System.Diagnostics; +using System.Windows.Media; +using System.Windows; + +namespace ICSharpCode.WpfDesign.Designer.PropertyGrid +{ + public class PropertyGrid : INotifyPropertyChanged + { + public PropertyGrid() + { + Categories = new CategoriesCollection(); + Categories.Add(specialCategory); + Categories.Add(popularCategory); + Categories.Add(otherCategory); + Categories.Add(attachedCategory); + + Events = new PropertyNodeCollection(); + } + + Category specialCategory = new Category("Special"); + Category popularCategory = new Category("Popular"); + Category otherCategory = new Category("Other"); + Category attachedCategory = new Category("Attached"); + + Dictionary nodeFromDescriptor = new Dictionary(); + + public CategoriesCollection Categories { get; private set; } + public PropertyNodeCollection Events { get; private set; } + + private PropertyGridGroupMode _groupMode; + + public PropertyGridGroupMode GroupMode + { + get { return _groupMode; } + set + { + if (_groupMode != value) + { + _groupMode = value; + + RaisePropertyChanged("GroupMode"); + + Reload(); + } + } + } + + PropertyGridTab currentTab; + + public PropertyGridTab CurrentTab { + get { + return currentTab; + } + set { + currentTab = value; + RaisePropertyChanged("CurrentTab"); + RaisePropertyChanged("NameBackground"); + } + } + + string filter; + + public string Filter { + get { + return filter; + } + set { + filter = value; + Reload(); + RaisePropertyChanged("Filter"); + } + } + + DesignItem singleItem; + + public DesignItem SingleItem { + get { + return singleItem; + } + private set { + if (singleItem != null) { + singleItem.NameChanged -= singleItem_NameChanged; + } + singleItem = value; + if (singleItem != null) { + singleItem.NameChanged += singleItem_NameChanged; + } + RaisePropertyChanged("SingleItem"); + RaisePropertyChanged("Name"); + RaisePropertyChanged("IsNameEnabled"); + IsNameCorrect = true; + } + } + + void singleItem_NameChanged(object sender, EventArgs e) + { + RaisePropertyChanged("Name"); + } + + public string OldName { + get; private set; + } + + public string Name { + get { + if (SingleItem != null) { + return SingleItem.Name; + } + return null; + } + set { + if (SingleItem != null) { + try { + if (string.IsNullOrEmpty(value)) { + OldName = null; + SingleItem.Name = null; + } else { + OldName = SingleItem.Name; + SingleItem.Name = value; + } + IsNameCorrect = true; + } catch { + IsNameCorrect = false; + } + RaisePropertyChanged("Name"); + } + } + } + + bool isNameCorrect = true; + + public bool IsNameCorrect { + get { + return isNameCorrect; + } + set { + isNameCorrect = value; + RaisePropertyChanged("IsNameCorrect"); + } + } + + public bool IsNameEnabled { + get { + return SingleItem != null; + } + } + + IEnumerable selectedItems; + + public IEnumerable SelectedItems { + get { + return selectedItems; + } + set { + selectedItems = value; + RaisePropertyChanged("SelectedItems"); + Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new Action( + delegate { + Reload(); + })); + } + } + + public void ClearFilter() + { + Filter = null; + } + + void Reload() + { + Clear(); + + if (SelectedItems == null || SelectedItems.Count() == 0) return; + if (SelectedItems.Count() == 1) SingleItem = SelectedItems.First(); + + foreach (var md in GetDescriptors()) { + if (PassesFilter(md.Name)) + AddNode(md); + } + } + + void Clear() + { + foreach (var c in Categories) { + c.IsVisible = false; + foreach (var p in c.Properties) { + p.IsVisible = false; + } + } + + foreach (var e in Events) { + e.IsVisible = false; + } + + SingleItem = null; + } + + List GetDescriptors() + { + List list = new List(); + + if (SelectedItems.Count() == 1) { + foreach (MemberDescriptor d in TypeHelper.GetAvailableProperties(SingleItem.Component)) { + list.Add(d); + } + foreach (MemberDescriptor d in TypeHelper.GetAvailableEvents(SingleItem.ComponentType)) { + list.Add(d); + } + } else { + foreach (MemberDescriptor d in TypeHelper.GetCommonAvailableProperties(SelectedItems.Select(t => t.Component))) { + list.Add(d); + } + } + + return list; + } + + bool PassesFilter(string name) + { + if (string.IsNullOrEmpty(Filter)) return true; + for (int i = 0; i < name.Length; i++) { + if (i == 0 || char.IsUpper(name[i])) { + if (string.Compare(name, i, Filter, 0, Filter.Length, true) == 0) { + return true; + } + } + } + return false; + } + + void AddNode(MemberDescriptor md) + { + var designProperties = SelectedItems.Select(item => item.Properties.GetProperty(md)).ToArray(); + if (!Metadata.IsBrowsable(designProperties[0])) return; + + PropertyNode node; + if (nodeFromDescriptor.TryGetValue(md, out node)) { + node.Load(designProperties); + } else { + node = new PropertyNode(); + node.Load(designProperties); + if (node.IsEvent) { + Events.AddSorted(node); + } else { + var cat = PickCategory(node); + cat.Properties.AddSorted(node); + node.Category = cat; + } + nodeFromDescriptor[md] = node; + } + node.IsVisible = true; + if (node.Category != null) + node.Category.IsVisible = true; + } + + Category PickCategory(PropertyNode node) + { + if (Metadata.IsPopularProperty(node.FirstProperty)) return popularCategory; + if (node.FirstProperty.IsAttachedDependencyProperty()) return attachedCategory; + var typeName = node.FirstProperty.DeclaringType.FullName; + if (typeName.StartsWith("System.Windows.") || typeName.StartsWith("ICSharpCode.WpfDesign.Designer.Controls.")) + return otherCategory; + return specialCategory; + } + + #region INotifyPropertyChanged Members + + public event PropertyChangedEventHandler PropertyChanged; + + void RaisePropertyChanged(string name) + { + if (PropertyChanged != null) { + PropertyChanged(this, new PropertyChangedEventArgs(name)); + } + } + + #endregion + + //class CategoryNameComparer : IComparer + //{ + // public static CategoryNameComparer Instance = new CategoryNameComparer(); + + // public int Compare(string x, string y) + // { + // int i1 = Array.IndexOf(Metadata.CategoryOrder, x); + // if (i1 == -1) i1 = int.MaxValue; + // int i2 = Array.IndexOf(Metadata.CategoryOrder, y); + // if (i2 == -1) i2 = int.MaxValue; + // if (i1 == i2) return x.CompareTo(y); + // return i1.CompareTo(i2); + // } + //} + } + + public class CategoriesCollection : SortedObservableCollection + { + public CategoriesCollection() + : base(n => n.Name) + { + } + } + + public enum PropertyGridGroupMode + { + GroupByPopularCategorys, + GroupByCategorys, + Ungrouped, + } + + public enum PropertyGridTab + { + Properties, + Events + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/NameScopeHelper.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/NameScopeHelper.cs index d77f74961e..990b6079b6 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/NameScopeHelper.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/NameScopeHelper.cs @@ -1,79 +1,79 @@ -// 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.Windows; -using System.Windows.Markup; - -namespace ICSharpCode.WpfDesign.XamlDom -{ - /// - /// Static methods to help with operations on Xaml elements. - /// - public static class NameScopeHelper - { - /// - /// Finds the XAML namescope for the specified object and uses it to unregister the old name and then register the new name. - /// - /// The object where the name was changed. - /// The old name. - /// The new name. - internal static void NameChanged(XamlObject namedObject, string oldName, string newName) - { - var obj = namedObject; - while (obj != null) { - var nameScope = GetNameScopeFromObject(obj.Instance); - if (nameScope != null) { - if (oldName != null) { - try { - nameScope.UnregisterName(oldName); - } catch (Exception x) { - Debug.WriteLine(x.Message); - } - } - if (newName != null) { - nameScope.RegisterName(newName, namedObject.Instance); - - try{ - var prp = namedObject.ElementType.GetProperty(namedObject.RuntimeNameProperty); - if (prp != null) - prp.SetValue(namedObject.Instance, newName, null); - } catch (Exception x) { - Debug.WriteLine(x.Message); - } - } - break; - } - obj = obj.ParentObject; - } - } - - /// - /// Gets the XAML namescope for the specified object. - /// - /// The object to get the XAML namescope for. - /// A XAML namescope, as an instance. - public static INameScope GetNameScopeFromObject(object obj) - { - var nameScope = obj as INameScope; - if (nameScope == null) { - var depObj = obj as DependencyObject; - if (depObj != null) - nameScope = NameScope.GetNameScope(depObj); - } - - return nameScope; - } - - /// - /// Clears the if the object is a . - /// - /// The object to clear the on. - public static void ClearNameScopeProperty(object obj) - { - var depObj = obj as DependencyObject; - if (depObj != null) - depObj.ClearValue(NameScope.NameScopeProperty); - } - } -} +// 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.Windows; +using System.Windows.Markup; + +namespace ICSharpCode.WpfDesign.XamlDom +{ + /// + /// Static methods to help with operations on Xaml elements. + /// + public static class NameScopeHelper + { + /// + /// Finds the XAML namescope for the specified object and uses it to unregister the old name and then register the new name. + /// + /// The object where the name was changed. + /// The old name. + /// The new name. + internal static void NameChanged(XamlObject namedObject, string oldName, string newName) + { + var obj = namedObject; + while (obj != null) { + var nameScope = GetNameScopeFromObject(obj.Instance); + if (nameScope != null) { + if (oldName != null) { + try { + nameScope.UnregisterName(oldName); + } catch (Exception x) { + Debug.WriteLine(x.Message); + } + } + if (newName != null) { + nameScope.RegisterName(newName, namedObject.Instance); + + try{ + var prp = namedObject.ElementType.GetProperty(namedObject.RuntimeNameProperty); + if (prp != null) + prp.SetValue(namedObject.Instance, newName, null); + } catch (Exception x) { + Debug.WriteLine(x.Message); + } + } + break; + } + obj = obj.ParentObject; + } + } + + /// + /// Gets the XAML namescope for the specified object. + /// + /// The object to get the XAML namescope for. + /// A XAML namescope, as an instance. + public static INameScope GetNameScopeFromObject(object obj) + { + var nameScope = obj as INameScope; + if (nameScope == null) { + var depObj = obj as DependencyObject; + if (depObj != null) + nameScope = NameScope.GetNameScope(depObj); + } + + return nameScope; + } + + /// + /// Clears the if the object is a . + /// + /// The object to clear the on. + public static void ClearNameScopeProperty(object obj) + { + var depObj = obj as DependencyObject; + if (depObj != null) + depObj.ClearValue(NameScope.NameScopeProperty); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs index bf56fb5ec2..e7daa9f21d 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs @@ -1,491 +1,491 @@ -// 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.Diagnostics; -using System.Linq; -using System.Text; -using System.Xml; -using System.Windows; -using System.Windows.Markup; - -namespace ICSharpCode.WpfDesign.XamlDom -{ - /// - /// Describes a property on a . - /// - [DebuggerDisplay("XamlProperty: {PropertyName}")] - public sealed class XamlProperty - { - XamlObject parentObject; - internal readonly XamlPropertyInfo propertyInfo; - XamlPropertyValue propertyValue; - - CollectionElementsCollection collectionElements; - bool isCollection; - bool isResources; - - static readonly IList emptyCollectionElementsArray = new XamlPropertyValue[0]; - - // for use by parser only - internal XamlProperty(XamlObject parentObject, XamlPropertyInfo propertyInfo, XamlPropertyValue propertyValue) - : this(parentObject, propertyInfo) - { - PossiblyNameChanged(null, propertyValue); - - this.propertyValue = propertyValue; - if (propertyValue != null) { - propertyValue.ParentProperty = this; - } - - UpdateValueOnInstance(); - } - - internal XamlProperty(XamlObject parentObject, XamlPropertyInfo propertyInfo) - { - this.parentObject = parentObject; - this.propertyInfo = propertyInfo; - - if (propertyInfo.IsCollection) { - isCollection = true; - collectionElements = new CollectionElementsCollection(this); - - if (propertyInfo.Name.Equals(XamlConstants.ResourcesPropertyName, StringComparison.Ordinal) && - propertyInfo.ReturnType == typeof(ResourceDictionary)) { - isResources = true; - } - } - } - - /// - /// Gets the parent object for which this property was declared. - /// - public XamlObject ParentObject { - get { return parentObject; } - } - - /// - /// Gets the property name. - /// - public string PropertyName { - get { return propertyInfo.Name; } - } - - /// - /// Gets the type the property is declared on. - /// - public Type PropertyTargetType { - get { return propertyInfo.TargetType; } - } - - /// - /// Gets if this property is an attached property. - /// - public bool IsAttached { - get { return propertyInfo.IsAttached; } - } - - /// - /// Gets if this property is an event. - /// - public bool IsEvent { - get { return propertyInfo.IsEvent; } - } - - /// - /// Gets the return type of the property. - /// - public Type ReturnType { - get { return propertyInfo.ReturnType; } - } - - /// - /// Gets the type converter used to convert property values to/from string. - /// - public TypeConverter TypeConverter { - get { return propertyInfo.TypeConverter; } - } - - /// - /// Gets the category of the property. - /// - public string Category { - get { return propertyInfo.Category; } - } - - /// - /// Gets the value of the property. Can be null if the property is a collection property. - /// - public XamlPropertyValue PropertyValue { - get { return propertyValue; } - set { SetPropertyValue(value); } - } - - /// - /// Gets if the property represents the FrameworkElement.Resources property that holds a locally-defined resource dictionary. - /// - public bool IsResources { - get { return isResources; } - } - - /// - /// Gets if the property is a collection property. - /// - public bool IsCollection { - get { return isCollection; } - } - - /// - /// Gets the collection elements of the property. Is empty if the property is not a collection. - /// - public IList CollectionElements { - get { return collectionElements ?? emptyCollectionElementsArray; } - } - - /// - /// Gets if the property is set. - /// - public bool IsSet { - get { return propertyValue != null || - _propertyElement != null; // collection - } - } - - /// - /// Occurs when the value of the IsSet property has changed. - /// - public event EventHandler IsSetChanged; - - /// - /// Occurs when the value of the property has changed. - /// - public event EventHandler ValueChanged; - - /// - /// Occurs when MarkupExtension evaluated PropertyValue dosn't changed but ValueOnInstance does. - /// - public event EventHandler ValueOnInstanceChanged; - - void SetPropertyValue(XamlPropertyValue value) - { - // Binding... - //if (IsCollection) { - // throw new InvalidOperationException("Cannot set the value of collection properties."); - //} - - bool wasSet = this.IsSet; - - PossiblyNameChanged(propertyValue, value); - - //reset expression - var xamlObject = propertyValue as XamlObject; - if (xamlObject != null && xamlObject.IsMarkupExtension) - propertyInfo.ResetValue(parentObject.Instance); - - ResetInternal(); - - propertyValue = value; - propertyValue.ParentProperty = this; - propertyValue.AddNodeTo(this); - UpdateValueOnInstance(); - - ParentObject.OnPropertyChanged(this); - - if (!wasSet) { - if (IsSetChanged != null) { - IsSetChanged(this, EventArgs.Empty); - } - } - - if (ValueChanged != null) { - ValueChanged(this, EventArgs.Empty); - } - } - - internal void UpdateValueOnInstance() - { - if (PropertyValue != null) { - try { - ValueOnInstance = PropertyValue.GetValueFor(propertyInfo); - } - catch { - Debug.WriteLine("UpdateValueOnInstance() failed"); - } - } - } - - /// - /// Resets the properties value. - /// - public void Reset() - { - if (IsSet) { - - propertyInfo.ResetValue(parentObject.Instance); - ResetInternal(); - - ParentObject.OnPropertyChanged(this); - - if (IsSetChanged != null) { - IsSetChanged(this, EventArgs.Empty); - } - if (ValueChanged != null) { - ValueChanged(this, EventArgs.Empty); - } - } - } - - void ResetInternal() - { - if (propertyValue != null) { - propertyValue.RemoveNodeFromParent(); - propertyValue.ParentProperty = null; - propertyValue = null; - } - if (_propertyElement != null) { - _propertyElement.ParentNode.RemoveChild(_propertyElement); - _propertyElement = null; - } - } - - XmlElement _propertyElement; - - internal void ParserSetPropertyElement(XmlElement propertyElement) - { - XmlElement oldPropertyElement = _propertyElement; - if (oldPropertyElement == propertyElement) return; - - _propertyElement = propertyElement; - - if (oldPropertyElement != null && IsCollection) { - Debug.WriteLine("Property element for " + this.PropertyName + " already exists, merging.."); - foreach (XamlPropertyValue val in this.collectionElements) { - val.RemoveNodeFromParent(); - val.AddNodeTo(this); - } - oldPropertyElement.ParentNode.RemoveChild(oldPropertyElement); - } - } - - bool IsFirstChildResources(XamlObject obj) - { - return obj.XmlElement.FirstChild != null && - obj.XmlElement.FirstChild.Name.EndsWith("." + XamlConstants.ResourcesPropertyName) && - obj.Properties.Where((prop) => prop.IsResources).FirstOrDefault() != null; - } - - XmlElement CreatePropertyElement() - { - string ns = parentObject.OwnerDocument.GetNamespaceFor(parentObject.ElementType); - return parentObject.OwnerDocument.XmlDocument.CreateElement( - parentObject.OwnerDocument.GetPrefixForNamespace(ns), - parentObject.ElementType.Name + "." + this.PropertyName, - ns - ); - } - - internal void AddChildNodeToProperty(XmlNode newChildNode) - { - if (this.IsCollection) { - // this is the default collection - InsertNodeInCollection(newChildNode, collectionElements.Count); - return; - } - if (_propertyElement == null) { - if (PropertyName == parentObject.ContentPropertyName) { - if (IsFirstChildResources(parentObject)) { - // Resources element should always be first - parentObject.XmlElement.InsertAfter(newChildNode, parentObject.XmlElement.FirstChild); - } - else - parentObject.XmlElement.InsertBefore(newChildNode, parentObject.XmlElement.FirstChild); - return; - } - _propertyElement = CreatePropertyElement(); - - if (IsFirstChildResources(parentObject)) { - // Resources element should always be first - parentObject.XmlElement.InsertAfter(_propertyElement, parentObject.XmlElement.FirstChild); - } - else - parentObject.XmlElement.InsertBefore(_propertyElement, parentObject.XmlElement.FirstChild); - } - _propertyElement.AppendChild(newChildNode); - } - - internal void InsertNodeInCollection(XmlNode newChildNode, int index) - { - Debug.Assert(index >= 0 && index <= collectionElements.Count); - XmlElement collection = _propertyElement; - if (collection == null) { - if (collectionElements.Count == 0 && this.PropertyName != this.ParentObject.ContentPropertyName) { - // we have to create the collection element - _propertyElement = CreatePropertyElement(); - - if (this.IsResources) { - parentObject.XmlElement.PrependChild(_propertyElement); - } else { - parentObject.XmlElement.AppendChild(_propertyElement); - } - - collection = _propertyElement; - } else { - // this is the default collection - collection = parentObject.XmlElement; - } - } - if (collectionElements.Count == 0) { - // collection is empty -> we may insert anywhere - collection.AppendChild(newChildNode); - } else if (index == collectionElements.Count) { - // insert after last element in collection - collection.InsertAfter(newChildNode, collectionElements[collectionElements.Count - 1].GetNodeForCollection()); - } else { - // insert before specified index - collection.InsertBefore(newChildNode, collectionElements[index].GetNodeForCollection()); - } - } - - internal XmlAttribute SetAttribute(string value) - { - string name; - var element = ParentObject.XmlElement; - - if (IsAttached) - { - if (PropertyTargetType == typeof (DesignTimeProperties) || PropertyTargetType == typeof (MarkupCompatibilityProperties)) - name = PropertyName; - else - name = PropertyTargetType.Name + "." + PropertyName; - - string ns = ParentObject.OwnerDocument.GetNamespaceFor(PropertyTargetType); - string prefix = element.GetPrefixOfNamespace(ns); - - if (String.IsNullOrEmpty(prefix)) { - prefix = ParentObject.OwnerDocument.GetPrefixForNamespace(ns); - } - - if (!string.IsNullOrEmpty(prefix)) { - element.SetAttribute(name, ns, value); - return element.GetAttributeNode(name, ns); - } - } else { - name = PropertyName; - } - - element.SetAttribute(name, string.Empty, value); - return element.GetAttributeNode(name); - } - - internal string GetNameForMarkupExtension() - { - if (IsAttached) { - string name = PropertyTargetType.Name + "." + PropertyName; - - var element = ParentObject.XmlElement; - string ns = ParentObject.OwnerDocument.GetNamespaceFor(PropertyTargetType); - var prefix = element.GetPrefixOfNamespace(ns); - if (string.IsNullOrEmpty(prefix)) - return name; - else - return prefix + ":" + name; - } else - return PropertyName; - } - - /// - /// used internally by the XamlParser. - /// Add a collection element that already is part of the XML DOM. - /// - internal void ParserAddCollectionElement(XmlElement collectionPropertyElement, XamlPropertyValue val) - { - if (collectionPropertyElement != null && _propertyElement == null) { - ParserSetPropertyElement(collectionPropertyElement); - } - collectionElements.AddInternal(val); - val.ParentProperty = this; - if (collectionPropertyElement != _propertyElement) { - val.RemoveNodeFromParent(); - val.AddNodeTo(this); - } - } - - /// - /// Gets/Sets the value of the property on the instance without updating the XAML document. - /// - public object ValueOnInstance { - get { - if (IsEvent) { - if (propertyValue != null) - return propertyValue.GetValueFor(null); - else - return null; - } else { - return propertyInfo.GetValue(parentObject.Instance); - } - } - set { - propertyInfo.SetValue(parentObject.Instance, value); - if (ValueOnInstanceChanged != null) - ValueOnInstanceChanged(this, EventArgs.Empty); - } - } - - /// - /// Gets if this property is considered "advanced" and should be hidden by default in a property grid. - /// - public bool IsAdvanced { - get { return propertyInfo.IsAdvanced; } - } - - /// - /// Gets the dependency property. - /// - public DependencyProperty DependencyProperty { - get { - return propertyInfo.DependencyProperty; - } - } - - void PossiblyNameChanged(XamlPropertyValue oldValue, XamlPropertyValue newValue) - { - if (ParentObject.RuntimeNameProperty != null && PropertyName == ParentObject.RuntimeNameProperty) { - - if (!String.IsNullOrEmpty(ParentObject.GetXamlAttribute("Name"))) { - throw new XamlLoadException("The property 'Name' is set more than once."); - } - - string oldName = null; - string newName = null; - - var oldTextValue = oldValue as XamlTextValue; - if (oldTextValue != null) oldName = oldTextValue.Text; - - var newTextValue = newValue as XamlTextValue; - if (newTextValue != null) newName = newTextValue.Text; - - NameScopeHelper.NameChanged(ParentObject, oldName, newName); - } - } - - /*public bool IsAttributeSyntax { - get { - return attribute != null; - } - } - - public bool IsElementSyntax { - get { - return element != null; - } - } - - public bool IsImplicitDefaultProperty { - get { - return attribute == null && element == null; - } - }*/ - } -} +// 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.Diagnostics; +using System.Linq; +using System.Text; +using System.Xml; +using System.Windows; +using System.Windows.Markup; + +namespace ICSharpCode.WpfDesign.XamlDom +{ + /// + /// Describes a property on a . + /// + [DebuggerDisplay("XamlProperty: {PropertyName}")] + public sealed class XamlProperty + { + XamlObject parentObject; + internal readonly XamlPropertyInfo propertyInfo; + XamlPropertyValue propertyValue; + + CollectionElementsCollection collectionElements; + bool isCollection; + bool isResources; + + static readonly IList emptyCollectionElementsArray = new XamlPropertyValue[0]; + + // for use by parser only + internal XamlProperty(XamlObject parentObject, XamlPropertyInfo propertyInfo, XamlPropertyValue propertyValue) + : this(parentObject, propertyInfo) + { + PossiblyNameChanged(null, propertyValue); + + this.propertyValue = propertyValue; + if (propertyValue != null) { + propertyValue.ParentProperty = this; + } + + UpdateValueOnInstance(); + } + + internal XamlProperty(XamlObject parentObject, XamlPropertyInfo propertyInfo) + { + this.parentObject = parentObject; + this.propertyInfo = propertyInfo; + + if (propertyInfo.IsCollection) { + isCollection = true; + collectionElements = new CollectionElementsCollection(this); + + if (propertyInfo.Name.Equals(XamlConstants.ResourcesPropertyName, StringComparison.Ordinal) && + propertyInfo.ReturnType == typeof(ResourceDictionary)) { + isResources = true; + } + } + } + + /// + /// Gets the parent object for which this property was declared. + /// + public XamlObject ParentObject { + get { return parentObject; } + } + + /// + /// Gets the property name. + /// + public string PropertyName { + get { return propertyInfo.Name; } + } + + /// + /// Gets the type the property is declared on. + /// + public Type PropertyTargetType { + get { return propertyInfo.TargetType; } + } + + /// + /// Gets if this property is an attached property. + /// + public bool IsAttached { + get { return propertyInfo.IsAttached; } + } + + /// + /// Gets if this property is an event. + /// + public bool IsEvent { + get { return propertyInfo.IsEvent; } + } + + /// + /// Gets the return type of the property. + /// + public Type ReturnType { + get { return propertyInfo.ReturnType; } + } + + /// + /// Gets the type converter used to convert property values to/from string. + /// + public TypeConverter TypeConverter { + get { return propertyInfo.TypeConverter; } + } + + /// + /// Gets the category of the property. + /// + public string Category { + get { return propertyInfo.Category; } + } + + /// + /// Gets the value of the property. Can be null if the property is a collection property. + /// + public XamlPropertyValue PropertyValue { + get { return propertyValue; } + set { SetPropertyValue(value); } + } + + /// + /// Gets if the property represents the FrameworkElement.Resources property that holds a locally-defined resource dictionary. + /// + public bool IsResources { + get { return isResources; } + } + + /// + /// Gets if the property is a collection property. + /// + public bool IsCollection { + get { return isCollection; } + } + + /// + /// Gets the collection elements of the property. Is empty if the property is not a collection. + /// + public IList CollectionElements { + get { return collectionElements ?? emptyCollectionElementsArray; } + } + + /// + /// Gets if the property is set. + /// + public bool IsSet { + get { return propertyValue != null || + _propertyElement != null; // collection + } + } + + /// + /// Occurs when the value of the IsSet property has changed. + /// + public event EventHandler IsSetChanged; + + /// + /// Occurs when the value of the property has changed. + /// + public event EventHandler ValueChanged; + + /// + /// Occurs when MarkupExtension evaluated PropertyValue dosn't changed but ValueOnInstance does. + /// + public event EventHandler ValueOnInstanceChanged; + + void SetPropertyValue(XamlPropertyValue value) + { + // Binding... + //if (IsCollection) { + // throw new InvalidOperationException("Cannot set the value of collection properties."); + //} + + bool wasSet = this.IsSet; + + PossiblyNameChanged(propertyValue, value); + + //reset expression + var xamlObject = propertyValue as XamlObject; + if (xamlObject != null && xamlObject.IsMarkupExtension) + propertyInfo.ResetValue(parentObject.Instance); + + ResetInternal(); + + propertyValue = value; + propertyValue.ParentProperty = this; + propertyValue.AddNodeTo(this); + UpdateValueOnInstance(); + + ParentObject.OnPropertyChanged(this); + + if (!wasSet) { + if (IsSetChanged != null) { + IsSetChanged(this, EventArgs.Empty); + } + } + + if (ValueChanged != null) { + ValueChanged(this, EventArgs.Empty); + } + } + + internal void UpdateValueOnInstance() + { + if (PropertyValue != null) { + try { + ValueOnInstance = PropertyValue.GetValueFor(propertyInfo); + } + catch { + Debug.WriteLine("UpdateValueOnInstance() failed"); + } + } + } + + /// + /// Resets the properties value. + /// + public void Reset() + { + if (IsSet) { + + propertyInfo.ResetValue(parentObject.Instance); + ResetInternal(); + + ParentObject.OnPropertyChanged(this); + + if (IsSetChanged != null) { + IsSetChanged(this, EventArgs.Empty); + } + if (ValueChanged != null) { + ValueChanged(this, EventArgs.Empty); + } + } + } + + void ResetInternal() + { + if (propertyValue != null) { + propertyValue.RemoveNodeFromParent(); + propertyValue.ParentProperty = null; + propertyValue = null; + } + if (_propertyElement != null) { + _propertyElement.ParentNode.RemoveChild(_propertyElement); + _propertyElement = null; + } + } + + XmlElement _propertyElement; + + internal void ParserSetPropertyElement(XmlElement propertyElement) + { + XmlElement oldPropertyElement = _propertyElement; + if (oldPropertyElement == propertyElement) return; + + _propertyElement = propertyElement; + + if (oldPropertyElement != null && IsCollection) { + Debug.WriteLine("Property element for " + this.PropertyName + " already exists, merging.."); + foreach (XamlPropertyValue val in this.collectionElements) { + val.RemoveNodeFromParent(); + val.AddNodeTo(this); + } + oldPropertyElement.ParentNode.RemoveChild(oldPropertyElement); + } + } + + bool IsFirstChildResources(XamlObject obj) + { + return obj.XmlElement.FirstChild != null && + obj.XmlElement.FirstChild.Name.EndsWith("." + XamlConstants.ResourcesPropertyName) && + obj.Properties.Where((prop) => prop.IsResources).FirstOrDefault() != null; + } + + XmlElement CreatePropertyElement() + { + string ns = parentObject.OwnerDocument.GetNamespaceFor(parentObject.ElementType); + return parentObject.OwnerDocument.XmlDocument.CreateElement( + parentObject.OwnerDocument.GetPrefixForNamespace(ns), + parentObject.ElementType.Name + "." + this.PropertyName, + ns + ); + } + + internal void AddChildNodeToProperty(XmlNode newChildNode) + { + if (this.IsCollection) { + // this is the default collection + InsertNodeInCollection(newChildNode, collectionElements.Count); + return; + } + if (_propertyElement == null) { + if (PropertyName == parentObject.ContentPropertyName) { + if (IsFirstChildResources(parentObject)) { + // Resources element should always be first + parentObject.XmlElement.InsertAfter(newChildNode, parentObject.XmlElement.FirstChild); + } + else + parentObject.XmlElement.InsertBefore(newChildNode, parentObject.XmlElement.FirstChild); + return; + } + _propertyElement = CreatePropertyElement(); + + if (IsFirstChildResources(parentObject)) { + // Resources element should always be first + parentObject.XmlElement.InsertAfter(_propertyElement, parentObject.XmlElement.FirstChild); + } + else + parentObject.XmlElement.InsertBefore(_propertyElement, parentObject.XmlElement.FirstChild); + } + _propertyElement.AppendChild(newChildNode); + } + + internal void InsertNodeInCollection(XmlNode newChildNode, int index) + { + Debug.Assert(index >= 0 && index <= collectionElements.Count); + XmlElement collection = _propertyElement; + if (collection == null) { + if (collectionElements.Count == 0 && this.PropertyName != this.ParentObject.ContentPropertyName) { + // we have to create the collection element + _propertyElement = CreatePropertyElement(); + + if (this.IsResources) { + parentObject.XmlElement.PrependChild(_propertyElement); + } else { + parentObject.XmlElement.AppendChild(_propertyElement); + } + + collection = _propertyElement; + } else { + // this is the default collection + collection = parentObject.XmlElement; + } + } + if (collectionElements.Count == 0) { + // collection is empty -> we may insert anywhere + collection.AppendChild(newChildNode); + } else if (index == collectionElements.Count) { + // insert after last element in collection + collection.InsertAfter(newChildNode, collectionElements[collectionElements.Count - 1].GetNodeForCollection()); + } else { + // insert before specified index + collection.InsertBefore(newChildNode, collectionElements[index].GetNodeForCollection()); + } + } + + internal XmlAttribute SetAttribute(string value) + { + string name; + var element = ParentObject.XmlElement; + + if (IsAttached) + { + if (PropertyTargetType == typeof (DesignTimeProperties) || PropertyTargetType == typeof (MarkupCompatibilityProperties)) + name = PropertyName; + else + name = PropertyTargetType.Name + "." + PropertyName; + + string ns = ParentObject.OwnerDocument.GetNamespaceFor(PropertyTargetType); + string prefix = element.GetPrefixOfNamespace(ns); + + if (String.IsNullOrEmpty(prefix)) { + prefix = ParentObject.OwnerDocument.GetPrefixForNamespace(ns); + } + + if (!string.IsNullOrEmpty(prefix)) { + element.SetAttribute(name, ns, value); + return element.GetAttributeNode(name, ns); + } + } else { + name = PropertyName; + } + + element.SetAttribute(name, string.Empty, value); + return element.GetAttributeNode(name); + } + + internal string GetNameForMarkupExtension() + { + if (IsAttached) { + string name = PropertyTargetType.Name + "." + PropertyName; + + var element = ParentObject.XmlElement; + string ns = ParentObject.OwnerDocument.GetNamespaceFor(PropertyTargetType); + var prefix = element.GetPrefixOfNamespace(ns); + if (string.IsNullOrEmpty(prefix)) + return name; + else + return prefix + ":" + name; + } else + return PropertyName; + } + + /// + /// used internally by the XamlParser. + /// Add a collection element that already is part of the XML DOM. + /// + internal void ParserAddCollectionElement(XmlElement collectionPropertyElement, XamlPropertyValue val) + { + if (collectionPropertyElement != null && _propertyElement == null) { + ParserSetPropertyElement(collectionPropertyElement); + } + collectionElements.AddInternal(val); + val.ParentProperty = this; + if (collectionPropertyElement != _propertyElement) { + val.RemoveNodeFromParent(); + val.AddNodeTo(this); + } + } + + /// + /// Gets/Sets the value of the property on the instance without updating the XAML document. + /// + public object ValueOnInstance { + get { + if (IsEvent) { + if (propertyValue != null) + return propertyValue.GetValueFor(null); + else + return null; + } else { + return propertyInfo.GetValue(parentObject.Instance); + } + } + set { + propertyInfo.SetValue(parentObject.Instance, value); + if (ValueOnInstanceChanged != null) + ValueOnInstanceChanged(this, EventArgs.Empty); + } + } + + /// + /// Gets if this property is considered "advanced" and should be hidden by default in a property grid. + /// + public bool IsAdvanced { + get { return propertyInfo.IsAdvanced; } + } + + /// + /// Gets the dependency property. + /// + public DependencyProperty DependencyProperty { + get { + return propertyInfo.DependencyProperty; + } + } + + void PossiblyNameChanged(XamlPropertyValue oldValue, XamlPropertyValue newValue) + { + if (ParentObject.RuntimeNameProperty != null && PropertyName == ParentObject.RuntimeNameProperty) { + + if (!String.IsNullOrEmpty(ParentObject.GetXamlAttribute("Name"))) { + throw new XamlLoadException("The property 'Name' is set more than once."); + } + + string oldName = null; + string newName = null; + + var oldTextValue = oldValue as XamlTextValue; + if (oldTextValue != null) oldName = oldTextValue.Text; + + var newTextValue = newValue as XamlTextValue; + if (newTextValue != null) newName = newTextValue.Text; + + NameScopeHelper.NameChanged(ParentObject, oldName, newName); + } + } + + /*public bool IsAttributeSyntax { + get { + return attribute != null; + } + } + + public bool IsElementSyntax { + get { + return element != null; + } + } + + public bool IsImplicitDefaultProperty { + get { + return attribute == null && element == null; + } + }*/ + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ExtensionMethods.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ExtensionMethods.cs index bd9df3670c..af0549508f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ExtensionMethods.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/ExtensionMethods.cs @@ -1,8 +1,8 @@ // 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.ComponentModel; +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.ComponentModel; using System.Windows; namespace ICSharpCode.WpfDesign @@ -23,52 +23,52 @@ namespace ICSharpCode.WpfDesign Math.Round(rect.Width, PlacementInformation.BoundsPrecision), Math.Round(rect.Height, PlacementInformation.BoundsPrecision) ); - } - - /// - /// Gets the design item property for the specified member descriptor. - /// - public static DesignItemProperty GetProperty(this DesignItemPropertyCollection properties, MemberDescriptor md) - { - DesignItemProperty prop = null; - - var pd = md as PropertyDescriptor; - if (pd != null) - { - var dpd = DependencyPropertyDescriptor.FromProperty(pd); - if (dpd != null) - { - if (dpd.IsAttached) - { - prop = properties.GetAttachedProperty(dpd.DependencyProperty); - } - else - { - prop = properties.GetProperty(dpd.DependencyProperty); - } - } - } - - if (prop == null) - { - prop = properties[md.Name]; - } - - return prop; - } - - /// - /// Gets if the specified design item property represents an attached dependency property. - /// - public static bool IsAttachedDependencyProperty(this DesignItemProperty property) - { - if (property.DependencyProperty != null) - { - var dpd = DependencyPropertyDescriptor.FromProperty(property.DependencyProperty, property.DesignItem.ComponentType); - return dpd.IsAttached; - } - - return false; + } + + /// + /// Gets the design item property for the specified member descriptor. + /// + public static DesignItemProperty GetProperty(this DesignItemPropertyCollection properties, MemberDescriptor md) + { + DesignItemProperty prop = null; + + var pd = md as PropertyDescriptor; + if (pd != null) + { + var dpd = DependencyPropertyDescriptor.FromProperty(pd); + if (dpd != null) + { + if (dpd.IsAttached) + { + prop = properties.GetAttachedProperty(dpd.DependencyProperty); + } + else + { + prop = properties.GetProperty(dpd.DependencyProperty); + } + } + } + + if (prop == null) + { + prop = properties[md.Name]; + } + + return prop; + } + + /// + /// Gets if the specified design item property represents an attached dependency property. + /// + public static bool IsAttachedDependencyProperty(this DesignItemProperty property) + { + if (property.DependencyProperty != null) + { + var dpd = DependencyPropertyDescriptor.FromProperty(property.DependencyProperty, property.DesignItem.ComponentType); + return dpd.IsAttached; + } + + return false; } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyGrid/PropertyNode.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyGrid/PropertyNode.cs index dd1efb0a43..46dafef069 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyGrid/PropertyNode.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyGrid/PropertyNode.cs @@ -1,420 +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.Linq; -using System.Text; -using System.ComponentModel; -using System.Windows; -using System.Windows.Controls; -using System.Collections.ObjectModel; -using System.Windows.Data; -using System.Windows.Media; -using System.Windows.Markup; - -namespace ICSharpCode.WpfDesign.PropertyGrid -{ - /// - /// View-Model class for the property grid. - /// - public class PropertyNode : INotifyPropertyChanged - { - static object Unset = new object(); - - /// - /// Gets the properties that are presented by this node. - /// This might be multiple properties if multiple controls are selected. - /// - public ReadOnlyCollection Properties { get; private set; } - - bool raiseEvents = true; - bool hasStringConverter; - - /// - /// Gets the name of the property. - /// - public string Name - { - get - { - var dp = FirstProperty.DependencyProperty; - if (dp != null) - { - var dpd = DependencyPropertyDescriptor.FromProperty(dp, FirstProperty.DesignItem.ComponentType); - if (dpd.IsAttached) - { - // Will return the attached property name in the form of . - return dpd.Name; - } - } - - return FirstProperty.Name; - } - } - - /// - /// Gets if this property node represents an event. - /// - public bool IsEvent { get { return FirstProperty.IsEvent; } } - - /// - /// Gets the design context associated with this set of properties. - /// - public DesignContext Context { get { return FirstProperty.DesignItem.Context; } } - - /// - /// Gets the service container associated with this set of properties. - /// - public ServiceContainer Services { get { return FirstProperty.DesignItem.Services; } } - - /// - /// Gets the editor control that edits this property. - /// - public FrameworkElement Editor { get; private set; } - - /// - /// Gets the first property (equivalent to Properties[0]) - /// - public DesignItemProperty FirstProperty { get { return Properties[0]; } } - - /// - /// For nested property nodes, gets the parent node. - /// - public PropertyNode Parent { get; private set; } - - /// - /// For nested property nodes, gets the level of this node. - /// - public int Level { get; private set; } - - /// - /// Gets the category of this node. - /// - public Category Category { get; set; } - - /// - /// Gets the list of child nodes. - /// - public ObservableCollection Children { get; private set; } - - /// - /// Gets the list of advanced child nodes (not visible by default). - /// - public ObservableCollection MoreChildren { get; private set; } - - bool isExpanded; - - /// - /// Gets whether this property node is currently expanded. - /// - public bool IsExpanded { - get { - return isExpanded; - } - set { - isExpanded = value; - UpdateChildren(); - RaisePropertyChanged("IsExpanded"); - } - } - - /// - /// Gets whether this property node has children. - /// - public bool HasChildren { - get { return Children.Count > 0 || MoreChildren.Count > 0; } - } - - /// - /// Gets the description object using the IPropertyDescriptionService. - /// - public object Description { - get { - IPropertyDescriptionService s = Services.GetService(); - if (s != null) { - return s.GetDescription(FirstProperty); - } - return null; - } - } - - /// - /// Gets/Sets the value of this property. - /// - public object Value { - get { - if (IsAmbiguous) return null; - var result = FirstProperty.ValueOnInstance; - if (result == DependencyProperty.UnsetValue) return null; - return result; - } - set { - SetValueCore(value); - } - } - - /// - /// Gets/Sets the value of this property in string form - /// - public string ValueString { - get { - if (ValueItem == null || ValueItem.Component is MarkupExtension) { - if (Value == null) return null; - if (hasStringConverter) { - return FirstProperty.TypeConverter.ConvertToInvariantString(Value); - } - return "(" + Value.GetType().Name + ")"; - } - return "(" + ValueItem.ComponentType.Name + ")"; - } - set { - // make sure we only catch specific exceptions - // and/or show the error message to the user - //try { - Value = FirstProperty.TypeConverter.ConvertFromInvariantString(value); - //} catch { - // OnValueOnInstanceChanged(); - //} - } - } - - /// - /// Gets whether the property node is enabled for editing. - /// - public bool IsEnabled { - get { - return ValueItem == null && hasStringConverter; - } - } - - /// - /// Gets whether this property was set locally. - /// - public bool IsSet { - get { - foreach (var p in Properties) { - if (p.IsSet) return true; - } - return false; - } - } - - /// - /// Gets the color of the name. - /// Depends on the type of the value (binding/resource/etc.) - /// - public Brush NameForeground { - get { - if (ValueItem != null) { - object component = ValueItem.Component; - if (component is BindingBase) - return Brushes.DarkGoldenrod; - if (component is StaticResourceExtension || component is DynamicResourceExtension) - return Brushes.DarkGreen; - } - return SystemColors.WindowTextBrush; - } - } - - /// - /// Returns the DesignItem that owns the property (= the DesignItem that is currently selected). - /// Returns null if multiple DesignItems are selected. - /// - public DesignItem ValueItem { - get { - if (Properties.Count == 1) { - return FirstProperty.Value; - } - return null; - } - } - - /// - /// Gets whether the property value is ambiguous (multiple controls having different values are selected). - /// - public bool IsAmbiguous { - get { - foreach (var p in Properties) { - if (!object.Equals(p.ValueOnInstance, FirstProperty.ValueOnInstance)) { - return true; - } - } - return false; - } - } - - bool isVisible; - - /// - /// Gets/Sets whether the property is visible. - /// - public bool IsVisible { - get { - return isVisible; - } - set { - isVisible = value; - RaisePropertyChanged("IsVisible"); - } - } - - /// - /// Gets whether resetting the property is possible. - /// - public bool CanReset { - get { return IsSet; } - } - - /// - /// Resets the property. - /// - public void Reset() - { - SetValueCore(Unset); - } - - /// - /// Replaces the value of this node with a new binding. - /// - public void CreateBinding() - { - Value = new Binding(); - IsExpanded = true; - } - - void SetValueCore(object value) - { - raiseEvents = false; - if (value == Unset) { - foreach (var p in Properties) { - p.Reset(); - } - } else { - foreach (var p in Properties) { - p.SetValue(value); - } - } - raiseEvents = true; - OnValueChanged(); - } - - void OnValueChanged() - { - RaisePropertyChanged("IsSet"); - RaisePropertyChanged("Value"); - RaisePropertyChanged("ValueString"); - RaisePropertyChanged("IsAmbiguous"); - RaisePropertyChanged("FontWeight"); - RaisePropertyChanged("IsEnabled"); - RaisePropertyChanged("NameForeground"); - - UpdateChildren(); - } - - void OnValueOnInstanceChanged() - { - RaisePropertyChanged("Value"); - RaisePropertyChanged("ValueString"); - } - - /// - /// Creates a new PropertyNode instance. - /// - public PropertyNode() - { - Children = new ObservableCollection(); - MoreChildren = new ObservableCollection(); - } - - PropertyNode(DesignItemProperty[] properties, PropertyNode parent) : this() - { - this.Parent = parent; - this.Level = parent == null ? 0 : parent.Level + 1; - Load(properties); - } - - /// - /// Initializes this property node with the specified properties. - /// - public void Load(DesignItemProperty[] properties) - { - if (this.Properties != null) { - // detach events from old properties - foreach (var property in this.Properties) { - property.ValueChanged -= new EventHandler(property_ValueChanged); - property.ValueOnInstanceChanged -= new EventHandler(property_ValueOnInstanceChanged); - } - } - - this.Properties = new ReadOnlyCollection(properties); - - if (Editor == null) - Editor = EditorManager.CreateEditor(FirstProperty); - - foreach (var property in properties) { - property.ValueChanged += new EventHandler(property_ValueChanged); - property.ValueOnInstanceChanged += new EventHandler(property_ValueOnInstanceChanged); - } - - hasStringConverter = - FirstProperty.TypeConverter.CanConvertFrom(typeof(string)) && - FirstProperty.TypeConverter.CanConvertTo(typeof(string)); - - OnValueChanged(); - } - - void property_ValueOnInstanceChanged(object sender, EventArgs e) - { - if (raiseEvents) OnValueOnInstanceChanged(); - } - - void property_ValueChanged(object sender, EventArgs e) - { - if (raiseEvents) OnValueChanged(); - } - - void UpdateChildren() - { - Children.Clear(); - MoreChildren.Clear(); - - if (Parent == null || Parent.IsExpanded) { - if (ValueItem != null) { - var list = TypeHelper.GetAvailableProperties(ValueItem.Component) - .OrderBy(d => d.Name) - .Select(d => new PropertyNode(new[] { ValueItem.Properties.GetProperty(d) }, this)); - - foreach (var node in list) { - if (Metadata.IsBrowsable(node.FirstProperty)) { - node.IsVisible = true; - if (Metadata.IsPopularProperty(node.FirstProperty)) { - Children.Add(node); - } else { - MoreChildren.Add(node); - } - } - } - } - } - - RaisePropertyChanged("HasChildren"); - } - - #region INotifyPropertyChanged Members - - /// - /// Occurs when a property has changed. Used to support WPF data binding. - /// - public event PropertyChangedEventHandler PropertyChanged; - - void RaisePropertyChanged(string name) - { - if (PropertyChanged != null) { - PropertyChanged(this, new PropertyChangedEventArgs(name)); - } - } - - #endregion - } -} +// 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.Linq; +using System.Text; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Collections.ObjectModel; +using System.Windows.Data; +using System.Windows.Media; +using System.Windows.Markup; + +namespace ICSharpCode.WpfDesign.PropertyGrid +{ + /// + /// View-Model class for the property grid. + /// + public class PropertyNode : INotifyPropertyChanged + { + static object Unset = new object(); + + /// + /// Gets the properties that are presented by this node. + /// This might be multiple properties if multiple controls are selected. + /// + public ReadOnlyCollection Properties { get; private set; } + + bool raiseEvents = true; + bool hasStringConverter; + + /// + /// Gets the name of the property. + /// + public string Name + { + get + { + var dp = FirstProperty.DependencyProperty; + if (dp != null) + { + var dpd = DependencyPropertyDescriptor.FromProperty(dp, FirstProperty.DesignItem.ComponentType); + if (dpd.IsAttached) + { + // Will return the attached property name in the form of . + return dpd.Name; + } + } + + return FirstProperty.Name; + } + } + + /// + /// Gets if this property node represents an event. + /// + public bool IsEvent { get { return FirstProperty.IsEvent; } } + + /// + /// Gets the design context associated with this set of properties. + /// + public DesignContext Context { get { return FirstProperty.DesignItem.Context; } } + + /// + /// Gets the service container associated with this set of properties. + /// + public ServiceContainer Services { get { return FirstProperty.DesignItem.Services; } } + + /// + /// Gets the editor control that edits this property. + /// + public FrameworkElement Editor { get; private set; } + + /// + /// Gets the first property (equivalent to Properties[0]) + /// + public DesignItemProperty FirstProperty { get { return Properties[0]; } } + + /// + /// For nested property nodes, gets the parent node. + /// + public PropertyNode Parent { get; private set; } + + /// + /// For nested property nodes, gets the level of this node. + /// + public int Level { get; private set; } + + /// + /// Gets the category of this node. + /// + public Category Category { get; set; } + + /// + /// Gets the list of child nodes. + /// + public ObservableCollection Children { get; private set; } + + /// + /// Gets the list of advanced child nodes (not visible by default). + /// + public ObservableCollection MoreChildren { get; private set; } + + bool isExpanded; + + /// + /// Gets whether this property node is currently expanded. + /// + public bool IsExpanded { + get { + return isExpanded; + } + set { + isExpanded = value; + UpdateChildren(); + RaisePropertyChanged("IsExpanded"); + } + } + + /// + /// Gets whether this property node has children. + /// + public bool HasChildren { + get { return Children.Count > 0 || MoreChildren.Count > 0; } + } + + /// + /// Gets the description object using the IPropertyDescriptionService. + /// + public object Description { + get { + IPropertyDescriptionService s = Services.GetService(); + if (s != null) { + return s.GetDescription(FirstProperty); + } + return null; + } + } + + /// + /// Gets/Sets the value of this property. + /// + public object Value { + get { + if (IsAmbiguous) return null; + var result = FirstProperty.ValueOnInstance; + if (result == DependencyProperty.UnsetValue) return null; + return result; + } + set { + SetValueCore(value); + } + } + + /// + /// Gets/Sets the value of this property in string form + /// + public string ValueString { + get { + if (ValueItem == null || ValueItem.Component is MarkupExtension) { + if (Value == null) return null; + if (hasStringConverter) { + return FirstProperty.TypeConverter.ConvertToInvariantString(Value); + } + return "(" + Value.GetType().Name + ")"; + } + return "(" + ValueItem.ComponentType.Name + ")"; + } + set { + // make sure we only catch specific exceptions + // and/or show the error message to the user + //try { + Value = FirstProperty.TypeConverter.ConvertFromInvariantString(value); + //} catch { + // OnValueOnInstanceChanged(); + //} + } + } + + /// + /// Gets whether the property node is enabled for editing. + /// + public bool IsEnabled { + get { + return ValueItem == null && hasStringConverter; + } + } + + /// + /// Gets whether this property was set locally. + /// + public bool IsSet { + get { + foreach (var p in Properties) { + if (p.IsSet) return true; + } + return false; + } + } + + /// + /// Gets the color of the name. + /// Depends on the type of the value (binding/resource/etc.) + /// + public Brush NameForeground { + get { + if (ValueItem != null) { + object component = ValueItem.Component; + if (component is BindingBase) + return Brushes.DarkGoldenrod; + if (component is StaticResourceExtension || component is DynamicResourceExtension) + return Brushes.DarkGreen; + } + return SystemColors.WindowTextBrush; + } + } + + /// + /// Returns the DesignItem that owns the property (= the DesignItem that is currently selected). + /// Returns null if multiple DesignItems are selected. + /// + public DesignItem ValueItem { + get { + if (Properties.Count == 1) { + return FirstProperty.Value; + } + return null; + } + } + + /// + /// Gets whether the property value is ambiguous (multiple controls having different values are selected). + /// + public bool IsAmbiguous { + get { + foreach (var p in Properties) { + if (!object.Equals(p.ValueOnInstance, FirstProperty.ValueOnInstance)) { + return true; + } + } + return false; + } + } + + bool isVisible; + + /// + /// Gets/Sets whether the property is visible. + /// + public bool IsVisible { + get { + return isVisible; + } + set { + isVisible = value; + RaisePropertyChanged("IsVisible"); + } + } + + /// + /// Gets whether resetting the property is possible. + /// + public bool CanReset { + get { return IsSet; } + } + + /// + /// Resets the property. + /// + public void Reset() + { + SetValueCore(Unset); + } + + /// + /// Replaces the value of this node with a new binding. + /// + public void CreateBinding() + { + Value = new Binding(); + IsExpanded = true; + } + + void SetValueCore(object value) + { + raiseEvents = false; + if (value == Unset) { + foreach (var p in Properties) { + p.Reset(); + } + } else { + foreach (var p in Properties) { + p.SetValue(value); + } + } + raiseEvents = true; + OnValueChanged(); + } + + void OnValueChanged() + { + RaisePropertyChanged("IsSet"); + RaisePropertyChanged("Value"); + RaisePropertyChanged("ValueString"); + RaisePropertyChanged("IsAmbiguous"); + RaisePropertyChanged("FontWeight"); + RaisePropertyChanged("IsEnabled"); + RaisePropertyChanged("NameForeground"); + + UpdateChildren(); + } + + void OnValueOnInstanceChanged() + { + RaisePropertyChanged("Value"); + RaisePropertyChanged("ValueString"); + } + + /// + /// Creates a new PropertyNode instance. + /// + public PropertyNode() + { + Children = new ObservableCollection(); + MoreChildren = new ObservableCollection(); + } + + PropertyNode(DesignItemProperty[] properties, PropertyNode parent) : this() + { + this.Parent = parent; + this.Level = parent == null ? 0 : parent.Level + 1; + Load(properties); + } + + /// + /// Initializes this property node with the specified properties. + /// + public void Load(DesignItemProperty[] properties) + { + if (this.Properties != null) { + // detach events from old properties + foreach (var property in this.Properties) { + property.ValueChanged -= new EventHandler(property_ValueChanged); + property.ValueOnInstanceChanged -= new EventHandler(property_ValueOnInstanceChanged); + } + } + + this.Properties = new ReadOnlyCollection(properties); + + if (Editor == null) + Editor = EditorManager.CreateEditor(FirstProperty); + + foreach (var property in properties) { + property.ValueChanged += new EventHandler(property_ValueChanged); + property.ValueOnInstanceChanged += new EventHandler(property_ValueOnInstanceChanged); + } + + hasStringConverter = + FirstProperty.TypeConverter.CanConvertFrom(typeof(string)) && + FirstProperty.TypeConverter.CanConvertTo(typeof(string)); + + OnValueChanged(); + } + + void property_ValueOnInstanceChanged(object sender, EventArgs e) + { + if (raiseEvents) OnValueOnInstanceChanged(); + } + + void property_ValueChanged(object sender, EventArgs e) + { + if (raiseEvents) OnValueChanged(); + } + + void UpdateChildren() + { + Children.Clear(); + MoreChildren.Clear(); + + if (Parent == null || Parent.IsExpanded) { + if (ValueItem != null) { + var list = TypeHelper.GetAvailableProperties(ValueItem.Component) + .OrderBy(d => d.Name) + .Select(d => new PropertyNode(new[] { ValueItem.Properties.GetProperty(d) }, this)); + + foreach (var node in list) { + if (Metadata.IsBrowsable(node.FirstProperty)) { + node.IsVisible = true; + if (Metadata.IsPopularProperty(node.FirstProperty)) { + Children.Add(node); + } else { + MoreChildren.Add(node); + } + } + } + } + } + + RaisePropertyChanged("HasChildren"); + } + + #region INotifyPropertyChanged Members + + /// + /// Occurs when a property has changed. Used to support WPF data binding. + /// + public event PropertyChangedEventHandler PropertyChanged; + + void RaisePropertyChanged(string name) + { + if (PropertyChanged != null) { + PropertyChanged(this, new PropertyChangedEventArgs(name)); + } + } + + #endregion + } +} diff --git a/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs b/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs index 9e165a4f1e..0d6007ece5 100644 --- a/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs +++ b/src/Main/Base/Project/Src/Editor/IBracketSearcher.cs @@ -1,52 +1,52 @@ -// 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 ICSharpCode.SharpDevelop.Editor -{ - /// - /// Allows language specific search for matching brackets. - /// - public interface IBracketSearcher - { - /// - /// Searches for a matching bracket from the given offset to the start of the document. - /// - /// A BracketSearchResult that contains the positions and lengths of the brackets. Return null if there is nothing to highlight. - BracketSearchResult SearchBracket(IDocument document, int offset); - } - - public class DefaultBracketSearcher : IBracketSearcher - { - public static readonly DefaultBracketSearcher DefaultInstance = new DefaultBracketSearcher(); - - public BracketSearchResult SearchBracket(IDocument document, int offset) - { - return null; - } - } - - /// - /// Describes a pair of matching brackets found by IBracketSearcher. - /// - public class BracketSearchResult - { - public int OpeningBracketOffset { get; private set; } - - public int OpeningBracketLength { get; private set; } - - public int ClosingBracketOffset { get; private set; } - - public int ClosingBracketLength { get; private set; } - - public BracketSearchResult(int openingBracketOffset, int openingBracketLength, - int closingBracketOffset, int closingBracketLength) - { - this.OpeningBracketOffset = openingBracketOffset; - this.OpeningBracketLength = openingBracketLength; - this.ClosingBracketOffset = closingBracketOffset; - this.ClosingBracketLength = closingBracketLength; - } - } -} +// 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 ICSharpCode.SharpDevelop.Editor +{ + /// + /// Allows language specific search for matching brackets. + /// + public interface IBracketSearcher + { + /// + /// Searches for a matching bracket from the given offset to the start of the document. + /// + /// A BracketSearchResult that contains the positions and lengths of the brackets. Return null if there is nothing to highlight. + BracketSearchResult SearchBracket(IDocument document, int offset); + } + + public class DefaultBracketSearcher : IBracketSearcher + { + public static readonly DefaultBracketSearcher DefaultInstance = new DefaultBracketSearcher(); + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + return null; + } + } + + /// + /// Describes a pair of matching brackets found by IBracketSearcher. + /// + public class BracketSearchResult + { + public int OpeningBracketOffset { get; private set; } + + public int OpeningBracketLength { get; private set; } + + public int ClosingBracketOffset { get; private set; } + + public int ClosingBracketLength { get; private set; } + + public BracketSearchResult(int openingBracketOffset, int openingBracketLength, + int closingBracketOffset, int closingBracketLength) + { + this.OpeningBracketOffset = openingBracketOffset; + this.OpeningBracketLength = openingBracketLength; + this.ClosingBracketOffset = closingBracketOffset; + this.ClosingBracketLength = closingBracketLength; + } + } +}