Commits

spencercw committed 42a458b

Implement viewing the memory in the debugger.

Comments (0)

Files changed (26)

GbDebuggerMsvs/Engine.cpp

 		CProgram *program = NULL;
 		if (rgpProgramNodes[i] != NULL &&
 			rgpProgramNodes[i]->QueryInterface(IID_GbDebuggerMsvsProgram,
-			reinterpret_cast<void**>(&program)) == S_OK)
+			reinterpret_cast<void **>(&program)) == S_OK)
 		{
 			program->EngineAttach(this, rgpPrograms[i], pCallback, dwReason);
 			program->Release();

GbDebuggerMsvs/Expression.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {ADB96366-7866-48E2-B733-03F799833F55} = s 'Expression Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/ExpressionContext.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {12B96D13-E1AF-472D-94AD-9F7D6EEC477D} = s 'ExpressionContext Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/GbDebuggerMsvs.idl

 	{
 		[default] interface IDebugProperty2;
 	};
+	[
+		uuid(12B96D13-E1AF-472D-94AD-9F7D6EEC477D)		
+	]
+	coclass ExpressionContext
+	{
+		[default] interface IDebugExpressionContext2;
+	};
+	[
+		uuid(ADB96366-7866-48E2-B733-03F799833F55)		
+	]
+	coclass Expression
+	{
+		[default] interface IDebugExpression2;
+	};
+	[
+		uuid(F1FDC21F-9741-4A02-9F08-1D5D239508D6)		
+	]
+	coclass MemoryContext
+	{
+		[default] interface IDebugMemoryContext2;
+	};
+	[
+		uuid(9D16918B-D5EB-46CC-BCC7-4E500F202E86)		
+	]
+	coclass MemoryBytes
+	{
+		[default] interface IDebugMemoryBytes2;
+	};
 };

GbDebuggerMsvs/GbDebuggerMsvs.rc

Binary file modified.

GbDebuggerMsvs/GbDebuggerMsvs.vcxproj

     <ClCompile Include="context.cpp" />
     <ClCompile Include="Engine.cpp" />
     <ClCompile Include="events.cpp" />
+    <ClCompile Include="expression.cpp" />
+    <ClCompile Include="expression_context.cpp" />
     <ClCompile Include="GbDebuggerMsvs.cpp" />
     <ClCompile Include="GbDebuggerMsvs_i.c">
       <CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
       </PrecompiledHeader>
     </ClCompile>
+    <ClCompile Include="memory_bytes.cpp" />
+    <ClCompile Include="memory_context.cpp" />
     <ClCompile Include="Program.cpp" />
     <ClCompile Include="ProgramProvider.cpp" />
     <ClCompile Include="property.cpp" />
     <ClInclude Include="context.h" />
     <ClInclude Include="Engine.h" />
     <ClInclude Include="events.h" />
+    <ClInclude Include="expression.h" />
+    <ClInclude Include="expression_context.h" />
     <ClInclude Include="GbDebuggerMsvs.h" />
     <ClInclude Include="GbDebuggerMsvs_i.h" />
+    <ClInclude Include="memory_bytes.h" />
+    <ClInclude Include="memory_context.h" />
     <ClInclude Include="Program.h" />
     <ClInclude Include="ProgramProvider.h" />
     <ClInclude Include="property.h" />
     <None Include="DebugProperty.rgs" />
     <None Include="Engine.rgs" />
     <None Include="EnumFrameInfo.rgs" />
+    <None Include="Expression.rgs" />
+    <None Include="ExpressionContext.rgs" />
     <None Include="GbDebuggerMsvs.rgs" />
     <None Include="GbVsStackFrame.rgs" />
+    <None Include="MemoryBytes.rgs" />
+    <None Include="MemoryContext.rgs" />
     <None Include="Program.rgs" />
     <None Include="ProgramProvider.rgs" />
   </ItemGroup>

GbDebuggerMsvs/GbDebuggerMsvs.vcxproj.filters

     <ClCompile Include="property.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="expression_context.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="expression.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="memory_context.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="memory_bytes.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="stdafx.h">
     <ClInclude Include="property.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="expression_context.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="expression.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="memory_context.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="memory_bytes.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="GbDebuggerMsvs.rc">
     <None Include="DebugProperty.rgs">
       <Filter>Resource Files</Filter>
     </None>
