Commits

spencercw committed 84e8fe6

Implement display of register values in the debugger.

Comments (0)

Files changed (21)

GbDebuggerMsvs/DebugContext.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {9E6F8AED-8DFE-451C-AED1-6CB7B68A0FD3} = s 'DebugContext Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/DebugProperty.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {AD597949-4691-4D50-A20F-7ED82F6B8005} = s 'DebugProperty Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/Engine.cpp

 	return E_NOTIMPL;
 }
 
-HRESULT CEngine::RemoveAllSetExceptions(REFGUID /*guidType*/)
+HRESULT CEngine::RemoveAllSetExceptions(const GUID & /*guidType*/)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CEngine::RemoveAllSetExceptions\n";

GbDebuggerMsvs/Engine.h

 		IDebugPendingBreakpoint2 **ppPendingBP);
 	STDMETHOD(SetException)(EXCEPTION_INFO *pException);
 	STDMETHOD(RemoveSetException)(EXCEPTION_INFO *pException);
-	STDMETHOD(RemoveAllSetExceptions)(REFGUID guidType);
+	STDMETHOD(RemoveAllSetExceptions)(const GUID &guidType);
 	STDMETHOD(GetEngineId)(GUID *pguidEngine);
 	STDMETHOD(DestroyProgram)(IDebugProgram2 *pProgram);
 	STDMETHOD(ContinueFromSynchronousEvent)(IDebugEvent2 *pEvent);

GbDebuggerMsvs/GbDebuggerMsvs.idl

 	{
 		[default] interface IDebugCodeContext2;
 	};
+	[
+		uuid(AD597949-4691-4D50-A20F-7ED82F6B8005)		
+	]
+	coclass DebugProperty
+	{
+		[default] interface IDebugProperty2;
+	};
 };

GbDebuggerMsvs/GbDebuggerMsvs.rc

Binary file modified.

GbDebuggerMsvs/GbDebuggerMsvs.vcxproj

     </ClCompile>
     <ClCompile Include="Program.cpp" />
     <ClCompile Include="ProgramProvider.cpp" />
+    <ClCompile Include="property.cpp" />
     <ClCompile Include="stack_frame.cpp" />
     <ClCompile Include="stdafx.cpp">
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="com_enum_with_count.h" />
+    <ClInclude Include="com_enum.h" />
     <ClInclude Include="context.h" />
     <ClInclude Include="Engine.h" />
     <ClInclude Include="events.h" />
     <ClInclude Include="GbDebuggerMsvs_i.h" />
     <ClInclude Include="Program.h" />
     <ClInclude Include="ProgramProvider.h" />
+    <ClInclude Include="property.h" />
     <ClInclude Include="Resource.h" />
     <ClInclude Include="stack_frame.h" />
     <ClInclude Include="stdafx.h" />
   </ItemGroup>
   <ItemGroup>
     <None Include="DebugContext.rgs" />
+    <None Include="DebugProperty.rgs" />
     <None Include="Engine.rgs" />
     <None Include="EnumFrameInfo.rgs" />
     <None Include="GbDebuggerMsvs.rgs" />

GbDebuggerMsvs/GbDebuggerMsvs.vcxproj.filters

     <ClCompile Include="context.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="property.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="stdafx.h">
     <ClInclude Include="events.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="com_enum_with_count.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
     <ClInclude Include="stack_frame.h">
       <Filter>Header Files</Filter>
     </ClInclude>
     <ClInclude Include="context.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="com_enum.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="property.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="GbDebuggerMsvs.rc">
     <None Include="DebugContext.rgs">
       <Filter>Resource Files</Filter>
     </None>
+    <None Include="DebugProperty.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Midl Include="GbDebuggerMsvs.idl">

GbDebuggerMsvs/ProgramProvider.cpp

 }
 
 HRESULT CProgramProvider::GetProviderProgramNode(PROVIDER_FLAGS /*Flags*/,
-	IDebugDefaultPort2 * /*pPort*/, AD_PROCESS_ID /*processId*/, REFGUID /*guidEngine*/,
+	IDebugDefaultPort2 * /*pPort*/, AD_PROCESS_ID /*processId*/, const GUID & /*guidEngine*/,
 	UINT64 /*programId*/, IDebugProgramNode2 ** /*ppProgramNode*/)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 
 HRESULT CProgramProvider::WatchForProviderEvents(PROVIDER_FLAGS /*Flags*/,
 	IDebugDefaultPort2 * /*pPort*/, AD_PROCESS_ID /*processId*/, CONST_GUID_ARRAY /*EngineFilter*/,
-	REFGUID /*guidLaunchingEngine*/, IDebugPortNotify2 * /*pEventCallback*/)
+	const GUID & /*guidLaunchingEngine*/, IDebugPortNotify2 * /*pEventCallback*/)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CProgramProvider::WatchForProviderEvents\n";

GbDebuggerMsvs/ProgramProvider.h

 END_COM_MAP()
 
 	// IDebugProgramProvider2
