зеркало из https://github.com/microsoft/cppwinrt.git
717 строки
26 KiB
C++
717 строки
26 KiB
C++
#include "pch.h"
|
|
#include <variant>
|
|
#include "object_visualizer.h"
|
|
#include "property_visualizer.h"
|
|
|
|
using namespace Microsoft::VisualStudio::Debugger;
|
|
using namespace Microsoft::VisualStudio::Debugger::Evaluation;
|
|
using namespace std::literals;
|
|
using namespace winrt;
|
|
using namespace winmd::reader;
|
|
|
|
#define IID_IInspectable L"AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90"
|
|
#define IID_IStringable L"96369F54-8EB6-48F0-ABCE-C1B211E627C3"
|
|
|
|
constexpr struct
|
|
{
|
|
PCWSTR propField;
|
|
PCWSTR displayType;
|
|
}
|
|
g_categoryData[] =
|
|
{
|
|
{ L"b", L"bool" },
|
|
{ L"c", L"wchar_t" },
|
|
{ L"i1", L"int8_t" },
|
|
{ L"u1", L"uint8_t" },
|
|
{ L"i2", L"int16_t" },
|
|
{ L"u2", L"uint16_t" },
|
|
{ L"i4", L"int32_t" },
|
|
{ L"u4", L"uint32_t" },
|
|
{ L"i8", L"int64_t" },
|
|
{ L"u8", L"uint64_t" },
|
|
{ L"r4", L"float" },
|
|
{ L"r8", L"double" },
|
|
{ L"s,sh", L"winrt::hstring" },
|
|
{ L"g", L"winrt::guid" },
|
|
};
|
|
|
|
NatvisDiagnosticLevel GetNatvisDiagnosticLevel()
|
|
{
|
|
static NatvisDiagnosticLevel level = NatvisDiagnosticLevel::Unknown;
|
|
if (level != NatvisDiagnosticLevel::Unknown)
|
|
{
|
|
return level;
|
|
}
|
|
level = NatvisDiagnosticLevel::Error;
|
|
|
|
// If < VS16, just output errors
|
|
if (!DkmComponentManager::IsApiVersionSupported(DkmApiVersion::VS16RTM))
|
|
{
|
|
return level;
|
|
}
|
|
|
|
// Else, use VS natvis diagnostics level directly
|
|
HKEY userSettingsKey;
|
|
if (FAILED(DkmGlobalSettings::OpenVSUserSettingsKey(L"Debugger\\NatvisDiagnostics", &userSettingsKey)))
|
|
{
|
|
return level;
|
|
}
|
|
|
|
char data[MAX_PATH];
|
|
DWORD dataSize = _countof(data);
|
|
if (RegGetValue(userSettingsKey, "", "Level", RRF_RT_REG_SZ, nullptr, data, &dataSize) == ERROR_SUCCESS)
|
|
{
|
|
switch (data[0])
|
|
{
|
|
case 'O':
|
|
level = NatvisDiagnosticLevel::Off;
|
|
break;
|
|
case 'E':
|
|
level = NatvisDiagnosticLevel::Error;
|
|
break;
|
|
case 'W':
|
|
level = NatvisDiagnosticLevel::Warning;
|
|
break;
|
|
case 'V':
|
|
level = NatvisDiagnosticLevel::Verbose;
|
|
break;
|
|
}
|
|
}
|
|
RegCloseKey(userSettingsKey);
|
|
return level;
|
|
}
|
|
|
|
HRESULT NatvisDiagnostic(DkmProcess* process, std::wstring_view const& messageText, NatvisDiagnosticLevel level, HRESULT errorCode)
|
|
{
|
|
if (GetNatvisDiagnosticLevel() < level)
|
|
{
|
|
return S_OK;
|
|
}
|
|
auto userMessage = std::wstring(L"Natvis C++/WinRT: ") + std::wstring(messageText) + L"\n";
|
|
com_ptr<DkmString> pUserMessageText;
|
|
IF_FAIL_RET(DkmString::Create(DkmSourceString(userMessage.c_str()), pUserMessageText.put()));
|
|
com_ptr<DkmUserMessage> pUserMessage;
|
|
IF_FAIL_RET(DkmUserMessage::Create(process->Connection(), process, DkmUserMessageOutputKind::UnfilteredOutputWindowMessage, pUserMessageText.get(), 0, errorCode, pUserMessage.put()));
|
|
return pUserMessage->Post();
|
|
}
|
|
|
|
static HRESULT EvaluatePropertyExpression(
|
|
_In_ PropertyData const& prop,
|
|
_In_ DkmVisualizedExpression* pExpression,
|
|
_In_ DkmPointerValueHome* pObject,
|
|
ObjectType objectType,
|
|
_Out_ com_ptr<DkmEvaluationResult>& pEvaluationResult
|
|
)
|
|
{
|
|
wchar_t abiAddress[40];
|
|
auto process = pExpression->RuntimeInstance()->Process();
|
|
bool is64Bit = ((process->SystemInformation()->Flags() & DefaultPort::DkmSystemInformationFlags::Is64Bit) != 0);
|
|
swprintf_s(abiAddress, is64Bit ? L"%s0x%I64x" : L"%s0x%08x", objectType == ObjectType::Abi ? L"(::IUnknown*)" : L"*(::IUnknown**)", pObject->Address());
|
|
wchar_t wszEvalText[500];
|
|
std::wstring propCast;
|
|
PCWSTR propField;
|
|
if (prop.category < PropertyCategory::Value)
|
|
{
|
|
propField = g_categoryData[(int)prop.category].propField;
|
|
}
|
|
else
|
|
{
|
|
propField = L"v";
|
|
propCast = L"*(" + prop.abiType + L"*)";
|
|
}
|
|
swprintf_s(wszEvalText, L"%sWINRT_abi_val(%s, L\"{%s}\", %i).%s", propCast.c_str(), abiAddress, prop.iid.c_str(), prop.index, propField);
|
|
|
|
com_ptr<DkmString> pEvalText;
|
|
IF_FAIL_RET(DkmString::Create(DkmSourceString(wszEvalText), pEvalText.put()));
|
|
NatvisDiagnostic(process, wszEvalText, NatvisDiagnosticLevel::Verbose);
|
|
|
|
auto evalFlags = DkmEvaluationFlags::TreatAsExpression | DkmEvaluationFlags::ForceEvaluationNow | DkmEvaluationFlags::ForceRealFuncEval;
|
|
auto inspectionContext = pExpression->InspectionContext();
|
|
com_ptr<DkmLanguageExpression> pLanguageExpression;
|
|
IF_FAIL_RET(DkmLanguageExpression::Create(
|
|
inspectionContext->Language(),
|
|
evalFlags,
|
|
pEvalText.get(),
|
|
DkmDataItem::Null(),
|
|
pLanguageExpression.put()
|
|
));
|
|
|
|
com_ptr<DkmInspectionContext> pInspectionContext;
|
|
if ( (pExpression->InspectionContext()->EvaluationFlags() & evalFlags) != evalFlags)
|
|
{
|
|
DkmInspectionContext::Create(
|
|
inspectionContext->InspectionSession(),
|
|
inspectionContext->RuntimeInstance(),
|
|
inspectionContext->Thread(),
|
|
inspectionContext->Timeout(),
|
|
evalFlags,
|
|
inspectionContext->FuncEvalFlags(),
|
|
inspectionContext->Radix(),
|
|
inspectionContext->Language(),
|
|
inspectionContext->ReturnValue(),
|
|
pInspectionContext.put()
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pInspectionContext.copy_from(inspectionContext);
|
|
}
|
|
|
|
auto hr = pExpression->EvaluateExpressionCallback(
|
|
pInspectionContext.get(),
|
|
pLanguageExpression.get(),
|
|
pExpression->StackFrame(),
|
|
pEvaluationResult.put()
|
|
);
|
|
if (hr != S_OK)
|
|
{
|
|
NatvisDiagnostic(process, L"EvaluateExpressionCallback failed", NatvisDiagnosticLevel::Warning, hr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT EvaluatePropertyString(
|
|
_In_ PropertyData const& prop,
|
|
_In_ DkmVisualizedExpression* pExpression,
|
|
_In_ DkmPointerValueHome* pObject,
|
|
ObjectType objectType,
|
|
_Out_ com_ptr<DkmString>& pValue
|
|
)
|
|
{
|
|
com_ptr<DkmEvaluationResult> pEvaluationResult;
|
|
IF_FAIL_RET(EvaluatePropertyExpression(prop, pExpression, pObject, objectType, pEvaluationResult));
|
|
if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
com_ptr<DkmSuccessEvaluationResult> pSuccessEvaluationResult = pEvaluationResult.as<DkmSuccessEvaluationResult>();
|
|
if (pSuccessEvaluationResult->Address()->Value() != 0)
|
|
{
|
|
pValue.copy_from(pSuccessEvaluationResult->Value());
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
static std::string GetRuntimeClass(
|
|
_In_ DkmVisualizedExpression* pExpression,
|
|
_In_ DkmPointerValueHome* pObject,
|
|
ObjectType objectType
|
|
)
|
|
{
|
|
com_ptr<DkmString> pValue;
|
|
EvaluatePropertyString({ IID_IInspectable, -2, PropertyCategory::String }, pExpression, pObject, objectType, pValue);
|
|
if (!pValue || pValue->Length() == 0)
|
|
{
|
|
return "";
|
|
}
|
|
return to_string(pValue->Value());
|
|
}
|
|
|
|
static HRESULT ObjectToString(
|
|
_In_ DkmVisualizedExpression* pExpression,
|
|
_In_ DkmPointerValueHome* pObject,
|
|
ObjectType objectType,
|
|
_Out_ com_ptr<DkmString>& pValue
|
|
)
|
|
{
|
|
if (SUCCEEDED(EvaluatePropertyString({ IID_IStringable, 0, PropertyCategory::String }, pExpression, pObject, objectType, pValue)))
|
|
{
|
|
if (pValue && pValue->Length() > 0)
|
|
{
|
|
return S_OK;
|
|
}
|
|
pValue = nullptr;
|
|
|
|
// WINRT_abi_val returned 0, which may be success or failure (due to VirtualQuery validation)
|
|
// Call back for the runtime class name to determine which it was
|
|
if (!GetRuntimeClass(pExpression, pObject, objectType).empty())
|
|
{
|
|
return DkmString::Create(L"<Expand object to view properties>", pValue.put());
|
|
}
|
|
}
|
|
|
|
// VirtualQuery validation failed (as determined by no runtime class name) or an
|
|
// exception escaped WINRT_abi_val (e.g, bad pointer, which we try to avoid via VirtualQuery)
|
|
return DkmString::Create(L"<Object uninitialized or information unavailable>", pValue.put());
|
|
}
|
|
|
|
static HRESULT CreateChildVisualizedExpression(
|
|
_In_ PropertyData const& prop,
|
|
_In_ DkmVisualizedExpression* pParent,
|
|
ObjectType objectType,
|
|
_Deref_out_ DkmChildVisualizedExpression** ppResult
|
|
)
|
|
{
|
|
*ppResult = nullptr;
|
|
|
|
com_ptr<DkmEvaluationResult> pEvaluationResult;
|
|
auto valueHome = make_com_ptr(pParent->ValueHome());
|
|
com_ptr<DkmPointerValueHome> pParentPointer = valueHome.as<DkmPointerValueHome>();
|
|
IF_FAIL_RET(EvaluatePropertyExpression(prop, pParent, pParentPointer.get(), objectType, pEvaluationResult));
|
|
if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
com_ptr<DkmSuccessEvaluationResult> pSuccessEvaluationResult = pEvaluationResult.as<DkmSuccessEvaluationResult>();
|
|
com_ptr<DkmString> pValue;
|
|
com_ptr<DkmPointerValueHome> pChildPointer;
|
|
bool isNonNullObject = false;
|
|
if (prop.category == PropertyCategory::Class)
|
|
{
|
|
auto childObjectAddress = pSuccessEvaluationResult->Address()->Value();
|
|
if (childObjectAddress)
|
|
{
|
|
isNonNullObject = true;
|
|
IF_FAIL_RET(DkmPointerValueHome::Create(childObjectAddress, pChildPointer.put()));
|
|
IF_FAIL_RET(ObjectToString(pParent, pChildPointer.get(), ObjectType::Abi, pValue));
|
|
}
|
|
}
|
|
if(!isNonNullObject)
|
|
{
|
|
com_ptr<DkmExpressionValueHome> expressionValueHome = make_com_ptr(pParent->ValueHome());
|
|
pChildPointer = expressionValueHome.as<DkmPointerValueHome>();
|
|
pValue.copy_from(pSuccessEvaluationResult->Value());
|
|
}
|
|
|
|
com_ptr<DkmString> pDisplayName;
|
|
IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put()));
|
|
|
|
PCWSTR displayType;
|
|
if (prop.category < PropertyCategory::Value)
|
|
{
|
|
displayType = g_categoryData[(int)prop.category].displayType;
|
|
}
|
|
else
|
|
{
|
|
displayType = prop.displayType.c_str();
|
|
}
|
|
com_ptr<DkmString> pDisplayType;
|
|
IF_FAIL_RET(DkmString::Create(displayType, pDisplayType.put()));
|
|
|
|
com_ptr<DkmSuccessEvaluationResult> pVisualizedResult;
|
|
IF_FAIL_RET(DkmSuccessEvaluationResult::Create(
|
|
pParent->InspectionContext(),
|
|
pParent->StackFrame(),
|
|
pDisplayName.get(),
|
|
pSuccessEvaluationResult->FullName(),
|
|
pSuccessEvaluationResult->Flags(),
|
|
pValue.get(),
|
|
pSuccessEvaluationResult->EditableValue(),
|
|
pDisplayType.get(),
|
|
pSuccessEvaluationResult->Category(),
|
|
pSuccessEvaluationResult->Access(),
|
|
pSuccessEvaluationResult->StorageType(),
|
|
pSuccessEvaluationResult->TypeModifierFlags(),
|
|
pSuccessEvaluationResult->Address(),
|
|
pSuccessEvaluationResult->CustomUIVisualizers(),
|
|
pSuccessEvaluationResult->ExternalModules(),
|
|
DkmDataItem::Null(),
|
|
pVisualizedResult.put()
|
|
));
|
|
|
|
com_ptr<DkmChildVisualizedExpression> pChildVisualizedExpression;
|
|
IF_FAIL_RET(DkmChildVisualizedExpression::Create(
|
|
pParent->InspectionContext(),
|
|
pParent->VisualizerId(),
|
|
pParent->SourceId(),
|
|
pParent->StackFrame(),
|
|
pChildPointer.get(),
|
|
pVisualizedResult.get(),
|
|
pParent,
|
|
2,
|
|
DkmDataItem::Null(),
|
|
pChildVisualizedExpression.put()
|
|
));
|
|
|
|
if (isNonNullObject)
|
|
{
|
|
com_ptr<object_visualizer> pObjectVisualizer = make_self<object_visualizer>(pChildVisualizedExpression.get(), ObjectType::Abi);
|
|
IF_FAIL_RET(pChildVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pObjectVisualizer.get()));
|
|
}
|
|
else
|
|
{
|
|
com_ptr<property_visualizer> pPropertyVisualizer = make_self<property_visualizer>(pChildVisualizedExpression.get(), pSuccessEvaluationResult.get());
|
|
IF_FAIL_RET(pChildVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pPropertyVisualizer.get()));
|
|
}
|
|
|
|
*ppResult = pChildVisualizedExpression.detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
struct property_type
|
|
{
|
|
MethodDef get;
|
|
MethodDef set;
|
|
};
|
|
|
|
void GetInterfaceData(
|
|
Microsoft::VisualStudio::Debugger::DkmProcess* process,
|
|
coded_index<TypeDefOrRef> index,
|
|
_Inout_ std::vector<PropertyData>& propertyData,
|
|
_Out_ bool& isStringable
|
|
){
|
|
auto [type, propIid] = ResolveTypeInterface(process, index);
|
|
if (!type)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (propIid == IID_IStringable)
|
|
{
|
|
isStringable = true;
|
|
return;
|
|
}
|
|
|
|
int32_t propIndex = -1;
|
|
for (auto&& method : type.MethodList())
|
|
{
|
|
propIndex++;
|
|
|
|
auto isGetter = method.Flags().SpecialName() && starts_with(method.Name(), "get_");
|
|
if (!isGetter)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::optional<PropertyCategory> propCategory;
|
|
std::wstring propAbiType;
|
|
std::wstring propDisplayType;
|
|
|
|
auto retType = method.Signature().ReturnType();
|
|
std::visit(overloaded{
|
|
[&](ElementType type)
|
|
{
|
|
if ((ElementType::Boolean <= type) && (type <= ElementType::String))
|
|
{
|
|
propCategory = (PropertyCategory)(static_cast<std::underlying_type<ElementType>::type>(type) -
|
|
static_cast<std::underlying_type<ElementType>::type>(ElementType::Boolean));
|
|
}
|
|
else if (type == ElementType::Object)
|
|
{
|
|
//propDisplayType = L"winrt::Windows::Foundation::IInspectable";
|
|
//propCategory = PropertyCategory::Class;
|
|
//propAbiType = L"winrt::impl::inspectable_abi*";
|
|
}
|
|
},
|
|
[&](coded_index<TypeDefOrRef> const& index)
|
|
{
|
|
auto type = ResolveType(process, index);
|
|
if (!type)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto typeName = type.TypeName();
|
|
if (typeName == "GUID"sv)
|
|
{
|
|
propCategory = PropertyCategory::Guid;
|
|
}
|
|
else
|
|
{
|
|
auto ns = std::string(type.TypeNamespace());
|
|
auto name = std::string(type.TypeName());
|
|
|
|
// Map numeric type names
|
|
if (ns == "Windows.Foundation.Numerics")
|
|
{
|
|
if (name == "Matrix3x2") { name = "float3x2"; }
|
|
else if (name == "Matrix4x4") { name = "float4x4"; }
|
|
else if (name == "Plane") { name = "plane"; }
|
|
else if (name == "Quaternion") { name = "quaternion"; }
|
|
else if (name == "Vector2") { name = "float2"; }
|
|
else if (name == "Vector3") { name = "float3"; }
|
|
else if (name == "Vector4") { name = "float4"; }
|
|
}
|
|
|
|
// Types come back from winmd files with '.', need to be '::'
|
|
// Ex. Windows.Foundation.Uri needs to be Windows::Foundation::Uri
|
|
auto fullTypeName = ns + "::" + name;
|
|
wchar_t cppTypename[500];
|
|
size_t i, j;
|
|
for (i = 0, j = 0; i < (fullTypeName.length() + 1); i++, j++)
|
|
{
|
|
if (fullTypeName[i] == L'.')
|
|
{
|
|
cppTypename[j++] = L':';
|
|
cppTypename[j] = L':';
|
|
}
|
|
else
|
|
{
|
|
cppTypename[j] = fullTypeName[i];
|
|
}
|
|
}
|
|
|
|
propDisplayType = std::wstring(L"winrt::") + cppTypename;
|
|
if(get_category(type) == category::class_type)
|
|
{
|
|
propCategory = PropertyCategory::Class;
|
|
propAbiType = L"winrt::impl::inspectable_abi*";
|
|
}
|
|
else
|
|
{
|
|
propCategory = PropertyCategory::Value;
|
|
propAbiType = propDisplayType;
|
|
}
|
|
}
|
|
},
|
|
[&](GenericTypeIndex /*var*/)
|
|
{
|
|
NatvisDiagnostic(process, L"Generics not yet supported", NatvisDiagnosticLevel::Warning);
|
|
},
|
|
[&](GenericMethodTypeIndex /*var*/)
|
|
{
|
|
NatvisDiagnostic(process, L"Generics not yet supported", NatvisDiagnosticLevel::Warning);
|
|
},
|
|
[&](GenericTypeInstSig const& /*type*/)
|
|
{
|
|
NatvisDiagnostic(process, L"Generics not yet supported", NatvisDiagnosticLevel::Warning);
|
|
}
|
|
}, retType.Type().Type());
|
|
|
|
if (propCategory)
|
|
{
|
|
auto propName = method.Name().substr(4);
|
|
std::wstring propDisplayName(propName.cbegin(), propName.cend());
|
|
propertyData.push_back({ propIid, propIndex, *propCategory, propAbiType, propDisplayType, propDisplayName });
|
|
}
|
|
}
|
|
}
|
|
|
|
void object_visualizer::GetPropertyData()
|
|
{
|
|
auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome());
|
|
com_ptr<DkmPointerValueHome> pObject = valueHome.as<DkmPointerValueHome>();
|
|
auto rc = GetRuntimeClass(m_pVisualizedExpression.get(), pObject.get(), m_objectType);
|
|
if (rc.empty())
|
|
{
|
|
return;
|
|
}
|
|
auto process = m_pVisualizedExpression->RuntimeInstance()->Process();
|
|
// runtime class name is delimited by L"..."
|
|
GetTypeProperties(process, std::string_view{ rc.data() + 2, rc.length() - 3 });
|
|
}
|
|
|
|
void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name)
|
|
{
|
|
// TODO: add support for direct generic interface implementations (e.g., key_value_pair)
|
|
auto type = FindType(process, type_name);
|
|
if (!type)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (get_category(type) == category::class_type)
|
|
{
|
|
auto const& [extends_namespace, extends_name] = get_base_class_namespace_and_name(type);
|
|
if(!extends_namespace.empty() && !extends_name.empty())
|
|
{
|
|
auto base_type = std::string(extends_namespace) + "." + std::string(extends_name);
|
|
if (base_type != "System.Object")
|
|
{
|
|
GetTypeProperties(process, base_type);
|
|
}
|
|
}
|
|
auto impls = type.InterfaceImpl();
|
|
for (auto&& impl : impls)
|
|
{
|
|
GetInterfaceData(process, impl.Interface(), m_propertyData, m_isStringable);
|
|
}
|
|
}
|
|
else if (get_category(type) == category::interface_type)
|
|
{
|
|
auto impls = type.InterfaceImpl();
|
|
for (auto&& impl : impls)
|
|
{
|
|
GetInterfaceData(process, impl.Interface(), m_propertyData, m_isStringable);
|
|
}
|
|
GetInterfaceData(process, type.coded_index<TypeDefOrRef>(), m_propertyData, m_isStringable);
|
|
}
|
|
}
|
|
|
|
HRESULT object_visualizer::CreateEvaluationResult(_In_ DkmVisualizedExpression* pVisualizedExpression, _In_ ObjectType objectType, _Deref_out_ DkmEvaluationResult** ppResultObject)
|
|
{
|
|
com_ptr<object_visualizer> pObjectVisualizer = make_self<object_visualizer>(pVisualizedExpression, objectType);
|
|
|
|
IF_FAIL_RET(pVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pObjectVisualizer.get()));
|
|
|
|
IF_FAIL_RET(pObjectVisualizer->CreateEvaluationResult(ppResultObject));
|
|
|
|
IF_FAIL_RET(pVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, *ppResultObject));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef COMPONENT_DEPLOYMENT
|
|
static std::set<UINT64> g_refresh_cache;
|
|
bool requires_refresh(UINT64 address, DkmEvaluationFlags_t evalFlags)
|
|
{
|
|
auto refreshed = g_refresh_cache.find(address) != g_refresh_cache.end();
|
|
return !refreshed && ((evalFlags & DkmEvaluationFlags::EnableExtendedSideEffects) != DkmEvaluationFlags::EnableExtendedSideEffects);
|
|
}
|
|
void cache_refresh(UINT64 address)
|
|
{
|
|
g_refresh_cache.insert(address);
|
|
}
|
|
#else
|
|
bool requires_refresh(UINT64, DkmEvaluationFlags_t)
|
|
{
|
|
return false;
|
|
}
|
|
void cache_refresh(UINT64)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
HRESULT object_visualizer::CreateEvaluationResult(_Deref_out_ DkmEvaluationResult** ppResultObject)
|
|
{
|
|
com_ptr<DkmRootVisualizedExpression> pRootVisualizedExpression = m_pVisualizedExpression.as<DkmRootVisualizedExpression>();
|
|
|
|
auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome());
|
|
com_ptr<DkmPointerValueHome> pPointerValueHome = valueHome.as<DkmPointerValueHome>();
|
|
auto address = pPointerValueHome->Address();
|
|
|
|
com_ptr<DkmString> pValue;
|
|
DkmEvaluationResultFlags_t evalResultFlags = DkmEvaluationResultFlags::ReadOnly | DkmEvaluationResultFlags::Expandable;
|
|
if (requires_refresh(address, m_pVisualizedExpression->InspectionContext()->EvaluationFlags()))
|
|
{
|
|
IF_FAIL_RET(DkmString::Create(L"<Refresh to view properties>", pValue.put()));
|
|
evalResultFlags |= DkmEvaluationResultFlags::EnableExtendedSideEffectsUponRefresh | DkmEvaluationResultFlags::CanEvaluateNow;
|
|
}
|
|
else
|
|
{
|
|
cache_refresh(address);
|
|
IF_FAIL_RET(ObjectToString(m_pVisualizedExpression.get(), pPointerValueHome.get(), m_objectType, pValue));
|
|
}
|
|
|
|
com_ptr<DkmDataAddress> pAddress;
|
|
IF_FAIL_RET(DkmDataAddress::Create(m_pVisualizedExpression->StackFrame()->RuntimeInstance(), address, nullptr, pAddress.put()));
|
|
|
|
com_ptr<DkmSuccessEvaluationResult> pSuccessEvaluationResult;
|
|
IF_FAIL_RET(DkmSuccessEvaluationResult::Create(
|
|
m_pVisualizedExpression->InspectionContext(),
|
|
m_pVisualizedExpression->StackFrame(),
|
|
pRootVisualizedExpression->Name(),
|
|
pRootVisualizedExpression->FullName(),
|
|
evalResultFlags,
|
|
pValue.get(),
|
|
pValue.get(),
|
|
pRootVisualizedExpression->Type(),
|
|
DkmEvaluationResultCategory::Class,
|
|
DkmEvaluationResultAccessType::None,
|
|
DkmEvaluationResultStorageType::None,
|
|
DkmEvaluationResultTypeModifierFlags::None,
|
|
pAddress.get(),
|
|
(DkmReadOnlyCollection<DkmCustomUIVisualizerInfo*>*)nullptr,
|
|
(DkmReadOnlyCollection<DkmModuleInstance*>*)nullptr,
|
|
DkmDataItem::Null(),
|
|
pSuccessEvaluationResult.put()
|
|
));
|
|
|
|
pSuccessEvaluationResult->SetDataItem(DkmDataCreationDisposition::CreateNew, this);
|
|
|
|
*ppResultObject = (DkmEvaluationResult*)pSuccessEvaluationResult.detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT object_visualizer::GetChildren(
|
|
_In_ UINT32 InitialRequestSize,
|
|
_In_ DkmInspectionContext* pInspectionContext,
|
|
_Out_ DkmArray<DkmChildVisualizedExpression*>* pInitialChildren,
|
|
_Deref_out_ DkmEvaluationResultEnumContext** ppEnumContext)
|
|
{
|
|
// Ignore metadata errors to ensure that Raw Data is always available
|
|
if (m_propertyData.empty())
|
|
{
|
|
try
|
|
{
|
|
GetPropertyData();
|
|
}
|
|
catch (std::invalid_argument const& e)
|
|
{
|
|
std::string_view message(e.what());
|
|
NatvisDiagnostic(m_pVisualizedExpression.get(),
|
|
std::wstring(L"Exception in object_visualizer::GetPropertyData: ") +
|
|
std::wstring(message.begin(), message.end()),
|
|
NatvisDiagnosticLevel::Error, to_hresult());
|
|
}
|
|
catch (...)
|
|
{
|
|
NatvisDiagnostic(m_pVisualizedExpression.get(),
|
|
L"Exception in object_visualizer::GetPropertyData", NatvisDiagnosticLevel::Error, to_hresult());
|
|
}
|
|
}
|
|
|
|
com_ptr<DkmEvaluationResultEnumContext> pEnumContext;
|
|
IF_FAIL_RET(DkmEvaluationResultEnumContext::Create(
|
|
static_cast<uint32_t>(m_propertyData.size()),
|
|
m_pVisualizedExpression->StackFrame(),
|
|
pInspectionContext,
|
|
this,
|
|
pEnumContext.put()));
|
|
|
|
IF_FAIL_RET(GetItems(m_pVisualizedExpression.get(), pEnumContext.get(), 0, InitialRequestSize, pInitialChildren));
|
|
|
|
*ppEnumContext = pEnumContext.detach();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT object_visualizer::GetItems(
|
|
_In_ DkmVisualizedExpression* pVisualizedExpression,
|
|
_In_ DkmEvaluationResultEnumContext* /*pEnumContext*/,
|
|
_In_ UINT32 StartIndex,
|
|
_In_ UINT32 Count,
|
|
_Out_ DkmArray<DkmChildVisualizedExpression*>* pItems)
|
|
{
|
|
CAutoDkmArray<DkmChildVisualizedExpression*> resultValues;
|
|
IF_FAIL_RET(DkmAllocArray(std::min(m_propertyData.size(), size_t(Count)), &resultValues));
|
|
|
|
auto pParent = pVisualizedExpression;
|
|
auto childCount = std::min(m_propertyData.size() - StartIndex, (size_t)Count);
|
|
for(auto i = 0; i < childCount; ++i)
|
|
{
|
|
auto& prop = m_propertyData[i + (size_t)StartIndex];
|
|
com_ptr<DkmChildVisualizedExpression> pPropertyVisualized;
|
|
if(FAILED(CreateChildVisualizedExpression(prop, pParent, m_objectType, pPropertyVisualized.put())))
|
|
{
|
|
com_ptr<DkmString> pErrorMessage;
|
|
IF_FAIL_RET(DkmString::Create(L"<Property evaluation failed>", pErrorMessage.put()));
|
|
|
|
com_ptr<DkmString> pDisplayName;
|
|
IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put()));
|
|
|
|
com_ptr<DkmFailedEvaluationResult> pVisualizedResult;
|
|
IF_FAIL_RET(DkmFailedEvaluationResult::Create(
|
|
pParent->InspectionContext(),
|
|
pParent->StackFrame(),
|
|
pDisplayName.get(),
|
|
nullptr,
|
|
pErrorMessage.get(),
|
|
DkmEvaluationResultFlags::ExceptionThrown,
|
|
DkmDataItem::Null(),
|
|
pVisualizedResult.put()
|
|
));
|
|
|
|
IF_FAIL_RET(DkmChildVisualizedExpression::Create(
|
|
pParent->InspectionContext(),
|
|
pParent->VisualizerId(),
|
|
pParent->SourceId(),
|
|
pParent->StackFrame(),
|
|
nullptr,
|
|
pVisualizedResult.get(),
|
|
pParent,
|
|
2,
|
|
DkmDataItem::Null(),
|
|
pPropertyVisualized.put()
|
|
));
|
|
}
|
|
resultValues.Members[i] = pPropertyVisualized.detach();
|
|
}
|
|
|
|
*pItems = resultValues.Detach();
|
|
return S_OK;
|
|
}
|