+    <None Include="ExpressionContext.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
+    <None Include="Expression.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
+    <None Include="MemoryContext.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
+    <None Include="MemoryBytes.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Midl Include="GbDebuggerMsvs.idl">

GbDebuggerMsvs/MemoryBytes.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {9D16918B-D5EB-46CC-BCC7-4E500F202E86} = s 'MemoryBytes Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/MemoryContext.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {F1FDC21F-9741-4A02-9F08-1D5D239508D6} = s 'MemoryContext Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/Program.cpp

 	return E_NOTIMPL;
 }
 
-HRESULT CProgram::GetMemoryBytes(IDebugMemoryBytes2 ** /*ppMemoryBytes*/)
+HRESULT CProgram::GetMemoryBytes(IDebugMemoryBytes2 **pMemoryBytes)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CProgram::GetMemoryBytes\n";
 	f.close();
 
-	return E_NOTIMPL;
+	*pMemoryBytes = memoryBytes_;
+	(*pMemoryBytes)->AddRef();
+	return S_OK;
 }
 
 HRESULT CProgram::GetDisassemblyStream(
 
 // CProgram methods
 
+HRESULT CProgram::FinalConstruct()
+{
+	CComObject<CMemoryBytes> *memoryBytes;
+	CComObject<CMemoryBytes>::CreateInstance(&memoryBytes);
+	memoryBytes_ = memoryBytes;
+
+	return S_OK;
+}
+
 void CProgram::EngineAttach(IDebugEngine2 *pEngine, IDebugProgram2 *pMDMProgram,
 	IDebugEventCallback2 *pCallback, DWORD)
 {

GbDebuggerMsvs/Program.h

 #include <msdbg.h>
 
 #include "GbDebuggerMsvs_i.h"
+#include "memory_bytes.h"
 
 #if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
 #error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
 
 	DECLARE_PROTECT_FINAL_CONSTRUCT()
 
-	HRESULT FinalConstruct()
-	{
-		return S_OK;
-	}
+	HRESULT FinalConstruct();
 
 	void FinalRelease()
 	{
 	CComPtr<IDebugEngine2> engine_;
 	GUID progId_;
 	CComPtr<IDebugEventCallback2> callback_;
+	CComPtr<CMemoryBytes> memoryBytes_;
 };
 
 OBJECT_ENTRY_AUTO(__uuidof(Program), CProgram)

GbDebuggerMsvs/expression.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "stdafx.h"
+
+#include "expression.h"
+
+#include <fstream>
+
+#include "property.h"
+
+using std::wstring;
+
+// IDebugExpression2 methods
+
+HRESULT CExpression::EvaluateAsync(EVALFLAGS dwFlags, IDebugEventCallback2 *pExprCallback)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CExpression::EvaluateAsync\n";
+	f.close();
+
+	(void) dwFlags;
+	(void) pExprCallback;
+
+	return E_NOTIMPL;
+}
+
+HRESULT CExpression::Abort()
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CExpression::Abort\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CExpression::EvaluateSync(
+	EVALFLAGS,
+	DWORD,
+	IDebugEventCallback2 *,
+	IDebugProperty2 **ppResult)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CExpression::EvaluateSync\n";
+	f.close();
+
+	if (val_ > 0xffff)
+	{
+		*ppResult = NULL;
+		return E_FAIL;
+	}
+
+	CComObject<CMemoryContext> *memoryContext;
+	CComObject<CMemoryContext>::CreateInstance(&memoryContext);
+	memoryContext->AddRef();
+	memoryContext->Init(static_cast<uint16_t>(val_));
+
+	CComObject<CDebugProperty> *debugProperty;
+	CComObject<CDebugProperty>::CreateInstance(&debugProperty);
+	debugProperty->AddRef();
+	debugProperty->Init(wstring(), wstring(), wstring(), wstring(),
+		DBG_ATTRIB_VALUE_READONLY | DBG_ATTRIB_ACCESS_NONE | DBG_ATTRIB_STORAGE_NONE |
+		DBG_ATTRIB_TYPE_NONE, memoryContext);
+
+	*ppResult = debugProperty;
+	memoryContext->Release();
+	return S_OK;
+}
+
+// CExpression methods
+
+void CExpression::Init(uint64_t val)
+{
+	val_ = val;
+}