-	STDMETHOD(GetProviderProcessData)(PROVIDER_FLAGS Flags, IDebugDefaultPort2 *pPort,
-		AD_PROCESS_ID processId, CONST_GUID_ARRAY EngineFilter, PROVIDER_PROCESS_DATA *pProcess);
-	STDMETHOD(GetProviderProgramNode)(PROVIDER_FLAGS Flags, IDebugDefaultPort2 *pPort,
-		AD_PROCESS_ID processId, REFGUID guidEngine, UINT64 programId,
+	STDMETHOD(GetProviderProcessData)(
+		PROVIDER_FLAGS Flags,
+		IDebugDefaultPort2 *pPort,
+		AD_PROCESS_ID processId,
+		CONST_GUID_ARRAY EngineFilter,
+		PROVIDER_PROCESS_DATA *pProcess);
+	STDMETHOD(GetProviderProgramNode)(
+		PROVIDER_FLAGS Flags,
+		IDebugDefaultPort2 *pPort,
+		AD_PROCESS_ID processId,
+		const GUID &guidEngine,
+		UINT64 programId,
 		IDebugProgramNode2 **ppProgramNode);
-	STDMETHOD(WatchForProviderEvents)(PROVIDER_FLAGS Flags, IDebugDefaultPort2 *pPort,
-		AD_PROCESS_ID processId, CONST_GUID_ARRAY EngineFilter, REFGUID guidLaunchingEngine,
+	STDMETHOD(WatchForProviderEvents)(
+		PROVIDER_FLAGS Flags,
+		IDebugDefaultPort2 *pPort,
+		AD_PROCESS_ID processId,
+		CONST_GUID_ARRAY EngineFilter,
+		const GUID & guidLaunchingEngine,
 		IDebugPortNotify2 *pEventCallback);
 	STDMETHOD(SetLocale)(WORD wLangID);
 

GbDebuggerMsvs/com_enum.h

+#pragma once
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+template <class Base, const IID* piid, class T, class Copy>
+class ATL_NO_VTABLE CComEnumWithCountImpl:
+	public Base
+{
+public:
+	CComEnumWithCountImpl()
+	{
+		m_begin = m_end = m_iter = NULL;
+		m_dwFlags = 0;
+		m_pUnk = NULL;
+	}
+
+	~CComEnumWithCountImpl()
+	{
+		if (m_dwFlags & BitOwn)
+		{
+			for (T* p = m_begin; p != m_end; p++)
+				Copy::destroy(p);
+			delete [] m_begin;
+		}
+		if (m_pUnk)
+			m_pUnk->Release();
+	}
+
+	STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched)
+	{
+		if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
+			return E_POINTER;
+		if (m_begin == NULL || m_end == NULL || m_iter == NULL)
+			return E_FAIL;
+		ULONG nRem = (ULONG)(m_end - m_iter);
+		HRESULT hRes = S_OK;
+		if (nRem < celt)
+			hRes = S_FALSE;
+		ULONG nMin = min(celt, nRem);
+		if (pceltFetched != NULL)
+			*pceltFetched = nMin;
+		while(nMin--)
+			Copy::copy(rgelt++, m_iter++);
+		return hRes;
+	}
+
+	STDMETHOD(Skip)(ULONG celt)
+	{
+		m_iter += celt;
+		if (m_iter < m_end)
+			return S_OK;
+		m_iter = m_end;
+		return S_FALSE;
+	}
+
+	STDMETHOD(Reset)(void)
+	{
+		m_iter = m_begin;
+		return S_OK;
+	}
+
+	STDMETHOD(Clone)(Base** ppEnum)
+	{
+		typedef CComObject<CComEnumWithCount<Base, piid, T, Copy> > _class;
+		HRESULT hRes = E_POINTER;
+		if (ppEnum != NULL)
+		{
+			_class* p = NULL;
+			ATLTRY(p = new _class)
+			if (p == NULL)
+			{
+				*ppEnum = NULL;
+				hRes = E_OUTOFMEMORY;
+			}
+			else
+			{
+				// If the data is a copy then we need to keep "this" object around
+				hRes = p->Init(m_begin, m_end, (m_dwFlags & BitCopy) ? this : m_pUnk);
+				if (FAILED(hRes))
+					delete p;
+				else
+				{
+					p->m_iter = m_iter;
+					hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum);
+					if (FAILED(hRes))
+					delete p;
+				}
+			}
+		}
+		return hRes;
+	}
+
+	STDMETHOD(GetCount)(ULONG* pcelt)
+	{
+		*pcelt = (ULONG)(m_end - m_begin);
+		return S_OK;
+	}
+
+	HRESULT Init(T* begin, T* end, IUnknown* pUnk, ATL::CComEnumFlags flags = AtlFlagNoCopy)
+	{
+		if (flags == AtlFlagCopy)
+		{
+			_ASSERTE(m_begin == NULL); //Init called twice?
+			ATLTRY(m_begin = new T[end-begin])
+			m_iter = m_begin;
+			if (m_begin == NULL)
+				return E_OUTOFMEMORY;
+			for (T* i=begin; i != end; i++)
+			{
+				Copy::init(m_iter);
+				Copy::copy(m_iter++, i);
+			}
+			m_end = m_begin + (end-begin);
+		}
+		else
+		{
+			m_begin = begin;
+			m_end = end;
+		}
+		m_pUnk = pUnk;
+		if (m_pUnk)
+			m_pUnk->AddRef();
+		m_iter = m_begin;
+		m_dwFlags = flags;
+		return S_OK;
+	}
+
+public:
+	IUnknown* m_pUnk;
+	T* m_begin;
+	T* m_end;
+	T* m_iter;
+	DWORD m_dwFlags;
+protected:
+	enum FlagBits { BitCopy = 1, BitOwn = 2 };
+};
+
+template <class Base, const IID* piid, class T, class Copy, class ThreadModel = ATL::CComObjectThreadModel>
+class ATL_NO_VTABLE CComEnumWithCount:
+	public CComEnumWithCountImpl<Base, piid, T, Copy>,
+	public ATL::CComObjectRootEx<ThreadModel>
+{
+public:
+	typedef CComEnumWithCount<Base, piid, T, Copy > _CComEnum;
+	typedef CComEnumWithCountImpl<Base, piid, T, Copy > _CComEnumBase;
+
+BEGIN_COM_MAP(_CComEnum)
+	COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
+END_COM_MAP()
+};

