Sample Implementation of Changing Values
Note
This article applies to Visual Studio 2015. 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, please see CLR Expression Evaluators and Managed Expression Evaluator Sample.
Every local displayed in the Locals window has an IDebugProperty2 object associated with it. This IDebugProperty2
object contains the local's name, value, and type. When a user changes the value of a local, Visual Studio calls SetValueAsString to update the value of the local in memory. In this example, the local is represented by the CFieldProperty
class that implements the IDebugProperty2
interface.
Note
For Watch and QuickWatch expressions, the value being changed is represented by the CValueProperty
class in the MyCEE sample. However, the implementation of IDebugProperty2::SetValueAsString
is the same as shown here.
This implementation of IDebugProperty2::SetValueAsString
performs the following tasks:
Evaluates the expression to produce a value.
Binds the associated IDebugField object to its memory location and produce an IDebugObject object.
Converts the value to a series of bytes.
Calls SetValue to store the bytes in memory.
Managed Code
This is an implementation of IDebugProperty2::SetValueAsString
in managed code.
[C#]
namespace EEMC
{
public class CFieldProperty : IDebugProperty2
{
public HRESULT SetValueAsString(
string pszValue,
uint dwRadix,
uint dwTimeout)
{
HRESULT hr = COM.E_NOTIMPL;
uint flags = (uint)enum_PARSEFLAGS.PARSE_EXPRESSION;
CParsedExpression parsedExpression =
new CParsedExpression(flags, dwRadix, pszValue);
IDebugProperty2 value;
parsedExpression.EvaluateSync(flags,
dwTimeout,
null,
null,
null,
"string",
out value);
if (value != null)
{
DEBUG_PROPERTY_INFO[] dpi = new DEBUG_PROPERTY_INFO[1];
hr = value.GetPropertyInfo(
(uint)enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_VALUE,
dwRadix,
dwTimeout,
null,
0,
dpi);
if (hr == COM.S_OK)
{
hr = Field.SetValue(binder,
field,
dpi[0].bstrValue);
}
}
return hr;
}
}
//----------------------------------------------------------------------------
internal class Field
{
internal static HRESULT SetValue(
IDebugBinder binder,
IDebugField field,
string bstrValue)
{
HRESULT hr = COM.E_FAIL;
uint fieldSize = 0;
Type fieldType = GetType(field, out fieldSize);
if (fieldType != null)
{
FIELD_INFO[] fi = new FIELD_INFO[1];
hr = field.GetInfo((uint)enum_FIELD_INFO_FIELDS.FIF_MODIFIERS, fi);
if (hr != COM.S_OK ||
(fi[0].dwModifiers & (uint)enum_FIELD_MODIFIERS.FIELD_MOD_CONSTANT) != 0)
{
// Couldn't get field info or field is constant and can't be changed.
return COM.E_FAIL;
}
IDebugObject valueObject;
IntPtr pBuffer = new IntPtr();
int bufferSize = 0;
binder.Bind(null, field, out valueObject);
if (valueObject != null)
{
if (fieldType == typeof(sbyte))
{
sbyte value = Convert.ToSByte(bstrValue);
bufferSize = 1;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(short))
{
System.Int16 value = Convert.ToInt16(bstrValue);
bufferSize = 2;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(int))
{
System.Int32 value = Convert.ToInt32(bstrValue);
bufferSize = 4;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(long))
{
System.Int64 value = Convert.ToInt64(bstrValue);
bufferSize = 8;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(byte))
{
byte value = Convert.ToByte(bstrValue);
bufferSize = 1;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(char))
{
char value = Convert.ToChar(bstrValue);
bufferSize = 1;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(bool))
{
bool value = Convert.ToBoolean(bstrValue);
bufferSize = 1;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(uint))
{
System.UInt32 value = Convert.ToUInt32(bstrValue);
bufferSize = 4;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(ulong))
{
System.UInt64 value = Convert.ToUInt64(bstrValue);
bufferSize = 8;
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(float))
{
float value = Convert.ToSingle(bstrValue);
bufferSize = sizeof(float);
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(double))
{
double value = Convert.ToDouble(bstrValue);
bufferSize = sizeof(double);
pBuffer = Marshal.AllocCoTaskMem(bufferSize);
Marshal.StructureToPtr(value, pBuffer, false);
}
if (fieldType == typeof(string))
{
bufferSize = bstrValue.Length;
pBuffer = Marshal.StringToCoTaskMemAuto(bstrValue);
}
if (bufferSize != 0)
{
byte[] byteBuffer = new byte[bufferSize];
for (int i = 0; i < bufferSize; i++)
{
byteBuffer[i] = Marshal.ReadByte(pBuffer,i);
}
Marshal.FreeCoTaskMem(pBuffer);
hr = valueObject.SetValue(byteBuffer, (uint)bufferSize);
}
}
}
return hr;
}
}
}
Unmanaged Code
This is an implementation of IDebugProperty2::SetValueAsString
in managed code. The helper function FieldCoerceValueType
(not shown) forces a VARIANT
to be a specific type and makes sure the value is one of the types FieldSetValue
can handle.
[C++]
STDMETHODIMP CFieldProperty::SetValueAsString(
in LPCOLESTR pszValueStr,
in DWORD radix,
in DWORD timeout
)
{
HRESULT hr;
//evaluate the value
VARIANT value;
hr = Evaluate( m_provider,
m_address,
m_binder,
pszValueStr,
NULL,
&value );
if (FAILED(hr))
return hr;
if (hr == S_FALSE)
return E_FAIL; //parse failed
//copy the bits
hr = FieldSetValue( m_binder, m_field, value );
return hr;
}
//----------------------------------------------------------------------------
HRESULT FieldSetValue(
in IDebugBinder* pbinder,
in IDebugField* pfield,
in VARIANT& rawValue )
{
if (pfield == NULL)
return E_INVALIDARG;
if (pbinder == NULL)
return E_INVALIDARG;
HRESULT hr = S_OK;
IDebugObject* pobject = NULL;
VARIANT value;
VariantInit(&value);
//check the type
hr = FieldCoerceValueType( pbinder, pfield, rawValue, &value );
if (FAILED(hr))
goto fail;
if (hr == S_FALSE)
{
VariantClear(value);
return E_FAIL;
}
//get the object
hr = pbinder->Bind( NULL, pfield, &pobject );
if (FAILED(hr))
{
pobject->Release();
VariantClear(value);
return hr;
}
//set the value
switch (value.vt)
{
case VT_BSTR:
{
if (value.bstrVal == NULL)
{
LPOLESTR pszEmptyStr = OLE("");
hr = pobject->SetValue( reinterpret_cast<BYTE*>(pszEmptyStr),
sizeof(OLECHAR) );
}
else
hr = pobject->SetValue( reinterpret_cast<BYTE*>(value.bstrVal),
(SysStringLen(value.bstrVal)+1) * sizeof(wchar_t));
}
case VT_BOOL:
case VT_I1:
case VT_UI1:
hr = pobject->SetValue( reinterpret_cast<BYTE*>(&(value.byref)), 1 );
break;
case VT_I2:
case VT_UI2:
hr = pobject->SetValue( reinterpret_cast<BYTE*>(&(value.byref)), 2 );
break;
case VT_I4:
case VT_UI4:
case VT_R4:
hr = pobject->SetValue( reinterpret_cast<BYTE*>(&(value.byref)), 4 );
break;
case VT_I8:
case VT_UI8:
case VT_R8:
hr = pobject->SetValue( reinterpret_cast<BYTE*>(&(value.byref)), 8 );
break;
case VT_VOID:
case VT_EMPTY:
hr = E_FAIL;
break;
case VT_UNKNOWN:
{
//this is also a field (structured type)
if (value.punkVal == NULL)
{
pobject->Release();
VariantClear(value);
return E_FAIL;
};
IDebugField* valueField;
hr = value.punkVal->QueryInterface( IID_IDebugField,
reinterpret_cast<void**>(&valueField) );
if (FAILED(hr))
{
pobject->Release();
VariantClear(value);
return hr;
};
//for MyC we simply copy the bits
IDebugObject* valueObject;
hr = pbinder->Bind( NULL, valueField, &valueObject );
valueField->Release();
if (FAILED(hr))
{
pobject->Release();
VariantClear(value);
return hr;
};
BYTE* pvalueBits;
UINT valueSize;
hr = valueObject->GetSize( &valueSize );
if (FAILED(hr))
{
valueObject->Release();
pobject->Release();
VariantClear(value);
return hr;
};
pvalueBits = NALLOC(BYTE,valueSize+1);
if (!pvalueBits)
{
valueObject->Release();
pobject->Release();
VariantClear(value);
return E_OUTOFMEMORY;
};
hr = valueObject->GetValue( pvalueBits, valueSize );
valueObject->Release();
if (FAILED(hr))
{
free(pvalueBits);
pobject->Release();
VariantClear(value);
return hr;
}
hr = pobject->SetValue( pvalueBits, valueSize );
free(pvalueBits);
if (FAILED(hr))
{
pobject->Release();
VariantClear(value);
return hr;
}
break;
}
default:
//not a primitive type
hr = E_FAIL;
break;
}
VariantClear( &value );
pobject->Release();
return hr;
}