GbDebuggerMsvs/expression.h

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "GbDebuggerMsvs_i.h"
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+#endif
+
+using namespace ATL;
+
+class ATL_NO_VTABLE CExpression :
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CExpression, &CLSID_Expression>,
+	public IDebugExpression2
+{
+public:
+	CExpression()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_EXPRESSION)
+
+BEGIN_COM_MAP(CExpression)
+	COM_INTERFACE_ENTRY(IDebugExpression2)
+END_COM_MAP()
+
+	// IDebugExpression2 methods
+	STDMETHOD(EvaluateAsync)(EVALFLAGS dwFlags, IDebugEventCallback2 *pExprCallback);
+	STDMETHOD(Abort)();
+	STDMETHOD(EvaluateSync)(
+		EVALFLAGS dwFlags,
+		DWORD dwTimeout,
+		IDebugEventCallback2 *pExprCallback,
+		IDebugProperty2 **ppResult);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease()
+	{
+	}
+
+public:
+	void Init(uint64_t val);
+
+protected:
+	uint64_t val_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(Expression), CExpression)

GbDebuggerMsvs/expression_context.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "stdafx.h"
+
+#include "expression_context.h"
+
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include "expression.h"
+
+using std::hex;
+using std::wistringstream;
+using std::wstring;
+
+// IDebugExpressionContext2 methods
+
+HRESULT CExpressionContext::GetName(BSTR * /*pbstrName*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CExpressionContext::GetName\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CExpressionContext::ParseText(
+	const OLECHAR *pszCode,
+	PARSEFLAGS dwFlags,
+	UINT nRadix,
+	IDebugExpression2 **ppExpr,
+	BSTR *pbstrError,
+	UINT *pichError)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CExpressionContext::ParseText " << nRadix << "\n";
+	f.close();
+
+	*ppExpr = NULL;
+	*pbstrError = NULL;
+	*pichError = 0;
+
+	// Check the arguments
+	if (!pszCode || !ppExpr || !pbstrError || !pichError)
+	{
+		return E_POINTER;
+	}
+	if (nRadix != 10)
+	{
+		return E_INVALIDARG;
+	}
+	if (dwFlags != PARSE_EXPRESSION)
+	{
+		return E_NOTIMPL;
+	}
+
+	wstring str(pszCode);
+	bool isHex = false;
+	if (str.substr(0, 2) == L"0x")
+	{
+		isHex = true;
+		str = str.substr(2);
+	}
+
+	const wchar_t *validChars;
+	if (isHex)
+	{
+		validChars = L"0123456789abcdefABCDEF";
+	}
+	else
+	{
+		validChars = L"0123456789";
+	}
+	size_t index = str.find_first_not_of(validChars);
+	if (index != wstring::npos)
+	{
+		*pbstrError = SysAllocString(L"Expression contains invalid characters.");
+		*pichError = static_cast<UINT>(index);
+		return E_INVALIDARG;
+	}
+
+	// Parse the string
+	wistringstream iss(str);
+	if (isHex)
+	{
+		iss >> hex;
+	}
+
+	uint64_t val;
+	iss >> val;
+	if (!iss)
+	{
+		*pbstrError = SysAllocString(L"Failed to parse expression.");
+		return E_INVALIDARG;
+	}
+
+	CComObject<CExpression> *expression;
+	CComObject<CExpression>::CreateInstance(&expression);
+	expression->AddRef();
+	expression->Init(val);
+
+	*ppExpr = expression;
+	return S_OK;
+}

GbDebuggerMsvs/expression_context.h

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "GbDebuggerMsvs_i.h"
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+#endif
+
+using namespace ATL;
+
+class ATL_NO_VTABLE CExpressionContext :
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CExpressionContext, &CLSID_ExpressionContext>,
+	public IDebugExpressionContext2
+{
+public:
+	CExpressionContext()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_EXPRESSIONCONTEXT)
+
+BEGIN_COM_MAP(CExpressionContext)
+	COM_INTERFACE_ENTRY(IDebugExpressionContext2)
+END_COM_MAP()
+
+	// IDebugExpressionContext2 methods
+	STDMETHOD(GetName)(BSTR *pbstrName);
+	STDMETHOD(ParseText)(
+		const OLECHAR *pszCode,
+		PARSEFLAGS dwFlags,
+		UINT nRadix,
+		IDebugExpression2 **ppExpr,
+		BSTR *pbstrError,
+		UINT *pichError);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease()
+	{
+	}
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(ExpressionContext), CExpressionContext)

GbDebuggerMsvs/memory_bytes.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "stdafx.h"
+
+#include "memory_bytes.h"
+
+#include <fstream>
+#include <iomanip>
+
+#include "memory_context.h"
+
+// IDebugMemoryBytes2 methods
+
+HRESULT CMemoryBytes::ReadAt(
+	IDebugMemoryContext2 *pStartContext,
+	DWORD dwCount,
+	BYTE *rgbMemory,
+	DWORD *pdwRead,
+	DWORD *pdwUnreadable)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryBytes::ReadAt\n";
+	f.close();
+
+	if (!pStartContext || !rgbMemory || !pdwRead)
+	{
+		return E_POINTER;
+	}
+
+	if (dwCount > 0xffff)
+	{
+		return E_INVALIDARG;
+	}
+
+	CMemoryContext *memoryContext = NULL;
+	HRESULT hr = pStartContext->QueryInterface(IID_GbDebuggerMsvsMemoryContext,
+		reinterpret_cast<void **>(&memoryContext));
+	if (!SUCCEEDED(hr))
+	{
+		return E_INVALIDARG;
+	}
+
+	gbd->memRead(memoryContext->GetAddress(), static_cast<uint16_t>(dwCount), rgbMemory);
+	*pdwRead = dwCount;
+	*pdwUnreadable = 0;	
+
+	return S_OK;
+}
+
+HRESULT CMemoryBytes::WriteAt(
+	IDebugMemoryContext2 *pStartContext,
+	DWORD dwCount,
+	BYTE *rgbMemory)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryBytes::WriteAt\n";
+	f.close();
+
+	(void) pStartContext;
+	(void) dwCount;
+	(void) rgbMemory;
+
+	return E_NOTIMPL;
+}
+
+HRESULT CMemoryBytes::GetSize(UINT64 *pqwSize)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryBytes::GetSize\n";
+	f.close();
+
+	(void) pqwSize;
+
+	return E_NOTIMPL;
+}

GbDebuggerMsvs/memory_bytes.h

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "GbDebuggerMsvs_i.h"
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+#endif
+
+using namespace ATL;
+
+class ATL_NO_VTABLE CMemoryBytes :
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CMemoryBytes, &CLSID_MemoryBytes>,
+	public IDebugMemoryBytes2
+{
+public:
+	CMemoryBytes()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_MEMORYBYTES)
+
+BEGIN_COM_MAP(CMemoryBytes)
+	COM_INTERFACE_ENTRY(IDebugMemoryBytes2)
+END_COM_MAP()
+
+	// IDebugMemoryBytes2 methods
+	STDMETHOD(ReadAt)(
+		IDebugMemoryContext2 *pStartContext,
+		DWORD dwCount,
+		BYTE *rgbMemory,
+		DWORD *pdwRead,
+		DWORD *pdwUnreadable);
+	STDMETHOD(WriteAt)(
+		IDebugMemoryContext2 *pStartContext,
+		DWORD dwCount,
+		BYTE *rgbMemory);
+	STDMETHOD(GetSize)(UINT64 *pqwSize);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease()
+	{
+	}
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(MemoryBytes), CMemoryBytes)

GbDebuggerMsvs/memory_context.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "stdafx.h"
+
+#include "memory_context.h"
+
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+
+using std::hex;
+using std::setfill;
+using std::setw;
+using std::uppercase;
+using std::wostringstream;
+using std::wstring;
+
+// IDebugMemoryContext2 methods
+
+HRESULT CMemoryContext::GetName(BSTR *pbstrName)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryContext::GetName\n";
+	f.close();
+
+	(void) pbstrName;
+
+	return E_NOTIMPL;
+}
+
+HRESULT CMemoryContext::GetInfo(CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO *pInfo)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryContext::GetInfo " << hex << dwFields << "\n";
+	f.close();
+
+	memset(pInfo, 0, sizeof(*pInfo));
+
+	wostringstream oss;
+	oss << hex << uppercase << setfill(L'0') << L"0x" << setw(8) << static_cast<unsigned>(address_);
+	wstring address = oss.str();
+
+	if (dwFields & CIF_ADDRESS)
+	{
+		pInfo->dwFields |= CIF_ADDRESS;
+		pInfo->bstrAddress = SysAllocString(address.c_str());
+	}
+
+	return S_OK;
+}
+
+HRESULT CMemoryContext::Add(UINT64 dwCount, IDebugMemoryContext2 **ppMemCxt)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryContext::Add " << std::hex << std::setfill('0') << std::setw(4) << address_ << " " << std::dec << dwCount << "\n";
+	f.close();
+
+	CComObject<CMemoryContext> *context;
+	CComObject<CMemoryContext>::CreateInstance(&context);
+	context->AddRef();
+	context->Init(static_cast<uint16_t>(address_ + dwCount));
+
+	*ppMemCxt = context;
+	return S_OK;
+}
+
+HRESULT CMemoryContext::Subtract(UINT64 dwCount, IDebugMemoryContext2 **ppMemCxt)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryContext::Subtract " << std::hex << std::setfill('0') << std::setw(4) << address_ << " " << std::dec << dwCount << "\n";
+	f.close();
+
+	CComObject<CMemoryContext> *context;
+	CComObject<CMemoryContext>::CreateInstance(&context);
+	context->AddRef();
+	context->Init(static_cast<uint16_t>(address_ - dwCount));
+
+	*ppMemCxt = context;
+	return S_OK;
+}
+
+HRESULT CMemoryContext::Compare(
+	CONTEXT_COMPARE compare,
+	IDebugMemoryContext2 **rgpMemoryContextSet,
+	DWORD dwMemoryContextSetLen,
+	DWORD *pdwMemoryContext)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CMemoryContext::Compare\n";
+	f.close();
+
+	(void) compare;
+	(void) rgpMemoryContextSet;
+	(void) dwMemoryContextSetLen;
+	(void) pdwMemoryContext;
+
+	return E_NOTIMPL;
+}
+
+// CMemoryContext methods
+
+void CMemoryContext::Init(uint16_t address)
+{
+	address_ = address;
+}
+
+uint16_t CMemoryContext::GetAddress() const
+{
+	return address_;
+}

GbDebuggerMsvs/memory_context.h

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "GbDebuggerMsvs_i.h"
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+#endif
+
+using namespace ATL;
+
+// {6B6C0D8A-CCFA-4E1D-BE99-9D483A911671}
+extern const __declspec(selectany) GUID IID_GbDebuggerMsvsMemoryContext =
+	{ 0x6b6c0d8a, 0xccfa, 0x4e1d, { 0xbe, 0x99, 0x9d, 0x48, 0x3a, 0x91, 0x16, 0x71 } };
+
+class ATL_NO_VTABLE CMemoryContext :
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CMemoryContext, &CLSID_MemoryContext>,
+	public IDebugMemoryContext2
+{
+public:
+	CMemoryContext()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_MEMORYCONTEXT)
+
+BEGIN_COM_MAP(CMemoryContext)
+	COM_INTERFACE_ENTRY(IDebugMemoryContext2)
+	COM_INTERFACE_ENTRY_IID(IID_GbDebuggerMsvsMemoryContext, CMemoryContext)
+END_COM_MAP()
+
+	// IDebugMemoryContext2 methods
+	STDMETHOD(GetName)(BSTR *pbstrName);
+	STDMETHOD(GetInfo)(CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO *pInfo);
+	STDMETHOD(Add)(UINT64 dwCount, IDebugMemoryContext2 **ppMemCxt);
+	STDMETHOD(Subtract)(UINT64 dwCount, IDebugMemoryContext2 **ppMemCxt);
+	STDMETHOD(Compare)(
+		CONTEXT_COMPARE compare,
+		IDebugMemoryContext2 **rgpMemoryContextSet,
+		DWORD dwMemoryContextSetLen,
+		DWORD *pdwMemoryContext);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease()
+	{
+	}
+
+public:
+	void Init(uint16_t address);
+	uint16_t GetAddress() const;
+
+protected:
+	uint16_t address_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(MemoryContext), CMemoryContext)

GbDebuggerMsvs/property.cpp

 	DEBUG_PROPERTY_INFO *pPropertyInfo)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
-	f << "CDebugProperty::GetPropertyInfo\n";
+	f << "CDebugProperty::GetPropertyInfo " << dwFields << " " << nRadix << "\n";
 	f.close();
 
 	PopulatePropertyInfo(pPropertyInfo, dwFields, nRadix);
 	return E_NOTIMPL;
 }
 
-HRESULT CDebugProperty::GetMemoryContext(IDebugMemoryContext2 ** /*ppMemory*/)
+HRESULT CDebugProperty::GetMemoryContext(IDebugMemoryContext2 **ppMemory)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CDebugProperty::GetMemoryContext\n";
 	f.close();
 