GbDebuggerMsvs/com_enum_with_count.h

-#pragma once
-
-#include <atlbase.h>
-
-template <class Base, const IID* piid, class T, class Copy>
-class ATL_NO_VTABLE CComEnumWithCountImpl:
-	public Base
-{
-public:
-	CComEnumWithCountImpl()
-	{
-		m_begin = m_end = m_iter = NULL;
-		m_dwFlags = 0;
-		m_pUnk = NULL;
-	}
-
-	~CComEnumWithCountImpl()
-	{
-		if (m_dwFlags & BitOwn)
-		{
-			for (T* p = m_begin; p != m_end; p++)
-				Copy::destroy(p);
-			delete [] m_begin;
-		}
-		if (m_pUnk)
-			m_pUnk->Release();
-	}
-
-	STDMETHOD(Next)(ULONG celt, T* rgelt, ULONG* pceltFetched)
-	{
-		if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))
-			return E_POINTER;
-		if (m_begin == NULL || m_end == NULL || m_iter == NULL)
-			return E_FAIL;
-		ULONG nRem = (ULONG)(m_end - m_iter);
-		HRESULT hRes = S_OK;
-		if (nRem < celt)
-			hRes = S_FALSE;
-		ULONG nMin = min(celt, nRem);
-		if (pceltFetched != NULL)
-			*pceltFetched = nMin;
-		while(nMin--)
-			Copy::copy(rgelt++, m_iter++);
-		return hRes;
-	}
-
-	STDMETHOD(Skip)(ULONG celt)
-	{
-		m_iter += celt;
-		if (m_iter < m_end)
-			return S_OK;
-		m_iter = m_end;
-		return S_FALSE;
-	}
-
-	STDMETHOD(Reset)(void)
-	{
-		m_iter = m_begin;
-		return S_OK;
-	}
-
-	STDMETHOD(Clone)(Base** ppEnum)
-	{
-		typedef CComObject<CComEnumWithCount<Base, piid, T, Copy> > _class;
-		HRESULT hRes = E_POINTER;
-		if (ppEnum != NULL)
-		{
-			_class* p = NULL;
-			ATLTRY(p = new _class)
-			if (p == NULL)
-			{
-				*ppEnum = NULL;
-				hRes = E_OUTOFMEMORY;
-			}
-			else
-			{
-				// If the data is a copy then we need to keep "this" object around
-				hRes = p->Init(m_begin, m_end, (m_dwFlags & BitCopy) ? this : m_pUnk);
-				if (FAILED(hRes))
-					delete p;
-				else
-				{
-					p->m_iter = m_iter;
-					hRes = p->_InternalQueryInterface(*piid, (void**)ppEnum);
-					if (FAILED(hRes))
-					delete p;
-				}
-			}
-		}
-		return hRes;
-	}
-
-	STDMETHOD(GetCount)(ULONG* pcelt)
-	{
-		*pcelt = (ULONG)(m_end - m_begin);
-		return S_OK;
-	}
-
-	HRESULT Init(T* begin, T* end, IUnknown* pUnk, CComEnumFlags flags = AtlFlagNoCopy)
-	{
-		if (flags == AtlFlagCopy)
-		{
-			_ASSERTE(m_begin == NULL); //Init called twice?
-			ATLTRY(m_begin = new T[end-begin])
-			m_iter = m_begin;
-			if (m_begin == NULL)
-				return E_OUTOFMEMORY;
-			for (T* i=begin; i != end; i++)
-			{
-				Copy::init(m_iter);
-				Copy::copy(m_iter++, i);
-			}
-			m_end = m_begin + (end-begin);
-		}
-		else
-		{
-			m_begin = begin;
-			m_end = end;
-		}
-		m_pUnk = pUnk;
-		if (m_pUnk)
-			m_pUnk->AddRef();
-		m_iter = m_begin;
-		m_dwFlags = flags;
-		return S_OK;
-	}
-
-public:
-	IUnknown* m_pUnk;
-	T* m_begin;
-	T* m_end;
-	T* m_iter;
-	DWORD m_dwFlags;
-protected:
-	enum FlagBits { BitCopy = 1, BitOwn = 2 };
-};
-
-template <class Base, const IID* piid, class T, class Copy, class ThreadModel = CComObjectThreadModel>
-class ATL_NO_VTABLE CComEnumWithCount:
-	public CComEnumWithCountImpl<Base, piid, T, Copy>,
-	public CComObjectRootEx<ThreadModel>
-{
-public:
-	typedef CComEnumWithCount<Base, piid, T, Copy > _CComEnum;
-	typedef CComEnumWithCountImpl<Base, piid, T, Copy > _CComEnumBase;
-
-BEGIN_COM_MAP(_CComEnum)
-	COM_INTERFACE_ENTRY_IID(*piid, _CComEnumBase)
-END_COM_MAP()
-};

