diff --git a/data/resources/StringResources.resx b/data/resources/StringResources.resx
index 787a4f0294..f29cd9261e 100644
--- a/data/resources/StringResources.resx
+++ b/data/resources/StringResources.resx
@@ -5728,6 +5728,15 @@ Microsoft.Tools.WindowsInstallerXml.Extensions.NetFxCompiler, WixNetFxExtension<
Object Graph
+
+ Memory
+
+
+ Jump to address:
+
+
+ Refresh
+
Parallel Stacks
diff --git a/data/resources/image/BitmapResources/BitmapResources.res b/data/resources/image/BitmapResources/BitmapResources.res
index 1cb487ede6..3e8469ea86 100644
--- a/data/resources/image/BitmapResources/BitmapResources.res
+++ b/data/resources/image/BitmapResources/BitmapResources.res
@@ -46,6 +46,8 @@ ParallelStacks.ZoomControl = PadIcons\ZoomControl.png
OutputPad.Toolbar.ClearOutputWindow = OutputPadIcons\ClearOutputWindow.png
OutputPad.Toolbar.ToggleWordWrap = OutputPadIcons\ToggleWordWrap.png
+#Memory pad
+MemoryPad.Icon = PadIcons\memory.png
Icons.16x16.OpenFolderBitmap = ProjectBrowserIcons\Folder.Open.png
Icons.16x16.ClosedFolderBitmap = ProjectBrowserIcons\Folder.Closed.png
diff --git a/data/resources/image/BitmapResources/PadIcons/memory.png b/data/resources/image/BitmapResources/PadIcons/memory.png
new file mode 100644
index 0000000000..879019b26b
Binary files /dev/null and b/data/resources/image/BitmapResources/PadIcons/memory.png differ
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
index 210f63b594..28c0df125a 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.addin
@@ -117,6 +117,13 @@
class = "ICSharpCode.SharpDevelop.Gui.Pads.ConsolePad"
defaultPosition = "Bottom, Hidden" />
+
+
+
+
+
+
+
+
+
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
index 6de0fd70b0..ddced0ecf8 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
@@ -102,6 +102,7 @@
CallStackPad.xaml
Code
+
@@ -120,6 +121,7 @@
WatchListAutoCompleteCell.xaml
+
DrawSurface.xaml
Code
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/MemoryPadCommands.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/MemoryPadCommands.cs
new file mode 100644
index 0000000000..8b37bbffb0
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/Commands/MemoryPadCommands.cs
@@ -0,0 +1,64 @@
+// 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 System.Windows.Input;
+
+using ICSharpCode.Core;
+
+namespace ICSharpCode.SharpDevelop.Gui.Pads
+{
+ public sealed class JumpToAddressCommand : AbstractComboBoxCommand
+ {
+ MemoryPad pad;
+ ComboBox comboBox;
+
+ protected override void OnOwnerChanged(EventArgs e)
+ {
+ this.pad = this.Owner as MemoryPad;
+ if (this.pad == null)
+ return;
+
+ comboBox = this.ComboBox as ComboBox;
+
+ if (this.comboBox == null)
+ return;
+
+ comboBox.KeyUp += (s, ea) => { if (ea.Key == Key.Enter) Run(); };
+ comboBox.IsEditable = true;
+ comboBox.Width = 130;
+
+ base.OnOwnerChanged(e);
+ }
+
+ public override void Run()
+ {
+ if (this.pad != null && this.comboBox != null) {
+ pad.JumpToAddress(comboBox.Text);
+ }
+ base.Run();
+ }
+ }
+
+ public sealed class RefreshMemoryCommand : AbstractCommand
+ {
+ MemoryPad pad;
+
+ protected override void OnOwnerChanged(EventArgs e)
+ {
+ this.pad = this.Owner as MemoryPad;
+ if (this.pad == null)
+ return;
+
+ base.OnOwnerChanged(e);
+ }
+
+ public override void Run()
+ {
+ if (this.pad == null)
+ return;
+
+ this.pad.Refresh(true);
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Pads/MemoryPad.cs b/src/AddIns/Debugger/Debugger.AddIn/Pads/MemoryPad.cs
new file mode 100644
index 0000000000..1ba1f5735d
--- /dev/null
+++ b/src/AddIns/Debugger/Debugger.AddIn/Pads/MemoryPad.cs
@@ -0,0 +1,169 @@
+// 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.Globalization;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows;
+using System.Windows.Controls;
+
+using Debugger;
+using Debugger.Interop;
+using ICSharpCode.Core;
+using ICSharpCode.Core.Presentation;
+using ICSharpCode.SharpDevelop.Debugging;
+
+namespace ICSharpCode.SharpDevelop.Gui.Pads
+{
+ public sealed class MemoryPad : DebuggerPad
+ {
+ Dictionary addressesMapping = new Dictionary();
+ ConsoleControl console;
+ int addressStep = 16;
+
+ Process debuggedProcess;
+
+ public MemoryPad()
+ {
+ this.console = new ConsoleControl();
+ this.panel.Children.Add(console);
+ this.console.Encoding = Encoding.Default;
+ RefreshPad();
+ this.console.SetReadonly();
+ }
+
+ protected override ToolBar BuildToolBar()
+ {
+ return ToolBarService.CreateToolBar(panel, this, "/SharpDevelop/Pads/MemoryPad/ToolBar");
+ }
+
+ protected override void SelectProcess(Process process)
+ {
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused -= OnProcessPaused;
+ }
+ debuggedProcess = process;
+ if (debuggedProcess != null) {
+ debuggedProcess.Paused += OnProcessPaused;
+ }
+ }
+
+ public override void RefreshPad()
+ {
+ Refresh();
+ base.RefreshPad();
+ }
+
+ public void JumpToAddress(string address)
+ {
+ try {
+ if (address.StartsWith("0x"))
+ address = address.Substring(2);
+
+ long addr = Int64.Parse(address, NumberStyles.AllowHexSpecifier);
+ long mod = addr % addressStep;
+
+ int line;
+ if (addressesMapping.ContainsKey(addr - mod))
+ line = addressesMapping[addr - mod];
+ else
+ line = 1;
+
+ console.SelectText(line, 0, 8);
+ console.JumpToLine(line);
+ } catch (System.Exception ex) {
+ #if DEBUG
+ LoggingService.Error(ex.Message);
+ #endif
+ }
+ }
+
+ public void Refresh(bool force = false)
+ {
+ if (debuggedProcess == null || debugger.IsProcessRunning)
+ return;
+
+ if (!force && addressesMapping.Count > 0)
+ return;
+
+ if (force) {
+ addressesMapping.Clear();
+ console.Clear();
+ }
+
+ long address;
+ byte[] memory = debuggedProcess.ReadProcessMemory(out address);
+
+ if (memory == null)
+ return;
+
+ int index = 0;
+ int div = memory.Length / addressStep;
+ int mod = memory.Length % addressStep;
+
+ while (index < div) {
+ StringBuilder sb = new StringBuilder();
+ addressesMapping.Add(address, index + 1);
+ // write address
+ sb.Append(address.ToString("X8"));address += addressStep;
+ sb.Append(" ");
+
+ // write bytes
+ for (int i = 0; i < addressStep; ++i) {
+ sb.Append(memory[index * addressStep + i].ToString("X2") + " ");
+ }
+ // write chars
+ StringBuilder sb1 = new StringBuilder();
+ for (int i = 0; i < addressStep; ++i) {
+ sb1.Append(((char)memory[index * addressStep + i]).ToString());
+ }
+ string s = sb1.ToString();
+ s = Regex.Replace(s, @"\r\n", string.Empty);
+ s = Regex.Replace(s, @"\n", string.Empty);
+ s = Regex.Replace(s, @"\r", string.Empty);
+ sb.Append(s);
+ sb.Append(Environment.NewLine);
+
+ // start writing in console
+ console.Append(sb.ToString());
+
+ index++;
+ }
+
+ if (mod != 0) {
+ // write the rest of memory
+ StringBuilder sb = new StringBuilder();
+ addressesMapping.Add(address, index + 1);
+ // write address
+ sb.Append(address.ToString("X8"));
+ sb.Append(" ");
+
+ // write bytes
+ for (int i = 0; i < mod; ++i) {
+ sb.Append(memory[index * addressStep + i].ToString("X2") + " ");
+ }
+ // write chars
+ StringBuilder sb1 = new StringBuilder();
+ for (int i = 0; i < mod; ++i) {
+ sb1.Append(((char)memory[index * addressStep + i]).ToString());
+ }
+ string s = sb1.ToString();
+ s = Regex.Replace(s, @"\r\n", string.Empty);
+ s = Regex.Replace(s, @"\n", string.Empty);
+ s = Regex.Replace(s, @"\r", string.Empty);
+ sb.Append(s);
+
+ sb.Append(Environment.NewLine);
+
+ // start writing in console
+ console.Append(sb.ToString());
+ }
+ }
+
+ private void OnProcessPaused(object sender, ProcessEventArgs e)
+ {
+ Refresh();
+ }
+ }
+}
diff --git a/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs b/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs
index 0a541f1e61..5c99cf079c 100644
--- a/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs
+++ b/src/AddIns/Debugger/Debugger.Core/Interop/NativeMethods.cs
@@ -7,8 +7,37 @@ using System;
using System.Runtime.InteropServices;
using System.Text;
+using Debugger.Interop.CorDebug;
+
namespace Debugger.Interop
-{
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MEMORY_BASIC_INFORMATION
+ {
+ public IntPtr BaseAddress;
+ public IntPtr AllocationBase;
+ public uint AllocationProtect;
+ public IntPtr RegionSize;
+ public uint State;
+ public uint Protect;
+ public uint Type;
+ }
+
+ [Flags]
+ public enum ProcessAccessFlags : uint
+ {
+ All = 0x001F0FFF,
+ Terminate = 0x00000001,
+ CreateThread = 0x00000002,
+ VMOperation = 0x00000008,
+ VMRead = 0x00000010,
+ VMWrite = 0x00000020,
+ DupHandle = 0x00000040,
+ SetInformation = 0x00000200,
+ QueryInformation = 0x00000400,
+ Synchronize = 0x00100000
+ }
+
public static class NativeMethods
{
[DllImport("kernel32.dll")]
@@ -22,6 +51,69 @@ namespace Debugger.Interop
[DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
public static extern int GetRequestedRuntimeVersion(string exeFilename, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pVersion, Int32 cchBuffer, out Int32 dwLength);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool VirtualQueryEx(IntPtr hProcess,
+ IntPtr lpAddress,
+ out MEMORY_BASIC_INFORMATION lpBuffer,
+ uint dwLength);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress,
+ UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool ReadProcessMemory(
+ IntPtr hProcess,
+ IntPtr lpBaseAddress,
+ [Out] byte[] lpBuffer,
+ int dwSize,
+ out int lpNumberOfBytesRead
+ );
+
+ public static byte[] ReadProcessMemory(this Process process, out long baseAddress)
+ {
+ uint handle = process.CorProcess.GetHandle();
+
+ var proc = System.Diagnostics.Process.GetProcessById((int)process.Id);
+ baseAddress = proc.MainModule.BaseAddress.ToInt64();
+ long addr = baseAddress;
+
+ byte[] memory = null;
+
+ while (true)
+ {
+ byte[] temp = new byte[1024];
+ int outSize;
+ bool success = ReadProcessMemory(new IntPtr(handle), new IntPtr(addr), temp, temp.Length, out outSize);
+
+ addr += 1024;
+
+ if (outSize == 0)
+ break;
+
+ if (memory == null) {
+ memory = new byte[outSize];
+ Array.Copy(temp, memory, outSize);
+ } else {
+ // expand memory
+ byte[] newTemp = new byte[memory.Length];
+ Array.Copy(memory, newTemp, memory.Length);
+
+ memory = new byte[memory.Length + outSize];
+ Array.Copy(newTemp, memory, newTemp.Length);
+ Array.Copy(temp, 0, memory, newTemp.Length, outSize);
+ }
+
+ if (!success) // break when we cannot read anymore
+ break;
+ }
+
+ return memory;
+ }
}
}
diff --git a/src/AddIns/Debugger/Debugger.Core/Process.cs b/src/AddIns/Debugger/Debugger.Core/Process.cs
index eec5f36165..b788876e84 100644
--- a/src/AddIns/Debugger/Debugger.Core/Process.cs
+++ b/src/AddIns/Debugger/Debugger.Core/Process.cs
@@ -362,6 +362,10 @@ namespace Debugger
get { return pauseSession == null; }
}
+ public uint Id {
+ get { return corProcess.GetID(); }
+ }
+
public bool IsPaused {
get { return !IsRunning; }
}
diff --git a/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs b/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs
index 762743bf1a..a1e0e5208a 100755
--- a/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs
+++ b/src/Main/Base/Project/Src/Gui/Pads/AbstractConsolePad.cs
@@ -1,19 +1,21 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
-using ICSharpCode.AvalonEdit;
-using ICSharpCode.Core.Presentation;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
+
+using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Core;
+using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
@@ -303,6 +305,21 @@ namespace ICSharpCode.SharpDevelop.Gui
}
}
+ public Encoding Encoding {
+ get {
+ return this.editor.Encoding;
+ }
+ set {
+ this.editor.Encoding = value;
+ }
+ }
+
+ public void SelectText(int line, int column, int length)
+ {
+ int offset = this.editor.Document.GetOffset(new TextLocation(line, column));
+ this.editor.Select(offset, length);
+ }
+
public void SetHighlighting(string language)
{
editor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition(language);
@@ -338,6 +355,11 @@ namespace ICSharpCode.SharpDevelop.Gui
this.editor.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
}
+ public void JumpToLine(int line)
+ {
+ this.editor.ScrollToLine(line);
+ }
+
public int CommandOffset {
get { return readOnlyRegion.EndOffset; }
}
@@ -369,6 +391,11 @@ namespace ICSharpCode.SharpDevelop.Gui
if (handler != null)
handler(this, e);
}
+
+ public void Clear()
+ {
+ editor.Clear();
+ }
}
public class BeginReadOnlySectionProvider : IReadOnlySectionProvider
diff --git a/src/Main/StartUp/Project/Resources/BitmapResources.resources b/src/Main/StartUp/Project/Resources/BitmapResources.resources
index 31f13c839a..929a830270 100644
Binary files a/src/Main/StartUp/Project/Resources/BitmapResources.resources and b/src/Main/StartUp/Project/Resources/BitmapResources.resources differ