-	return E_NOTIMPL;
+	if (!memoryContext_)
+	{
+		*ppMemory = NULL;
+		return S_GETMEMORYCONTEXT_NO_MEMORY_CONTEXT;
+	}
+	else
+	{
+		*ppMemory = memoryContext_;
+		(*ppMemory)->AddRef();
+		return S_OK;
+	}
 }
 
 HRESULT CDebugProperty::GetSize(DWORD * /*pdwSize*/)
 // CDebugProperty methods
 
 void CDebugProperty::Init(const wstring &fullName, const wstring &name, const wstring &type,
-	const wstring &value, DBG_ATTRIB_FLAGS attrib)
+	const wstring &value, DBG_ATTRIB_FLAGS attrib, CComPtr<CMemoryContext> memoryContext)
 {
 	fullName_ = fullName;
 	name_ = name;
 	type_ = type;
 	value_ = value;
 	attrib_ = attrib;
+	memoryContext_ = memoryContext;
 }
 
 void CDebugProperty::AddChild(CComPtr<CDebugProperty> child)
 	}
 	if (fields & DEBUGPROP_INFO_PROP)
 	{
-		propertyInfo->dwFields |= DEBUGPROP_INFO_ATTRIB;
+		propertyInfo->dwFields |= DEBUGPROP_INFO_PROP;
 		propertyInfo->pProperty = this;
 		propertyInfo->pProperty->AddRef();
 	}