GbDebuggerMsvs/events.cpp

 
 #include "events.h"
 
-#include "com_enum_with_count.h"
+#include "com_enum.h"
 
 typedef CComEnumWithCount<
 	IEnumDebugBoundBreakpoints2,

GbDebuggerMsvs/property.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 "property.h"
+
+#include <fstream>
+#include <vector>
+
+#include "com_enum.h"
+
+using std::vector;
+using std::wstring;
+
+// IDebugProperty2 methods
+
+HRESULT CDebugProperty::GetPropertyInfo(
+	DEBUGPROP_INFO_FLAGS dwFields,
+	DWORD nRadix,
+	DWORD,
+	IDebugReference2 **,
+	DWORD,
+	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.close();
+
+	PopulatePropertyInfo(pPropertyInfo, dwFields, nRadix);
+	return S_OK;
+}
+
+HRESULT CDebugProperty::SetValueAsString(
+	const OLECHAR * /*pszValue*/,
+	DWORD /*nRadix*/,
+	DWORD /*dwTimeout*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::SetValueAsString\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugProperty::SetValueAsReference(
+	IDebugReference2 ** /*rgpArgs*/,
+	DWORD /*dwArgCount*/,
+	IDebugReference2 * /*pValue*/,
+	DWORD /*dwTimeout*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::SetValueAsReference\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugProperty::EnumChildren(
+	DEBUGPROP_INFO_FLAGS dwFields,
+	DWORD dwRadix,
+	const GUID &,
+	DBG_ATTRIB_FLAGS,
+	const OLECHAR *,
+	DWORD,
+	IEnumDebugPropertyInfo2 **ppEnum)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::EnumChildren\n";
+	f.close();
+
+	if (children_.empty())
+	{
+		return S_FALSE;
+	}
+
+	// TODO: Handle guidFilter, dwAttribFilter and pszNameFilter parameters
+
+	// Create the property info structures for the children
+	vector<DEBUG_PROPERTY_INFO> childrenPropInfo(children_.size());
+	size_t i = 0;
+	for (vector<CComPtr<CDebugProperty> >::const_iterator child = children_.begin(),
+		end = children_.end(); child != end; ++child, ++i)
+	{
+		(*child)->PopulatePropertyInfo(&childrenPropInfo[i], dwFields, dwRadix);
+	}
+
+	// Create the enumerator
+	typedef CComEnumWithCount<
+		IEnumDebugPropertyInfo2,
+		&IID_IEnumDebugPropertyInfo2,
+		DEBUG_PROPERTY_INFO,
+		CopyDebugPropertyInfo> CEnumDebugPropertyInfo;
+	CComObject<CEnumDebugPropertyInfo> *enumerator;
+	CComObject<CEnumDebugPropertyInfo>::CreateInstance(&enumerator);
+
+	enumerator->Init(&*childrenPropInfo.begin(),
+		&*childrenPropInfo.begin() + childrenPropInfo.size(), NULL, AtlFlagCopy);
+	*ppEnum = enumerator;
+	(*ppEnum)->AddRef();
+
+	// Clean up
+	for (vector<DEBUG_PROPERTY_INFO>::iterator propertyInfo = childrenPropInfo.begin(),
+		end = childrenPropInfo.end(); propertyInfo != end; ++propertyInfo)
+	{
+		CopyDebugPropertyInfo::destroy(&*propertyInfo);
+	}
+
+	return S_OK;
+}
+
+HRESULT CDebugProperty::GetParent(IDebugProperty2 **ppParent)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::GetParent\n";
+	f.close();
+
+	if (parent_)
+	{
+		*ppParent = parent_;
+		(*ppParent)->AddRef();
+		return S_OK;
+	}
+	else
+	{
+		return S_GETPARENT_NO_PARENT;
+	}
+}
+
+HRESULT CDebugProperty::GetDerivedMostProperty(IDebugProperty2 ** /*ppDerivedMost*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::GetDerivedMostProperty\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugProperty::GetMemoryBytes(IDebugMemoryBytes2 ** /*ppMemoryBytes*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::GetMemoryBytes\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+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;
+}
+
+HRESULT CDebugProperty::GetSize(DWORD * /*pdwSize*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::GetSize\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugProperty::GetReference(IDebugReference2 ** /*ppReference*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::GetReference\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugProperty::GetExtendedInfo(const GUID & /*guidExtendedInfo*/, VARIANT * /*pExtendedInfo*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugProperty::GetExtendedInfo\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+// CDebugProperty methods
+
+void CDebugProperty::Init(const wstring &fullName, const wstring &name, const wstring &type,
+	const wstring &value, DBG_ATTRIB_FLAGS attrib)
+{
+	fullName_ = fullName;
+	name_ = name;
+	type_ = type;
+	value_ = value;
+	attrib_ = attrib;
+}
+
+void CDebugProperty::AddChild(CComPtr<CDebugProperty> child)
+{
+	child->parent_ = this;
+	children_.push_back(child);
+}
+
+void CDebugProperty::PopulatePropertyInfo(DEBUG_PROPERTY_INFO *propertyInfo,
+	DEBUGPROP_INFO_FLAGS fields, DWORD)
+{
+	memset(propertyInfo, 0, sizeof(*propertyInfo));
+	
+	if (fields & DEBUGPROP_INFO_FULLNAME)
+	{
+		propertyInfo->dwFields |= DEBUGPROP_INFO_FULLNAME;
+		propertyInfo->bstrFullName = SysAllocString(fullName_.c_str());
+	}
+	if (fields & DEBUGPROP_INFO_NAME)
+	{
+		propertyInfo->dwFields |= DEBUGPROP_INFO_NAME;
+		propertyInfo->bstrName = SysAllocString(name_.c_str());
+	}
+	if (fields & DEBUGPROP_INFO_TYPE)
+	{
+		propertyInfo->dwFields |= DEBUGPROP_INFO_TYPE;
+		propertyInfo->bstrType = SysAllocString(type_.c_str());
+	}
+	if (fields & DEBUGPROP_INFO_VALUE)
+	{
+		propertyInfo->dwFields |= DEBUGPROP_INFO_VALUE;
+		propertyInfo->bstrValue = SysAllocString(value_.c_str());
+	}
+	if (fields & DEBUGPROP_INFO_ATTRIB)
+	{
+		propertyInfo->dwFields |= DEBUGPROP_INFO_ATTRIB;
+		propertyInfo->dwAttrib = attrib_;
+	}
+	if (fields & DEBUGPROP_INFO_PROP)
+	{
+		propertyInfo->dwFields |= DEBUGPROP_INFO_ATTRIB;
+		propertyInfo->pProperty = this;
+		propertyInfo->pProperty->AddRef();
+	}
+}

GbDebuggerMsvs/property.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 <string>
+
+#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 CopyDebugPropertyInfo
+{
+public:
+	static HRESULT copy(DEBUG_PROPERTY_INFO *dest, const DEBUG_PROPERTY_INFO *src)
+	{
+		ATLENSURE(dest != NULL && src != NULL);
+
+		*dest = *src;
+		
+		dest->bstrFullName = SysAllocString(dest->bstrFullName);
+		dest->bstrName     = SysAllocString(dest->bstrName);
+		dest->bstrType     = SysAllocString(dest->bstrType);
+		dest->bstrValue    = SysAllocString(dest->bstrValue);
+
+		if (dest->pProperty)
+			dest->pProperty->AddRef();
+
+		return S_OK;
+	}
+	
+	static void init(DEBUG_PROPERTY_INFO *)
+	{
+	}
+	
+	static void destroy(DEBUG_PROPERTY_INFO *p) 
+	{
+		SysFreeString(p->bstrFullName);
+		SysFreeString(p->bstrName);
+		SysFreeString(p->bstrType);
+		SysFreeString(p->bstrValue);
+
+		if (p->pProperty)
+			p->pProperty->Release();
+	}
+};
+
+class ATL_NO_VTABLE CDebugProperty:
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CDebugProperty, &CLSID_DebugProperty>,
+	public IDebugProperty2
+{
+public:
+	CDebugProperty()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_DEBUGPROPERTY)
+
+BEGIN_COM_MAP(CDebugProperty)
+	COM_INTERFACE_ENTRY(IDebugProperty2)
+END_COM_MAP()
+
+	// IDebugProperty2 methods
+	STDMETHOD(GetPropertyInfo)(
+		DEBUGPROP_INFO_FLAGS dwFields,
+		DWORD nRadix,
+		DWORD dwTimeout,
+		IDebugReference2 **rgpArgs,
+		DWORD dwArgCount,
+		DEBUG_PROPERTY_INFO *pPropertyInfo);
+	STDMETHOD(SetValueAsString)(
+		const OLECHAR *pszValue,
+		DWORD nRadix,
+		DWORD dwTimeout);
+	STDMETHOD(SetValueAsReference)(
+		IDebugReference2 **rgpArgs,
+		DWORD dwArgCount,
+		IDebugReference2 *pValue,
+		DWORD dwTimeout);
+	STDMETHOD(EnumChildren)(
+		DEBUGPROP_INFO_FLAGS dwFields,
+		DWORD dwRadix,
+		const GUID &guidFilter,
+		DBG_ATTRIB_FLAGS dwAttribFilter,
+		const OLECHAR *pszNameFilter,
+		DWORD dwTimeout,
+		IEnumDebugPropertyInfo2 **ppEnum);
+	STDMETHOD(GetParent)(IDebugProperty2 **ppParent);
+	STDMETHOD(GetDerivedMostProperty)(IDebugProperty2 **ppDerivedMost);
+	STDMETHOD(GetMemoryBytes)(IDebugMemoryBytes2 **ppMemoryBytes);
+	STDMETHOD(GetMemoryContext)(IDebugMemoryContext2 **ppMemory);
+	STDMETHOD(GetSize)(DWORD *pdwSize);
+	STDMETHOD(GetReference)(IDebugReference2 **ppReference);
+	STDMETHOD(GetExtendedInfo)(const GUID &guidExtendedInfo, VARIANT *pExtendedInfo);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease()
+	{
+	}
+
+public:
+	void Init(const std::wstring &fullName, const std::wstring &name, const std::wstring &type,
+		const std::wstring &value, DBG_ATTRIB_FLAGS attrib);
+	void AddChild(CComPtr<CDebugProperty> child);
+
+	void PopulatePropertyInfo(DEBUG_PROPERTY_INFO *propertyInfo, DEBUGPROP_INFO_FLAGS fields,
+		DWORD radix);
+
+protected:
+	std::wstring fullName_;
+	std::wstring name_;
+	std::wstring type_;
+	std::wstring value_;
+	DBG_ATTRIB_FLAGS attrib_;
+
+	CComPtr<CDebugProperty> parent_;
+	std::vector<CComPtr<CDebugProperty> > children_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(DebugProperty), CDebugProperty)

GbDebuggerMsvs/resource.h

Binary file modified.

GbDebuggerMsvs/stack_frame.cpp

 
 #include "stdafx.h"
 
+#include <iomanip>
 #include <fstream>
+#include <sstream>
 
+#include "com_enum.h"
+#include "property.h"
 #include "stack_frame.h"
 
+using std::hex;
+using std::setfill;
+using std::setw;
+using std::uppercase;
 using std::vector;
+using std::wostringstream;
+using std::wstring;
 
 // IDebugStackFrame2 methods
 
 }
 
 HRESULT CGbVsStackFrame::EnumProperties(
-	DEBUGPROP_INFO_FLAGS /*dwFields*/,
-	UINT /*nRadix*/,
-	REFGUID /*guidFilter*/,
-	DWORD /*dwTimeout*/,
-	ULONG * /*pcelt*/,
-	IEnumDebugPropertyInfo2 ** /*ppepi*/)
+	DEBUGPROP_INFO_FLAGS dwFields,
+	UINT nRadix,
+	const GUID & guidFilter,
+	DWORD,
+	ULONG *pcelt,
+	IEnumDebugPropertyInfo2 **ppepi)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CGbVsStackFrame::EnumProperties\n";
 	f.close();
 
