#include "pch.h" #include #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 pUserMessageText; IF_FAIL_RET(DkmString::Create(DkmSourceString(userMessage.c_str()), pUserMessageText.put())); com_ptr 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& 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 (IsBuiltIn(prop.category)) { 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 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 pLanguageExpression; IF_FAIL_RET(DkmLanguageExpression::Create( inspectionContext->Language(), evalFlags, pEvalText.get(), DkmDataItem::Null(), pLanguageExpression.put() )); com_ptr 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& pValue ) { com_ptr pEvaluationResult; IF_FAIL_RET(EvaluatePropertyExpression(prop, pExpression, pObject, objectType, pEvaluationResult)); if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult) { return E_FAIL; } com_ptr pSuccessEvaluationResult = pEvaluationResult.as(); 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 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& 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"", 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"", pValue.put()); } static HRESULT CreateChildVisualizedExpression( _In_ PropertyData const& prop, _In_ DkmVisualizedExpression* pParent, ObjectType objectType, _Deref_out_ DkmChildVisualizedExpression** ppResult ) { *ppResult = nullptr; com_ptr pEvaluationResult; auto valueHome = make_com_ptr(pParent->ValueHome()); com_ptr pParentPointer = valueHome.as(); IF_FAIL_RET(EvaluatePropertyExpression(prop, pParent, pParentPointer.get(), objectType, pEvaluationResult)); if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult) { return E_FAIL; } com_ptr pSuccessEvaluationResult = pEvaluationResult.as(); com_ptr pValue; com_ptr 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 expressionValueHome = make_com_ptr(pParent->ValueHome()); pChildPointer = expressionValueHome.as(); pValue.copy_from(pSuccessEvaluationResult->Value()); } com_ptr pDisplayName; IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put())); PCWSTR displayType; if (IsBuiltIn(prop.category)) { displayType = g_categoryData[(int)prop.category].displayType; } else { displayType = prop.displayType.c_str(); } com_ptr pDisplayType; IF_FAIL_RET(DkmString::Create(displayType, pDisplayType.put())); com_ptr 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 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 pObjectVisualizer = make_self(pChildVisualizedExpression.get(), ObjectType::Abi); IF_FAIL_RET(pChildVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pObjectVisualizer.get())); } else { com_ptr pPropertyVisualizer = make_self(pChildVisualizedExpression.get(), pSuccessEvaluationResult.get()); IF_FAIL_RET(pChildVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pPropertyVisualizer.get())); } *ppResult = pChildVisualizedExpression.detach(); return S_OK; } std::optional GetPropertyCategory( Microsoft::VisualStudio::Debugger::DkmProcess* process, TypeSig const& owningType, TypeSig const& propertyType ) { std::optional propCategory; if (auto pElementType = std::get_if(&propertyType.Type())) { if ((ElementType::Boolean <= *pElementType) && (*pElementType <= ElementType::String)) { propCategory = (PropertyCategory)(static_cast::type>(*pElementType) - static_cast::type>(ElementType::Boolean)); } else if (*pElementType == ElementType::Object) { // result = PropertyCategory::Class; } } else if (auto pIndex = std::get_if>(&propertyType.Type())) { auto type = ResolveType(process, *pIndex); if (type) { if (get_category(type) == category::class_type || get_category(type) == category::interface_type) { propCategory = PropertyCategory::Class; } else { propCategory = PropertyCategory::Value; } } else if (pIndex->type() == TypeDefOrRef::TypeRef) { auto typeRef = pIndex->TypeRef(); if (typeRef.TypeNamespace() == "System" && typeRef.TypeName() == "Guid") { propCategory = PropertyCategory::Guid; } } } else if (auto pGenericInst = std::get_if(&propertyType.Type())) { XLANG_ASSERT(get_category(ResolveType(process, pGenericInst->GenericType())) == category::interface_type); propCategory = PropertyCategory::Class; } else if (auto pGenericIndex = std::get_if(&propertyType.Type())) { if (auto pOwner = std::get_if(&owningType.Type())) { auto const& index = pGenericIndex->index; auto const& genericArgs = pOwner->GenericArgs(); propCategory = GetPropertyCategory(process, owningType, genericArgs.first[index]); } else { NatvisDiagnostic(process, L"Can't resolve GenericTypeIndex property on non-generic Type", NatvisDiagnosticLevel::Warning); } } else { NatvisDiagnostic(process, L"Unsupported TypeSig encountered", NatvisDiagnosticLevel::Warning); } return propCategory; } struct writer { std::vector generic_params; std::string result; void write(char c) { result.push_back(c); } void write(std::string_view const& str) { for (auto c : str) { if (c == '.') { write(':'); write(':'); } else if (c != '`') { write(c); } else { return; } } } void write(ElementType type) { switch (type) { case ElementType::Boolean: write("bool"); break; case ElementType::Char: write("wchar_t"); break; case ElementType::I1: write("int8_t"); break; case ElementType::U1: write("uint8_t"); break; case ElementType::I2: write("int16_t"); break; case ElementType::U2: write("uint16_t"); break; case ElementType::I4: write("int32_t"); break; case ElementType::U4: write("uint32_t"); break; case ElementType::I8: write("int64_t"); break; case ElementType::U8: write("uint64_t"); break; case ElementType::R4: write("float"); break; case ElementType::R8: write("double"); break; case ElementType::String: write("winrt::hstring"); break; case ElementType::Object: write("winrt::Windows::Foundation::IInspectable"); break; default: XLANG_ASSERT(false); break; }; } void write_namespace_and_type(std::string_view ns, std::string_view name) { if (ns == "System") { if (name == "Guid") { ns = ""; name = "guid"; } } else if (ns == "Windows.Foundation") { if (name == "EventRegistrationToken") { ns = ""; name = "event_token"; } else if (name == "HResult") { ns = ""; name = "hresult"; } } else 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 = "quarternion"; } else if (name == "Vector2") { name = "float2"; } else if (name == "Vector3") { name = "float3"; } else if (name == "Vector4") { name = "float4"; } } write("winrt::"); if (!ns.empty()) { write(ns); write("::"); } write(name); } void write(TypeRef const& type) { write_namespace_and_type(type.TypeNamespace(), type.TypeName()); } void write(TypeDef const& type) { write_namespace_and_type(type.TypeNamespace(), type.TypeName()); } void write(TypeSpec const& type) { write(type.Signature().GenericTypeInst()); } void write(coded_index type) { switch (type.type()) { case TypeDefOrRef::TypeDef: write(type.TypeDef()); break; case TypeDefOrRef::TypeRef: write(type.TypeRef()); break; case TypeDefOrRef::TypeSpec: write(type.TypeSpec()); break; } } void write(GenericTypeInstSig const& type) { write(type.GenericType()); bool first = true; write("<"); for (auto&& elem : type.GenericArgs()) { if (first) { first = false; } else { write(", "); } write(elem); } write(">"); } void write(GenericTypeIndex const& var) { write(generic_params[var.index]); } void write(GenericMethodTypeIndex const&) { // Nothing } void write(TypeSig const& type) { std::visit([this](auto&& arg) { write(arg); }, type.Type()); } }; std::wstring string_to_wstring(std::string_view const& str) { int const size = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), nullptr, 0); if (size == 0) { return {}; } std::wstring result(size, L'?'); [[maybe_unused]] auto size_result = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), result.data(), size); XLANG_ASSERT(size == size_result); return result; } void GetInterfaceData( Microsoft::VisualStudio::Debugger::DkmProcess* process, TypeSig const& typeSig, _Inout_ std::vector& propertyData, _Out_ bool& isStringable ){ auto [type, propIid] = ResolveTypeInterface(process, typeSig); 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 propCategory = GetPropertyCategory(process, typeSig, method.Signature().ReturnType().Type()); if (propCategory) { std::wstring propAbiType; std::wstring propDisplayType; if (!IsBuiltIn(*propCategory)) { writer writer; if (auto pGenericTypeInst = std::get_if(&typeSig.Type())) { auto const& genericArgs = pGenericTypeInst->GenericArgs(); writer.generic_params.assign(genericArgs.first, genericArgs.second); } writer.write(method.Signature().ReturnType().Type()); propDisplayType = string_to_wstring(writer.result); if (*propCategory == PropertyCategory::Class) { propAbiType = L"winrt::impl::inspectable_abi*"; } else { propAbiType = propDisplayType; } } auto propName = method.Name().substr(4); propertyData.emplace_back(propIid, propIndex, *propCategory, std::move(propAbiType), std::move(propDisplayType), string_to_wstring(propName)); } } } void object_visualizer::GetPropertyData() { auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome()); com_ptr pObject = valueHome.as(); 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 }); } GenericTypeInstSig ReplaceGenericIndices(GenericTypeInstSig const& sig, std::vector const& genericArgs) { std::vector replacementArgs; for (auto&& arg : sig.GenericArgs()) { if (auto pGenericSig = std::get_if(&arg.Type())) { replacementArgs.emplace_back(ReplaceGenericIndices(*pGenericSig, genericArgs)); } else if (auto pGenericIndex = std::get_if(&arg.Type())) { replacementArgs.push_back(genericArgs[pGenericIndex->index]); } else { replacementArgs.push_back(arg); } } return GenericTypeInstSig{ sig.GenericType(), std::move(replacementArgs) }; } TypeSig ExpandInterfaceImplForType(coded_index impl, TypeSig const& type) { if (auto pGenericInst = std::get_if(&type.Type())) { if (impl.type() == TypeDefOrRef::TypeSpec) { auto const& genericArgs = pGenericInst->GenericArgs(); auto newSig = ReplaceGenericIndices(impl.TypeSpec().Signature().GenericTypeInst(), std::vector{ genericArgs.first, genericArgs.second }); return TypeSig{ newSig }; } } return TypeSig{ impl }; } 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 typeSig = FindType(process, type_name); TypeDef type{}; if (auto const* index = std::get_if>(&typeSig.Type())) { type = ResolveType(process, *index); } else if (auto const* genericInst = std::get_if(&typeSig.Type())) { type = ResolveType(process, genericInst->GenericType()); } 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, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } } else if (get_category(type) == category::interface_type) { auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } GetInterfaceData(process, typeSig, m_propertyData, m_isStringable); } } HRESULT object_visualizer::CreateEvaluationResult(_In_ DkmVisualizedExpression* pVisualizedExpression, _In_ ObjectType objectType, _Deref_out_ DkmEvaluationResult** ppResultObject) { com_ptr pObjectVisualizer = make_self(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 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 pRootVisualizedExpression = m_pVisualizedExpression.as(); auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome()); com_ptr pPointerValueHome = valueHome.as(); auto address = pPointerValueHome->Address(); com_ptr pValue; DkmEvaluationResultFlags_t evalResultFlags = DkmEvaluationResultFlags::ReadOnly | DkmEvaluationResultFlags::Expandable; if (requires_refresh(address, m_pVisualizedExpression->InspectionContext()->EvaluationFlags())) { IF_FAIL_RET(DkmString::Create(L"", 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 pAddress; IF_FAIL_RET(DkmDataAddress::Create(m_pVisualizedExpression->StackFrame()->RuntimeInstance(), address, nullptr, pAddress.put())); com_ptr 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*)nullptr, (DkmReadOnlyCollection*)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* 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 pEnumContext; IF_FAIL_RET(DkmEvaluationResultEnumContext::Create( static_cast(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* pItems) { CAutoDkmArray 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(size_t i = 0; i < childCount; ++i) { auto& prop = m_propertyData[i + (size_t)StartIndex]; com_ptr pPropertyVisualized; if(FAILED(CreateChildVisualizedExpression(prop, pParent, m_objectType, pPropertyVisualized.put()))) { com_ptr pErrorMessage; IF_FAIL_RET(DkmString::Create(L"", pErrorMessage.put())); com_ptr pDisplayName; IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put())); com_ptr 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; }