GbDebuggerMsvs/property.h

 #include <atlcom.h>
 
 #include "GbDebuggerMsvs_i.h"
+#include "memory_context.h"
 
 #if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
 #error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
 
 public:
 	void Init(const std::wstring &fullName, const std::wstring &name, const std::wstring &type,
-		const std::wstring &value, DBG_ATTRIB_FLAGS attrib);
+		const std::wstring &value, DBG_ATTRIB_FLAGS attrib, CComPtr<CMemoryContext> = NULL);
 	void AddChild(CComPtr<CDebugProperty> child);
 
 	void PopulatePropertyInfo(DEBUG_PROPERTY_INFO *propertyInfo, DEBUGPROP_INFO_FLAGS fields,
 	std::wstring value_;
 	DBG_ATTRIB_FLAGS attrib_;
 
+	CComPtr<CMemoryContext> memoryContext_;
 	CComPtr<CDebugProperty> parent_;
 	std::vector<CComPtr<CDebugProperty> > children_;
 };

GbDebuggerMsvs/resource.h

Binary file modified.

GbDebuggerMsvs/stack_frame.cpp

 #include <sstream>
 
 #include "com_enum.h"
+#include "expression_context.h"
 #include "property.h"
 #include "stack_frame.h"
 
 	return E_NOTIMPL;
 }
 