-	return E_NOTIMPL;
+	if (guidFilter == guidFilterRegisters)
+	{
+		// Create register categories
+#define DEFINE_CATEGORY(name, strName) \
+	CComObject<CDebugProperty> *name; \
+	CComObject<CDebugProperty>::CreateInstance(&name); \
+	name->AddRef(); \
+	name->Init(strName, strName, wstring(), wstring(), DBG_ATTRIB_VALUE_READONLY | \
+		DBG_ATTRIB_ACCESS_NONE | DBG_ATTRIB_TYPE_NONE);
+		
+		DEFINE_CATEGORY(cpu, L"CPU")
+		DEFINE_CATEGORY(flags, L"Flags")
+		
+		// Get the registers and save their values
+#define DEFINE_REGISTER(name, strName, width) \
+	CComObject<CDebugProperty> *name; \
+	CComObject<CDebugProperty>::CreateInstance(&name); \
+	name->AddRef(); \
+	{ \
+		wostringstream oss; \
+		oss << hex << uppercase << setfill(L'0') \
+		    << setw(width) << static_cast<unsigned>(registers.name); \
+		wstring value = oss.str(); \
+		name->Init(strName, strName, L"", value, DBG_ATTRIB_VALUE_READONLY | \
+			DBG_ATTRIB_ACCESS_NONE | DBG_ATTRIB_STORAGE_REGISTER | DBG_ATTRIB_TYPE_NONE | \
+			DBG_ATTRIB_DATA); \
+	} \
+	cpu->AddChild(name);
+
+#define DEFINE_FLAG(name, strName, bit) \
+	CComObject<CDebugProperty> *name; \
+	CComObject<CDebugProperty>::CreateInstance(&name); \
+	name->AddRef(); \
+	name->Init(strName, strName, L"", (registers.af & (1 << bit)) ? L"1" : L"0", \
+		DBG_ATTRIB_VALUE_READONLY | DBG_ATTRIB_ACCESS_NONE | DBG_ATTRIB_STORAGE_REGISTER | \
+		DBG_ATTRIB_TYPE_NONE | DBG_ATTRIB_DATA); \
+	flags->AddChild(name);
+
+		GbRegisters registers = gbd->registers();
+
+		DEFINE_REGISTER(af, L"AF", 4)
+		DEFINE_REGISTER(bc, L"BC", 4)
+		DEFINE_REGISTER(de, L"DE", 4)
+		DEFINE_REGISTER(hl, L"HL", 4)
+		DEFINE_REGISTER(sp, L"SP", 4)
+		DEFINE_REGISTER(pc, L"PC", 4)
+		DEFINE_REGISTER(ime, L"IME", 1)
+		
+		DEFINE_FLAG(z, L"Z", 7)
+		DEFINE_FLAG(n, L"N", 6)
+		DEFINE_FLAG(h, L"H", 5)
+		DEFINE_FLAG(c, L"C", 4)
+
+#undef DEFINE_CATEGORY
+#undef DEFINE_REGISTER
+#undef DEFINE_FLAG
+
+		DEBUG_PROPERTY_INFO properties[2];
+		cpu->PopulatePropertyInfo(&properties[0], dwFields, nRadix);
+		flags->PopulatePropertyInfo(&properties[1], dwFields, nRadix);
+
+		// Create the enumerator
+		typedef CComEnumWithCount<
+			IEnumDebugPropertyInfo2,
+			&IID_IEnumDebugPropertyInfo2,
+			DEBUG_PROPERTY_INFO,
+			CopyDebugPropertyInfo> CEnumDebugPropertyInfo;
+		CComObject<CEnumDebugPropertyInfo> *enumerator;
+		CComObject<CEnumDebugPropertyInfo>::CreateInstance(&enumerator);
+
+		enumerator->Init(properties, properties + 2, NULL, AtlFlagCopy);
+		*ppepi = enumerator;
+		(*ppepi)->AddRef();
+		*pcelt = 2;
+		
+		CopyDebugPropertyInfo::destroy(&properties[0]);
+		CopyDebugPropertyInfo::destroy(&properties[1]);
+
+		af->Release();
+		bc->Release();
+		de->Release();
+		hl->Release();
+		sp->Release();
+		pc->Release();
+
+		z->Release();
+		n->Release();
+		h->Release();
+		c->Release();
+		
+		cpu->Release();
+		flags->Release();
+
+		return S_OK;
+	}
+	else
+	{
+		return E_NOTIMPL;
+	}
 }
 
 HRESULT CGbVsStackFrame::GetThread(IDebugThread2 ** /*ppThread*/)

