diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 4b0a0c370..cee9e225d 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -13,9 +13,11 @@ using namespace winmd::reader; namespace { - std::vector db_files; std::unique_ptr db_cache; coded_index guid_TypeRef{}; + std::set loaded_ns; + std::list candidates_files; + bool winmd_candidates_collected{}; } coded_index FindGuidType() @@ -47,11 +49,11 @@ coded_index FindGuidType() void MetadataDiagnostic(DkmProcess* process, std::wstring const& status, std::filesystem::path const& path) { - auto path_str = path.string(); - auto message = status + std::wstring(path_str.begin(), path_str.end()); + auto message = status + path.native(); NatvisDiagnostic(process, message, NatvisDiagnosticLevel::Verbose); } +// Downloads a metadata file from a remote target. HRESULT DownloadMetadata(DkmProcess* process, std::filesystem::path const& remote_path, std::filesystem::path const& local_path) { auto conn = process->Connection(); @@ -97,15 +99,19 @@ HRESULT DownloadMetadata(DkmProcess* process, std::filesystem::path const& remot // If local file found, use it // If newer remote file found, download it to cache // If cached file found (downloaded or not), use it -bool FindMetadata(DkmProcess* process, std::filesystem::path& winmd_path) +bool FindMetadata(DkmProcess* process, std::filesystem::path& winmd_path, bool remote) { if (exists(winmd_path)) { return true; } - auto cached_path = winmd_path; - cached_path = std::filesystem::temp_directory_path(); + if (!remote) + { + return false; + } + + auto cached_path = std::filesystem::temp_directory_path(); cached_path.replace_filename(winmd_path.filename().c_str()); DownloadMetadata(process, winmd_path, cached_path); if (exists(cached_path)) @@ -117,57 +123,322 @@ bool FindMetadata(DkmProcess* process, std::filesystem::path& winmd_path) return false; } +void AddWinmdCandidate(std::filesystem::path const& candidate) +{ + // path.string() may corrupt non-ASCII paths. + std::string path_string = winrt::to_string(candidate.native()); + for (auto& c : path_string) + { + if (c >= 'A' && c <= 'Z') + { + c = c - 'A' + 'a'; + } + } + if (std::find(candidates_files.begin(), candidates_files.end(), path_string) == candidates_files.end()) + { + candidates_files.push_back(path_string); + } +} + +void CollectWinmdFromDirectoryLocal(std::filesystem::path const& directory) +{ + try + { + for (auto const& entry : std::filesystem::directory_iterator(directory)) + { + if (std::filesystem::is_regular_file(entry) && entry.path().extension() == L".winmd") + { + AddWinmdCandidate(entry.path()); + } + } + } + catch (...) + { + // If unable to read metadata, don't take down VS + } +} + +void CollectWinmdDirectory(DkmProcess* process, std::filesystem::path const& directory, bool remote) +{ + CollectWinmdFromDirectoryLocal(directory); + + if (!remote) + { + return; + } + + auto conn = process->Connection(); + com_ptr remote_dir; + com_ptr search_spec; + if (FAILED(DkmString::Create(directory.c_str(), remote_dir.put())) || + FAILED(DkmString::Create(L"*.winmd", search_spec.put()))) + { + return; + } + + DkmArray results; + if (FAILED(conn->GetFileListing(remote_dir.get(), search_spec.get(), false, &results))) + { + return; + } + + for (UINT32 i = 0; i < results.Length; ++i) + { + auto file_path = results.Members[i]->FilePath(); + if (file_path) + { + AddWinmdCandidate(file_path->Value()); + } + } +} + +void CollectSystemMetadata() +{ + std::array local{}; +#ifdef _WIN64 + ExpandEnvironmentStringsW(L"%windir%\\System32\\WinMetadata", local.data(), static_cast(local.size())); +#else + ExpandEnvironmentStringsW(L"%windir%\\SysNative\\WinMetadata", local.data(), static_cast(local.size())); +#endif + CollectWinmdFromDirectoryLocal(local.data()); +} + +bool EvaluateUInt64(DkmVisualizedExpression* pExpression, wchar_t const* expression, UINT64& value) +{ + com_ptr pEvalText; + if (FAILED(DkmString::Create(DkmSourceString(expression), pEvalText.put()))) + { + return false; + } + + auto evalFlags = DkmEvaluationFlags::TreatAsExpression + | DkmEvaluationFlags::ForceEvaluationNow + | DkmEvaluationFlags::ForceRealFuncEval; + + auto inspectionContext = pExpression->InspectionContext(); + + com_ptr pLanguageExpression; + if (FAILED(DkmLanguageExpression::Create(inspectionContext->Language(), + evalFlags, pEvalText.get(), DkmDataItem::Null(), pLanguageExpression.put()))) + { + return false; + } + + com_ptr pInspectionContext; + if ((inspectionContext->EvaluationFlags() & evalFlags) != evalFlags) + { + if (FAILED(DkmInspectionContext::Create( + inspectionContext->InspectionSession(), + inspectionContext->RuntimeInstance(), + inspectionContext->Thread(), + inspectionContext->Timeout(), + evalFlags, + inspectionContext->FuncEvalFlags(), + inspectionContext->Radix(), + inspectionContext->Language(), + inspectionContext->ReturnValue(), + pInspectionContext.put()))) + { + return false; + } + } + else + { + pInspectionContext.copy_from(inspectionContext); + } + + com_ptr pEvaluationResult; + auto hr = pExpression->EvaluateExpressionCallback(pInspectionContext.get(), pLanguageExpression.get(), + pExpression->StackFrame(), pEvaluationResult.put()); + + if (FAILED(hr) || !pEvaluationResult || pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult) + { + return false; + } + + auto pValue = pEvaluationResult.as()->Value(); + if (!pValue) + { + return false; + } + + wchar_t* text_end = nullptr; + auto text = pValue->Value(); + auto parsed = std::wcstoull(text, &text_end, 0); + if (text_end == text) + { + return false; + } + + value = parsed; + return true; +} + +// Asks the debuggee which the metadata of the references it consumes. The files are embedded as a +// semicolon separated wide string literal, and its size is embedded alongside it so that the string +// can be read out of the debuggee's memory in one exact read. +void CollectKnownMetadata(DkmVisualizedExpression* pExpression, DkmProcess* process) +{ + UINT64 address = 0; + UINT64 size = 0; + if (!EvaluateUInt64(pExpression, L"(unsigned long long)WINRT_Known_Winmds", address) || + !EvaluateUInt64(pExpression, L"(unsigned long long)WINRT_Known_Winmds_Size", size) || + !address || !size) + { + return; + } + + CAutoDkmArray stringMemory; + auto hr = process->ReadMemoryString(address, DkmReadMemoryFlags::None, sizeof(wchar_t), + static_cast(size / sizeof(wchar_t)), &stringMemory); + if (FAILED(hr)) + { + return; + } + + // The buffer includes the null terminator, which the list have no use for. + auto const characters = stringMemory.Length / sizeof(wchar_t); + std::wstring_view dir_list(reinterpret_cast(stringMemory.Members), characters - 1); + + size_t start = 0; + while (start <= dir_list.size()) + { + auto end = dir_list.find(L';', start); + if (end == std::wstring_view::npos) + { + end = dir_list.size(); + } + + if (end != start) + { + AddWinmdCandidate(std::filesystem::path(dir_list.substr(start, end - start))); + } + + start = end + 1; + } +} + +void EnsureWinmdCandidatesCollected(DkmVisualizedExpression* pExpression, WCHAR const* processPath) +{ + if (winmd_candidates_collected) + { + return; + } + winmd_candidates_collected = true; + + auto process = pExpression->RuntimeInstance()->Process(); + bool remote = (process->Connection()->Flags() & DkmTransportConnectionFlags_t::LocalComputer) == 0; + CollectKnownMetadata(pExpression, process); + CollectWinmdDirectory(process, std::filesystem::path(processPath).parent_path(), remote); + CollectSystemMetadata(); +} + +// The file that defines a type is named after the type's namespace, in lower case, so the +// namespace of a type name doubles as the name of the file to look it up in. +std::string ToLowerCasedWinmdName(std::string_view const& typeName) +{ + std::string result(typeName); + auto pos = result.rfind('.'); + if (pos == std::string::npos) + { + result.clear(); + } + else + { + result.resize(pos); + } + + for (auto& c : result) + { + if (c >= 'A' && c <= 'Z') + { + c = c - 'A' + 'a'; + } + } + return result; +} + // If type not indexed, simulate RoGetMetaDataFile's strategy for finding app-local metadata // and add to the database dynamically. RoGetMetaDataFile looks for types in the current process // so cannot be called directly. -void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_view const& typeName) +void LoadMetadata(DkmVisualizedExpression* pExpression, std::string_view const& typeName) { - auto winmd_path = path{ processPath }; - auto probe_file = std::string{ typeName }; - while (true) - { - winmd_path.replace_filename(probe_file + ".winmd"); - MetadataDiagnostic(process, L"Looking for ", winmd_path); - if (FindMetadata(process, winmd_path)) - { - MetadataDiagnostic(process, L"Loaded ", winmd_path); + auto process = pExpression->RuntimeInstance()->Process(); + auto processPath = process->Path()->Value(); + EnsureWinmdCandidatesCollected(pExpression, processPath); - auto const path_string = winmd_path.string(); + bool remote = (process->Connection()->Flags() & DkmTransportConnectionFlags_t::LocalComputer) == 0; + auto ns = ToLowerCasedWinmdName(typeName); - if (std::find(db_files.begin(), db_files.end(), path_string) == db_files.end()) + while (!ns.empty()) + { + // A namespace that has been looked at before must not be looked at again. + if (loaded_ns.insert(ns).second) + { + auto winmd_name = ns + ".winmd"; + for (auto it = candidates_files.begin(); it != candidates_files.end(); ++it) { - db_cache->add_database(path_string, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); }); - db_files.push_back(path_string); + std::filesystem::path path(*it); + if (path.filename().string() == winmd_name) + { + auto candidate = path; + candidates_files.erase(it); + if (FindMetadata(process, candidate, remote)) + { + try + { + db_cache->add_database(candidate.string(), [](TypeDef const& type) { + return type.Flags().WindowsRuntime(); + }); + } + catch (...) + { + NatvisDiagnostic(pExpression, + L"Unable to load metadata " + candidate.native(), + NatvisDiagnosticLevel::Warning); + } + } + break; + } } } - auto pos = probe_file.rfind('.'); - if (pos == std::string::npos) + auto dot = ns.rfind('.'); + if (dot == std::string::npos) { break; } - probe_file = probe_file.substr(0, pos); - } + ns.resize(dot); + } } -TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) +TypeDef FindSimpleType(DkmVisualizedExpression* pExpression, std::string_view const& typeName) { XLANG_ASSERT(typeName.find('<') == std::string_view::npos); auto type = db_cache->find(typeName); + if (type) + { + return type; + } + // If a namespace has already attempted to load the winmd, then skip it. + if (loaded_ns.count(ToLowerCasedWinmdName(typeName)) != 0) + { + NatvisDiagnostic(pExpression, + std::wstring(L"Could not find metadata for ") + std::wstring(typeName.begin(), typeName.end()), + NatvisDiagnosticLevel::Error); + return {}; + } + LoadMetadata(pExpression, typeName); + type = db_cache->find(typeName); if (!type) { - auto processPath = process->Path()->Value(); - LoadMetadata(process, processPath, typeName); - type = db_cache->find(typeName); - if (!type) - { - NatvisDiagnostic(process, - std::wstring(L"Could not find metadata for ") + std::wstring(typeName.begin(), typeName.end()), NatvisDiagnosticLevel::Error); - } + NatvisDiagnostic(pExpression, + std::wstring(L"Could not find metadata for ") + std::wstring(typeName.begin(), typeName.end()), + NatvisDiagnosticLevel::Error); } return type; } -TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) +TypeDef FindSimpleType(DkmVisualizedExpression* pExpression, std::string_view const& typeNamespace, std::string_view const& typeName) { XLANG_ASSERT(typeName.find('<') == std::string_view::npos); auto type = db_cache->find(typeNamespace, typeName); @@ -176,7 +447,7 @@ TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespac std::string fullName(typeNamespace); fullName.append("."); fullName.append(typeName); - FindSimpleType(process, fullName); + return FindSimpleType(pExpression, fullName); } return type; } @@ -198,7 +469,7 @@ std::vector ParseTypeName(std::string_view name) } template sent> -TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) +TypeSig ResolveGenericTypePart(DkmVisualizedExpression* pExpression, iter& it, sent const& end) { constexpr std::pair elementNames[] = { {"Boolean", ElementType::Boolean}, @@ -228,7 +499,7 @@ TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) return TypeSig{ FindGuidType() }; } - TypeDef type = FindSimpleType(process, partName); + TypeDef type = FindSimpleType(pExpression, partName); auto tickPos = partName.rfind('`'); if (tickPos == partName.npos) { @@ -240,55 +511,39 @@ TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) std::vector genericArgs; for (int i = 0; i < paramCount; ++i) { - genericArgs.push_back(ResolveGenericTypePart(process, ++it, end)); + genericArgs.push_back(ResolveGenericTypePart(pExpression, ++it, end)); } return TypeSig{ GenericTypeInstSig{ type.coded_index(), std::move(genericArgs) } }; } -TypeSig ResolveGenericType(DkmProcess* process, std::string_view genericName) +TypeSig ResolveGenericType(DkmVisualizedExpression* pExpression, std::string_view genericName) { auto parts = ParseTypeName(genericName); auto begin = parts.begin(); - return ResolveGenericTypePart(process, begin, parts.end()); + return ResolveGenericTypePart(pExpression, begin, parts.end()); } -TypeSig FindType(DkmProcess* process, std::string_view const& typeName) +TypeSig FindType(DkmVisualizedExpression* pExpression, std::string_view const& typeName) { auto paramIndex = typeName.find('<'); if (paramIndex == std::string_view::npos) { - return TypeSig{ FindSimpleType(process, typeName).coded_index() }; + auto type = FindSimpleType(pExpression, typeName); + if (!type) + { + return TypeSig{ ElementType::End }; + } + return TypeSig{ type.coded_index() }; } else { - return ResolveGenericType(process, typeName); + return ResolveGenericType(pExpression, typeName); } } cppwinrt_visualizer::cppwinrt_visualizer() { - try - { - std::array local{}; -#ifdef _WIN64 - ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast(local.size())); -#else - ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast(local.size())); -#endif - for (auto&& file : std::filesystem::directory_iterator(local.data())) - { - if (std::filesystem::is_regular_file(file)) - { - db_files.push_back(file.path().string()); - } - } - db_cache.reset(new cache(db_files, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); })); - } - catch (...) - { - // If unable to read metadata, don't take down VS - } - + db_cache = std::make_unique(); // Log an event for telemetry purposes when the visualizer is brought online com_ptr eventName; if SUCCEEDED(DkmString::Create(DkmSourceString(L"vs/vc/diagnostics/cppwinrtvisualizer/objectconstructed"), eventName.put())) @@ -305,7 +560,9 @@ cppwinrt_visualizer::~cppwinrt_visualizer() { ClearTypeResolver(); guid_TypeRef = {}; - db_files.clear(); + loaded_ns.clear(); + winmd_candidates_collected = {}; + candidates_files.clear(); db_cache.reset(); } diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index d953a1c03..88c114a9b 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -139,7 +139,7 @@ static HRESULT EvaluatePropertyExpression( com_ptr pInspectionContext; if ( (pExpression->InspectionContext()->EvaluationFlags() & evalFlags) != evalFlags) { - DkmInspectionContext::Create( + IF_FAIL_RET(DkmInspectionContext::Create( inspectionContext->InspectionSession(), inspectionContext->RuntimeInstance(), inspectionContext->Thread(), @@ -150,7 +150,7 @@ static HRESULT EvaluatePropertyExpression( inspectionContext->Language(), inspectionContext->ReturnValue(), pInspectionContext.put() - ); + )); } else { @@ -341,11 +341,12 @@ static HRESULT CreateChildVisualizedExpression( } std::optional GetPropertyCategory( - Microsoft::VisualStudio::Debugger::DkmProcess* process, + DkmVisualizedExpression* pExpression, TypeSig const& owningType, TypeSig const& propertyType ) { + auto process = pExpression->RuntimeInstance()->Process(); std::optional propCategory; if (auto pElementType = std::get_if(&propertyType.Type())) { @@ -361,7 +362,7 @@ std::optional GetPropertyCategory( } else if (auto pIndex = std::get_if>(&propertyType.Type())) { - auto type = ResolveType(process, *pIndex); + auto type = ResolveType(pExpression, *pIndex); if (type) { if (get_category(type) == category::class_type || get_category(type) == category::interface_type) @@ -393,7 +394,7 @@ std::optional GetPropertyCategory( { auto const& index = pGenericIndex->index; auto const& genericArgs = pOwner->GenericArgs(); - propCategory = GetPropertyCategory(process, owningType, genericArgs.first[index]); + propCategory = GetPropertyCategory(pExpression, owningType, genericArgs.first[index]); } else { @@ -618,12 +619,12 @@ std::wstring string_to_wstring(std::string_view const& str) } void GetInterfaceData( - Microsoft::VisualStudio::Debugger::DkmProcess* process, + DkmVisualizedExpression* pExpression, TypeSig const& typeSig, _Inout_ std::vector& propertyData, _Out_ bool& isStringable ){ - auto [type, propIid] = ResolveTypeInterface(process, typeSig); + auto [type, propIid] = ResolveTypeInterface(pExpression, typeSig); if (!type) { @@ -647,7 +648,7 @@ void GetInterfaceData( continue; } - std::optional propCategory = GetPropertyCategory(process, typeSig, method.Signature().ReturnType().Type()); + std::optional propCategory = GetPropertyCategory(pExpression, typeSig, method.Signature().ReturnType().Type()); if (propCategory) { std::wstring propAbiType; @@ -688,9 +689,8 @@ void object_visualizer::GetPropertyData() { 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 }); + GetTypeProperties(m_pVisualizedExpression.get(), std::string_view{ rc.data() + 2, rc.length() - 3 }); } GenericTypeInstSig ReplaceGenericIndices(GenericTypeInstSig const& sig, std::vector const& genericArgs) @@ -728,18 +728,18 @@ TypeSig ExpandInterfaceImplForType(coded_index impl, TypeSig const return TypeSig{ impl }; } -void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name) +void object_visualizer::GetTypeProperties(DkmVisualizedExpression* pExpression, 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); + auto typeSig = FindType(pExpression, type_name); TypeDef type{}; if (auto const* index = std::get_if>(&typeSig.Type())) { - type = ResolveType(process, *index); + type = ResolveType(pExpression, *index); } else if (auto const* genericInst = std::get_if(&typeSig.Type())) { - type = ResolveType(process, genericInst->GenericType()); + type = ResolveType(pExpression, genericInst->GenericType()); } if (!type) @@ -755,13 +755,13 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm auto base_type = std::string(extends_namespace) + "." + std::string(extends_name); if (base_type != "System.Object") { - GetTypeProperties(process, base_type); + GetTypeProperties(pExpression, base_type); } } auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { - GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); + GetInterfaceData(pExpression, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } } else if (get_category(type) == category::interface_type) @@ -769,9 +769,9 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { - GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); + GetInterfaceData(pExpression, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } - GetInterfaceData(process, typeSig, m_propertyData, m_isStringable); + GetInterfaceData(pExpression, typeSig, m_propertyData, m_isStringable); } } diff --git a/natvis/object_visualizer.h b/natvis/object_visualizer.h index 6d1a0f00c..f89544fd5 100644 --- a/natvis/object_visualizer.h +++ b/natvis/object_visualizer.h @@ -78,7 +78,7 @@ object_visualizer : winrt::implements private: void GetPropertyData(); - void GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name); + void GetTypeProperties(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pExpression, std::string_view const& type_name); winrt::com_ptr m_pVisualizedExpression; ObjectType m_objectType; std::vector m_propertyData; diff --git a/natvis/pch.h b/natvis/pch.h index 3de6807b3..17fad9f36 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -93,24 +94,24 @@ inline bool starts_with(std::string_view const& value, std::string_view const& m return 0 == value.compare(0, match.size(), match); } -winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); -winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); -winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); +winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pExpression, std::string_view const& typeName); +winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pExpression, std::string_view const& typeNamespace, std::string_view const& typeName); +winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pExpression, std::string_view const& typeName); -inline winmd::reader::TypeDef ResolveType(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::coded_index index) noexcept +inline winmd::reader::TypeDef ResolveType(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pExpression, winmd::reader::coded_index index) { switch (index.type()) { case winmd::reader::TypeDefOrRef::TypeDef: return index.TypeDef(); case winmd::reader::TypeDefOrRef::TypeRef: - return FindSimpleType(process, index.TypeRef().TypeNamespace(), index.TypeRef().TypeName()); + return FindSimpleType(pExpression, index.TypeRef().TypeNamespace(), index.TypeRef().TypeName()); default: //case TypeDefOrRef::TypeSpec: return winmd::reader::find_required(index.TypeSpec().Signature(). GenericTypeInst().GenericType().TypeRef()); } } -std::pair ResolveTypeInterface(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::TypeSig const& typeSig); +std::pair ResolveTypeInterface(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pExpression, winmd::reader::TypeSig const& typeSig); void ClearTypeResolver(); diff --git a/natvis/type_resolver.cpp b/natvis/type_resolver.cpp index de8af71f4..cdc8ab590 100644 --- a/natvis/type_resolver.cpp +++ b/natvis/type_resolver.cpp @@ -4,6 +4,7 @@ using namespace winrt; using namespace winmd::reader; using namespace std::literals; using namespace Microsoft::VisualStudio::Debugger; +using namespace Microsoft::VisualStudio::Debugger::Evaluation; static std::map, std::pair> _cache; @@ -278,7 +279,7 @@ static guid generate_guid(GenericTypeInstSig const& type) return set_named_guid_fields(endian_swap(to_guid(calculate_sha1(buffer)))); } -std::pair ResolveTypeInterface(DkmProcess* process, winmd::reader::TypeSig const& typeSig) +std::pair ResolveTypeInterface(DkmVisualizedExpression* pExpression, winmd::reader::TypeSig const& typeSig) { coded_index index; if (auto ptrIndex = std::get_if>(&typeSig.Type())) @@ -290,7 +291,7 @@ std::pair ResolveTypeInterface(DkmProcess* process, winmd return found->second; } - TypeDef type = ResolveType(process, index); + TypeDef type = ResolveType(pExpression, index); if (!type) { return {}; @@ -307,7 +308,7 @@ std::pair ResolveTypeInterface(DkmProcess* process, winmd { index = ptrGeneric->GenericType(); auto guid = format_guid(generate_guid(*ptrGeneric)); - return { ResolveType(process, index), guid }; + return { ResolveType(pExpression, index), guid }; } return {}; }; diff --git a/nuget/Microsoft.Windows.CppWinRT.targets b/nuget/Microsoft.Windows.CppWinRT.targets index a297258f3..11189eac1 100644 --- a/nuget/Microsoft.Windows.CppWinRT.targets +++ b/nuget/Microsoft.Windows.CppWinRT.targets @@ -902,6 +902,46 @@ $(XamlMetaDataProviderPch) + + + + <_CppWinRTNatvisWinmdFileItemsRaw Include="@(CppWinRTPlatformWinMDReferences->'%(RootDir)%(Directory)%(Filename)%(Extension)')" /> + <_CppWinRTNatvisWinmdFileItemsRaw Include="@(CppWinRTDirectWinMDReferences->'%(RootDir)%(Directory)%(Filename)%(Extension)')" /> + <_CppWinRTNatvisWinmdFileItemsRaw Include="@(CppWinRTDynamicProjectWinMDReferences->'%(RootDir)%(Directory)%(Filename)%(Extension)')" /> + <_CppWinRTNatvisWinmdFileItemsRaw Include="@(CppWinRTStaticProjectWinMDReferences->'%(RootDir)%(Directory)%(Filename)%(Extension)')" /> + <_CppWinRTNatvisWinmdFileItemsRaw Condition="'$(CppWinRTProjectWinMD)' != ''" Include="$([System.IO.Path]::GetFullPath('$(CppWinRTProjectWinMD)'))" /> + <_CppWinRTNatvisWinmdFileItems Include="@(_CppWinRTNatvisWinmdFileItemsRaw->Distinct())" /> + + + + <_CppWinRTNatvisWinmdFilesJoined>@(_CppWinRTNatvisWinmdFileItems->'%(Identity)', ';') + <_CppWinRTNatvisWinmdFilesEscaped>$([System.String]::Copy('$(_CppWinRTNatvisWinmdFilesJoined)').Replace('\','\\').Replace(';','%3B')) + <_CppWinRTNatvisWinmdFiles Condition="'$(_CppWinRTNatvisWinmdFilesEscaped)' != ''">L"$(_CppWinRTNatvisWinmdFilesEscaped)" + + + + + + + + %(ClCompile.PreprocessorDefinitions);WINRT_KNOWN_WINMDS=$(_CppWinRTNatvisWinmdFiles) + + + + CompileAsCppModule true NotUsing + + %(ClCompile.PreprocessorDefinitions);WINRT_KNOWN_WINMDS=$(_CppWinRTNatvisWinmdFiles) diff --git a/strings/base_natvis.h b/strings/base_natvis.h index 9e78563cb..d2ec75aef 100644 --- a/strings/base_natvis.h +++ b/strings/base_natvis.h @@ -100,4 +100,21 @@ decltype(winrt::impl::natvis::get_val) & WINRT_get_val = winrt::impl::natvis::ge #pragma comment(linker, "/include:WINRT_get_val") #endif +#ifdef WINRT_KNOWN_WINMDS +extern "C" +__declspec(selectany) +wchar_t const* WINRT_Known_Winmds = WINRT_KNOWN_WINMDS; +extern "C" +__declspec(selectany) +unsigned long WINRT_Known_Winmds_Size = sizeof(WINRT_KNOWN_WINMDS); + +#ifdef _M_IX86 +#pragma comment(linker, "/include:_WINRT_Known_Winmds") +#pragma comment(linker, "/include:_WINRT_Known_Winmds_Size") +#else +#pragma comment(linker, "/include:WINRT_Known_Winmds") +#pragma comment(linker, "/include:WINRT_Known_Winmds_Size") +#endif +#endif + #endif