Get local values
Applies to: Visual Studio Visual Studio for Mac
Note
This article applies to Visual Studio 2017. If you're looking for the latest Visual Studio documentation, see Visual Studio documentation. We recommend upgrading to the latest version of Visual Studio. Download it here
Important
In Visual Studio 2015, this way of implementing expression evaluators is deprecated. For information about implementing CLR expression evaluators, see CLR expression evaluators and Managed expression evaluator sample.
To get the value of a local, Visual Studio calls GetPropertyInfo for that local. In this implementation, the class CFieldProperty
implements the IDebugProperty2 interface for each local.
This implementation of IDebugProperty2::GetPropertyInfo
performs the following tasks:
Gets the local's name, property, and attributes from the FIELD_INFO structure filled in when the class was instantiated and initialized.
Gets the local's type from the IDebugField object.
Gets the local's value from the
IDebugField
object. This field is bound to the memory location of the local using the IDebugBinder object and the value is obtained from the resulting IDebugObject object.Returns all requested properties in a DEBUG_PROPERTY_INFO structure.
Managed code
This example shows an implementation of IDebugProperty2::GetPropertyInfo
for a method's local in managed code. It also shows a helper function, Field.GetType
, that is used to get the field's type. Field.GetValue
is shown in Evaluate locals. The helper function Field.MapModifiersToAttributes
(not shown) simply converts a field's FIELD_MODIFIERS flags to DBG_ATTRIB_FLAGS values.
namespace EEMC
{
public class CFieldProperty : IDebugProperty2
{
public HRESULT GetPropertyInfo(
uint dwFields,
uint radix,
uint timeout,
IDebugReference2[] refArgs,
uint argCount,
DEBUG_PROPERTY_INFO[] infoArray)
{
DEBUGPROP_INFO_FLAGS flags = (DEBUGPROP_INFO_FLAGS) dwFields;
DEBUGPROP_INFO_FLAGS infoFields = DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_NONE;
// Initialize the structure.
DEBUG_PROPERTY_INFO info = infoArray[0];
info.pProperty = null;
// Fill in the full name, if requested.
if (0 != (flags & DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_FULLNAME))
{
info.bstrFullName = fieldInfo.bstrFullName;
infoFields |= DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_FULLNAME;
}
// Fill in the name, if requested.
if (0 != (flags & DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_NAME))
{
info.bstrName = fieldInfo.bstrName;
infoFields |= DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_NAME;
}
// Fill in the type, if requested.
if (0 != (flags & DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_TYPE))
{
if (fieldType == null)
fieldType = Field.GetType(field, out fieldSize);
if (fieldType == null)
info.bstrType = "unknown";
else
info.bstrType = fieldType.ToString();
infoFields |= DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_TYPE;
}
// The property associated with this property is this property.
if (0 != (flags & DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_PROP))
{
info.pProperty = this;
infoFields |= DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_PROP;
}
// Get the property value, if requested.
if (0 != (flags & DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_VALUE))
{
if (fieldType == null)
fieldType = Field.GetType(field, out fieldSize);
if (fieldType == null)
info.bstrValue = "?";
else
{
object o = Field.GetValue(binder, field, fieldType, fieldSize);
if (o == null)
info.bstrValue = "?";
else
info.bstrValue = o.ToString();
}
infoFields |= DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_VALUE;
}
// Get the property attributes, if requested.
if (0 != (flags & DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_ATTRIB))
{
info.dwAttrib =
(ulong) Field.MapModifiersToAttributes(
(FIELD_MODIFIERS) fieldInfo.dwModifiers,
fieldKind);
infoFields |= DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_ATTRIB;
}
info.dwFields = (uint) infoFields;
infoArray[0] = info;
return COM.S_OK;
}
}
//----------------------------------------------------------------------------
internal class Field
{
internal static Type GetType(IDebugField field, out uint size)
{
size = 0;
IDebugField fieldType = null;
field.GetType(out fieldType);
if (fieldType == null) return null;
// Get field size.
fieldType.GetSize(out size);
// Get approximate type name.
FIELD_INFO[] fieldTypeInfo = new FIELD_INFO[1];
fieldType.GetInfo((uint) FIELD_INFO_FIELDS.FIF_NAME,
fieldTypeInfo);
// Map approximate name to type name.
switch (fieldTypeInfo[0].bstrName)
{
case "whole":
switch (size)
{
case 1: return typeof(sbyte);
case 2: return typeof(short);
case 4: return typeof(int);
case 8: return typeof(long);
}
break;
case "uwhole":
switch (size)
{
case 1: return typeof(byte);
case 2: return typeof(char);
case 4: return typeof(uint);
case 8: return typeof(ulong);
}
break;
case "real":
switch (size)
{
case 4: return typeof(float);
case 8: return typeof(double);
}
break;
case "bool": return typeof(bool);
case "string": return typeof(string);
}
return null;
}
}
Unmanaged code
This example shows an implementation of IDebugProperty2::GetPropertyInfo
for a method's local in unmanaged code. It also shows two helper functions, FieldGetType
and FieldGetValue
that are used to get the field's type and value, respectively. The VARIANT
s are used for the field's value and type as a VARIANT
can handle a wide variety of value types. In this implementation, FieldGetValue
returns an IDebugField object that is later converted to a value in a call to FieldGetPrimitiveValue
(which is shown in Evaluate locals).
STDMETHODIMP CFieldProperty::GetPropertyInfo(
in DEBUGPROP_INFO_FLAGS infoFlags,
in DWORD radix,
in DWORD timeout,
in IDebugReference2** ppargs,
in DWORD argCount,
out DEBUG_PROPERTY_INFO* ppropertyInfo
)
{
if (!ppropertyInfo)
return E_INVALIDARG;
else
memset(ppropertyInfo,0,sizeof(*ppropertyInfo));
if (infoFlags & DEBUGPROP_INFO_FULLNAME)
{
ppropertyInfo->dwFields |= DEBUGPROP_INFO_FULLNAME;
ppropertyInfo->bstrFullName = SysAllocString( m_fieldInfo.bstrFullName );
}
if (infoFlags & DEBUGPROP_INFO_NAME)
{
ppropertyInfo->dwFields |= DEBUGPROP_INFO_NAME;
ppropertyInfo->bstrName = SysAllocString( m_fieldInfo.bstrName );
}
if (infoFlags & DEBUGPROP_INFO_TYPE)
{
ppropertyInfo->dwFields |= DEBUGPROP_INFO_TYPE;
VARIANT type;
if (SUCCEEDED(FieldGetType( m_field, &type )))
{
// Convert the variant's type to a string
VariantTypeToString( type, &(ppropertyInfo->bstrType) );
VariantClear(&type);
}
if (ppropertyInfo->bstrType == NULL)
ppropertyInfo->bstrType = SysAllocString( GetString(IDS_MSG_UNKNOWNTYPE) );
}
if (infoFlags & DEBUGPROP_INFO_PROP)
{
ppropertyInfo->dwFields |= DEBUGPROP_INFO_PROP;
QueryInterface( IID_IDebugProperty2,
reinterpret_cast<void**>(&(ppropertyInfo->pProperty)) );
}
if (infoFlags & DEBUGPROP_INFO_VALUE)
{
ppropertyInfo->dwFields |= DEBUGPROP_INFO_VALUE;
//only show primitive values
VARIANT value;
if (SUCCEEDED(FieldGetValue(m_field, &value)))
{
VariantValueToString( radix, m_binder, value,
&(ppropertyInfo->bstrValue) );
VariantClear(&value);
}
if (ppropertyInfo->bstrValue == NULL)
ppropertyInfo->bstrValue = SysAllocString( GetString(IDS_MSG_UNKNOWNVALUE) );
}
if (infoFlags & DEBUGPROP_INFO_VALUE_AUTOEXPAND)
{
// AUTOEXPAND is ignored in this example
}
if (infoFlags & DEBUGPROP_INFO_ATTRIB)
{
ppropertyInfo->dwFields |= DEBUGPROP_INFO_ATTRIB;
FIELD_MODIFIERS modifiers = m_fieldInfo.dwModifiers;
DBG_ATTRIB_FLAGS attrib = DBG_ATTRIB_NONE;
//access
if (modifiers & FIELD_MOD_ACCESS_PUBLIC)
attrib |= DBG_ATTRIB_ACCESS_PUBLIC;
if (modifiers & FIELD_MOD_ACCESS_PRIVATE)
attrib |= DBG_ATTRIB_ACCESS_PRIVATE;
if (modifiers & FIELD_MOD_ACCESS_PROTECTED)
attrib |= DBG_ATTRIB_ACCESS_PROTECTED;
if (modifiers & FIELD_MOD_FINAL)
attrib |= DBG_ATTRIB_ACCESS_FINAL;
//constant
if (modifiers & FIELD_MOD_CONSTANT)
attrib |= DBG_ATTRIB_VALUE_READONLY;
//storage
if (m_fieldKind & FIELD_SYM_GLOBAL)
attrib |= DBG_ATTRIB_STORAGE_GLOBAL;
if (modifiers & FIELD_MOD_STATIC)
attrib |= DBG_ATTRIB_STORAGE_STATIC;
//type modifier
if (modifiers & FIELD_MOD_VIRTUAL)
attrib |= DBG_ATTRIB_TYPE_VIRTUAL;
if (modifiers & FIELD_MOD_CONSTANT)
attrib |= DBG_ATTRIB_TYPE_CONSTANT;
if (modifiers & FIELD_MOD_SYNCHRONIZED)
attrib |= DBG_ATTRIB_TYPE_SYNCHRONIZED;
if (modifiers & FIELD_MOD_VOLATILE)
attrib |= DBG_ATTRIB_TYPE_VOLATILE;
//type
if (m_fieldKind & FIELD_TYPE_METHOD)
attrib |= DBG_ATTRIB_METHOD;
if (m_fieldKind & FIELD_TYPE_PROP)
attrib |= DBG_ATTRIB_PROPERTY;
if (m_fieldKind & FIELD_TYPE_CLASS)
attrib |= DBG_ATTRIB_CLASS;
if (m_fieldKind & FIELD_TYPE_INTERFACE)
attrib |= DBG_ATTRIB_INTERFACE;
if (m_fieldKind & FIELD_TYPE_INNERCLASS)
attrib |= DBG_ATTRIB_INNERCLASS;
if (m_fieldKind & FIELD_KIND_SYMBOL)
attrib |= DBG_ATTRIB_DATA;
//set the debug attributes
ppropertyInfo->dwAttrib = attrib;
}
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// Helper functions
struct PrimitiveTypeInfo
{
LPCOLESTR pszName;
UINT size;
VARTYPE vt;
};
PrimitiveTypeInfo primitiveTypeTable[] =
{
{ OLE("string"), 0, VT_BSTR },
{ OLE("whole"), 1, VT_I1 },
{ OLE("whole"), 2, VT_I2 },
{ OLE("whole"), 4, VT_I4 },
{ OLE("whole"), 8, VT_I8 },
{ OLE("uwhole"), 1, VT_UI1 },
{ OLE("uwhole"), 2, VT_UI2 },
{ OLE("uwhole"), 4, VT_UI4 },
{ OLE("uwhole"), 8, VT_UI8 },
{ OLE("real"), 4, VT_R4 },
{ OLE("real"), 8, VT_R8 },
{ OLE("bool"), 1, VT_BOOL },
{ OLE("bool"), 2, VT_BOOL },
{ OLE("bool"), 4, VT_BOOL },
{ OLE("System.String"), 0, VT_BSTR },
{ OLE("System.SByte"), 1, VT_I1 },
{ OLE("System.Int16"), 2, VT_I2 },
{ OLE("System.Int32"), 4, VT_I4 },
{ OLE("System.Int64"), 8, VT_I8 },
{ OLE("System.Byte"), 1, VT_UI1 },
{ OLE("System.Char"), 1, VT_UI2 },
{ OLE("System.UInt16"), 2, VT_UI2 },
{ OLE("System.UInt32"), 4, VT_UI4 },
{ OLE("System.UInt64"), 8, VT_UI8 },
{ OLE("System.Single"), 4, VT_R4 },
{ OLE("System.Double"), 8, VT_R8 },
{ OLE("System.Boolean"), 1, VT_BOOL },
{ OLE("System.Boolean"), 2, VT_BOOL },
{ OLE("System.Boolean"), 4, VT_BOOL },
{ NULL, 0, VT_EMPTY }
};
HRESULT FieldGetType( in IDebugField* pfield, out VARIANT* pvarType )
{
HRESULT hr;
if (pfield == NULL)
return E_INVALIDARG;
if (pvarType == NULL)
return E_INVALIDARG;
else
*pvarType = 0;
//get type size and name
DWORD fieldTypeSize;
FIELD_INFO fieldTypeInfo;
IDebugField* pfieldType = NULL;
hr = pfield->GetType( &pfieldType );
if (FAILED(hr))
return hr;
hr = pfieldType->GetSize( &fieldTypeSize );
if (FAILED(hr))
{
pfieldType->Release();
return hr;
}
hr = pfieldType->GetInfo( FIF_NAME, &fieldTypeInfo );
if (FAILED(hr))
{
pfieldType->Release();
return hr;
}
//check for primitive types
memset( pvarType, 0, sizeof(*pvarType) );
VariantInit(pvarType);
for (PrimitiveTypeInfo* pprimTypeInfo = primitiveTypeTable;
pprimTypeInfo->pszName != NULL;
pprimTypeInfo++)
{
if (pprimTypeInfo->size == fieldTypeSize &&
(wcscmp(pprimTypeInfo->pszName,fieldTypeInfo.bstrName) == 0))
{
pvarType->vt = pprimTypeInfo->vt;
break;
}
}
//VT_UNKNOWN is used for all other (structured) types
if (pvarType->vt == VT_EMPTY)
{
pvarType->vt = VT_UNKNOWN;
pvarType->punkVal = pfieldType;
pvarType->punkVal->AddRef();
}
if (fieldTypeInfo.bstrName != NULL)
SysFreeString(fieldTypeInfo.bstrName);
pfieldType->Release();
return S_OK;
}
//----------------------------------------------------------------------------
HRESULT FieldGetValue( in IDebugField* pfield, out VARIANT* pvarValue )
{
if (pvarValue == NULL)
return E_INVALIDARG;
else
*pvarValue = 0;
if (pfield == NULL)
return E_INVALIDARG;
//we delay getting the primitive value by just setting VT_UNKNOWN
pvarValue->vt = VT_UNKNOWN;
pvarValue->punkVal = pfield;
pvarValue->punkVal->AddRef();
return S_OK;
}