GbDebuggerMsvs/stack_frame.h

 	STDMETHOD(EnumProperties)(
 		DEBUGPROP_INFO_FLAGS dwFields,
 		UINT nRadix,
-		REFGUID guidFilter,
+		const GUID &guidFilter,
 		DWORD dwTimeout,
 		ULONG *pcelt,
 		IEnumDebugPropertyInfo2 **ppepi);

gb_emulator/include/gb_emulator/gb_debugger.h

 
 #include <gb_emulator/defs.hpp>
 
+#define DECLARE_WORKING_VARS(type, name) \
+	type name##_; \
+	bool name##Ready_; \
+	boost::mutex name##Mutex_; \
+	boost::condition_variable name##Cv_;
+
 class Gb;
 
 struct GB_EMULATOR_API GbStackFrame
 	uint16_t instructionAddr;
 };
 
+struct GB_EMULATOR_API GbRegisters
+{
+	uint16_t af;
+	uint16_t bc;
+	uint16_t de;
+	uint16_t hl;
+	uint16_t sp;
+	uint16_t pc;
+	bool ime;
+};
+
 class GB_EMULATOR_API GbDebugger
 {
 private:
 
 	boost::filesystem::path modulePath();
 	std::vector<GbStackFrame> stackFrames();
+	GbRegisters registers();
 
 private:
 	Gb &gb_;
 	boost::asio::io_service ioService_;
 	boost::asio::io_service::work work_;
 	std::set<Listener *> listeners_;
-
-	boost::filesystem::path modulePath_;
-	boost::mutex modulePathMutex_;
-	boost::condition_variable modulePathCv_;
-
-	std::vector<GbStackFrame> stackFrames_;
-	boost::mutex stackFramesMutex_;
-	boost::condition_variable stackFramesCv_;
+	
+	DECLARE_WORKING_VARS(boost::filesystem::path, modulePath)
+	DECLARE_WORKING_VARS(std::vector<GbStackFrame>, stackFrames)
+	DECLARE_WORKING_VARS(GbRegisters, registers)
 
 	GbDebugger(Gb &gb);
 	~GbDebugger();
 
 	void doModulePath();
 	void doStackFrames();
+	void doRegisters();
 
 	boost::filesystem::path getModulePath();
 
 	GbDebugger & operator=(const GbDebugger &);
 };
 