-HRESULT CGbVsStackFrame::GetExpressionContext(IDebugExpressionContext2 ** /*ppExprCxt*/)
+HRESULT CGbVsStackFrame::GetExpressionContext(IDebugExpressionContext2 **ppExprCxt)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CGbVsStackFrame::GetExpressionContext\n";
 	f.close();
 
-	return E_NOTIMPL;
+	CComObject<CExpressionContext> *context;
+	CComObject<CExpressionContext>::CreateInstance(&context);
+	context->AddRef();
+	*ppExprCxt = context;
+	return S_OK;
 }
 
 HRESULT CGbVsStackFrame::GetLanguageInfo(BSTR * /*pbstrLanguage*/, GUID * /*pguidLanguage*/)

gb_emulator/include/gb_emulator/gb_debugger.h

 	boost::mutex name##Mutex_; \
 	boost::condition_variable name##Cv_;
 
+#define DECLARE_WORKING_VARS_NO_RET(name) \
+	bool name##Ready_; \
+	boost::mutex name##Mutex_; \
+	boost::condition_variable name##Cv_;
+
 class Gb;
 
 struct GB_EMULATOR_API GbStackFrame
 	std::vector<GbStackFrame> stackFrames();
 	GbRegisters registers();
 
+	void memRead(uint16_t address, uint16_t len, uint8_t *data);
+
 private:
 	Gb &gb_;
 	boost::asio::io_service ioService_;
 	DECLARE_WORKING_VARS(boost::filesystem::path, modulePath)
 	DECLARE_WORKING_VARS(std::vector<GbStackFrame>, stackFrames)
 	DECLARE_WORKING_VARS(GbRegisters, registers)
+	
+	DECLARE_WORKING_VARS_NO_RET(memRead)
 
 	GbDebugger(Gb &gb);
 	~GbDebugger();
 	void doStackFrames();
 	void doRegisters();
 
+	void doMemRead(uint16_t address, uint16_t len, uint8_t *data);
+
 	boost::filesystem::path getModulePath();
 
 	// Disabled operations

gb_emulator/src/gb_debugger.cpp

 using std::set;
 using std::vector;
 
+// Comma for macros..
+#define CO ,
+
 #define DEFINE_WRAPPER(type, name, bigName) \
 	type GbDebugger::name() \
 	{ \
 		return name##_; \
 	}
 
+#define DEFINE_WRAPPER_ARGS_NO_RET(name, bigName, args, postArgs) \
+	void GbDebugger::name(args) \
+	{ \
+		unique_lock<mutex> lock(name##Mutex_); \
+		name##Ready_ = false; \
+		ioService_.post(boost::bind(&GbDebugger::do##bigName, this, postArgs)); \
+		while (!name##Ready_) \
+		{ \
+			name##Cv_.wait(lock); \
+		} \
+	}
+
 #define NOTIFY(name) \
 	lock_guard<mutex> lock(name##Mutex_); \
 	name##Ready_ = true; \
 	name##_ = name; \
 	name##Cv_.notify_one();
 
+#define NOTIFY_NO_RET(name) \
+	lock_guard<mutex> lock(name##Mutex_); \
+	name##Ready_ = true; \
+	name##Cv_.notify_one();
+
 GbDebugger::GbDebugger(Gb &gb):
 gb_(gb),
 work_(ioService_)
 DEFINE_WRAPPER(vector<GbStackFrame>, stackFrames, StackFrames)
 DEFINE_WRAPPER(GbRegisters, registers, Registers)
 
+DEFINE_WRAPPER_ARGS_NO_RET(memRead, MemRead,
+	uint16_t address CO uint16_t len CO uint8_t *data, address CO len CO data)
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Implementation functions
 
 	NOTIFY(registers)
 }
 
+void GbDebugger::doMemRead(uint16_t address, uint16_t len, uint8_t *data)
+{
+	for (uint16_t i = 0; i != len; ++i, ++address)
+	{
+		data[i] = gb_.mem_.read(address);
+	}
+	NOTIFY_NO_RET(memRead)
+}
+
 fs::path GbDebugger::getModulePath()
 {
 	if (gb_.mem_.ioPorts[BLCK] && !gb_.romFilename_.empty())

gb_emulator/src/gb_memory.cpp

 	else if (ptr >= VIDEO_RAM)
 		return vram[ptr - VIDEO_RAM + VRAM_BANK_SIZE * vramBank];
 	else if (ptr >= ROM_BANKX)
-		return gb_.rom_[ptr - ROM_BANKX + ROM_BANK_SIZE * romBank];
+	{
+		uint16_t addr = ptr - ROM_BANKX + ROM_BANK_SIZE * romBank;
+		if (addr < gb_.rom_.length())
+		{
+			return gb_.rom_[addr];
+		}
+		else
+		{
+			return 0;
+		}
+	}
 	else
 	{
 		if (!ioPorts[BLCK] && (ptr < BIOS1_END || (ptr >= BIOS2_START && ptr < BIOS2_END)))