+#undef DECLARE_WORKING_VARS
+
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif

gb_emulator/src/gb.cpp

 	int timerCycles = DIVIDER_CYCLES;
 	for (;;)
 	{
-		debugger.poll();
-
 		int cyclesExecuted = cpu_.poll((min)(videoCycles, timerCycles));
 		videoCycles -= cyclesExecuted;
 		timerCycles -= cyclesExecuted;
 
+		debugger.poll();
+
 		if (timerCycles <= 0)
 		{
 			timerCycles += timers_.poll();

gb_emulator/src/gb_debugger.cpp

 using std::set;
 using std::vector;
 
+#define DEFINE_WRAPPER(type, name, bigName) \
+	type GbDebugger::name() \
+	{ \
+		unique_lock<mutex> lock(name##Mutex_); \
+		name##Ready_ = false; \
+		ioService_.post(boost::bind(&GbDebugger::do##bigName, this)); \
+		while (!name##Ready_) \
+		{ \
+			name##Cv_.wait(lock); \
+		} \
+		return name##_; \
+	}
+
+#define NOTIFY(name) \
+	lock_guard<mutex> lock(name##Mutex_); \
+	name##Ready_ = true; \
+	name##_ = name; \
+	name##Cv_.notify_one();
+
 GbDebugger::GbDebugger(Gb &gb):
 gb_(gb),
 work_(ioService_)
 	ioService_.post(boost::bind(&GbDebugger::doSuspend, this));
 }
 
-fs::path GbDebugger::modulePath()
-{
-	unique_lock<mutex> lock(modulePathMutex_);
-	modulePath_.clear();
-	ioService_.post(boost::bind(&GbDebugger::doModulePath, this));
-	
-	while (modulePath_.empty())
-	{
-		modulePathCv_.wait(lock);
-	}
-
-	return modulePath_;
-}
-
-vector<GbStackFrame> GbDebugger::stackFrames()
-{
-	unique_lock<mutex> lock(stackFramesMutex_);
-	stackFrames_.clear();
-	ioService_.post(boost::bind(&GbDebugger::doStackFrames, this));
-	
-	while (stackFrames_.empty())
-	{
-		stackFramesCv_.wait(lock);
-	}
-
-	return stackFrames_;
-}
+DEFINE_WRAPPER(fs::path, modulePath, ModulePath)
+DEFINE_WRAPPER(vector<GbStackFrame>, stackFrames, StackFrames)
+DEFINE_WRAPPER(GbRegisters, registers, Registers)
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Implementation functions
 	// Get the module path
 	fs::path modulePath = getModulePath();
 	
-	// Notify the caller that the data is ready
-	lock_guard<mutex> lock(modulePathMutex_);
-	modulePath_ = modulePath;
-	modulePathCv_.notify_one();
+	NOTIFY(modulePath)
 }
 
 void GbDebugger::doStackFrames()
 	frame.instructionAddr = gb_.cpu_.pc;
 	stackFrames.push_back(frame);
 	
-	// Notify the caller that the data is ready
-	lock_guard<mutex> lock(stackFramesMutex_);
-	stackFrames_ = stackFrames;
-	stackFramesCv_.notify_one();
+	NOTIFY(stackFrames)
+}
+
+void GbDebugger::doRegisters()
+{
+	GbRegisters registers;
+	registers.af = gb_.cpu_.r.r16[AF];
+	registers.bc = gb_.cpu_.r.r16[BC];
+	registers.de = gb_.cpu_.r.r16[DE];
+	registers.hl = gb_.cpu_.r.r16[HL];
+	registers.sp = gb_.cpu_.sp;
+	registers.pc = gb_.cpu_.pc;
+
+	switch (gb_.cpu_.ime)
+	{
+	case IME_DISABLED:
+	case IME_ENABLING:
+		registers.ime = false;
+		break;
+
+	case IME_ENABLED:
+	case IME_DISABLING:
+		registers.ime = true;
+		break;
+
+	default:
+		assert(!"all ime types should have been handled");
+	}
+	
+	NOTIFY(registers)
 }
 
 fs::path GbDebugger::getModulePath()