Skip to content

Commit e4c0b26

Browse files
authored
Allow visualization of properties on generic types (#1472)
1 parent f2a7ffb commit e4c0b26

File tree

8 files changed

+526
-142
lines changed

8 files changed

+526
-142
lines changed

natvis/cppwinrt_visualizer.cpp

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,39 @@ using namespace std::filesystem;
1111
using namespace winrt;
1212
using namespace winmd::reader;
1313

14-
std::vector<std::string> db_files;
15-
std::unique_ptr<cache> db_cache;
14+
namespace
15+
{
16+
std::vector<std::string> db_files;
17+
std::unique_ptr<cache> db_cache;
18+
coded_index<TypeDefOrRef> guid_TypeRef{};
19+
}
20+
21+
coded_index<TypeDefOrRef> FindGuidType()
22+
{
23+
if (!guid_TypeRef)
24+
{
25+
// There is no definitive TypeDef for System.Guid. But there are a variety of TypeRefs scattered about
26+
// This one should be relatively quick to find
27+
auto pv = db_cache->find("Windows.Foundation", "IPropertyValue");
28+
for (auto&& method : pv.MethodList())
29+
{
30+
if (method.Name() == "GetGuid")
31+
{
32+
auto const& sig = method.Signature();
33+
auto const& type = sig.ReturnType().Type().Type();
34+
XLANG_ASSERT(std::holds_alternative<coded_index<TypeDefOrRef>>(type));
35+
if (std::holds_alternative<coded_index<TypeDefOrRef>>(type))
36+
{
37+
guid_TypeRef = std::get<coded_index<TypeDefOrRef>>(type);
38+
XLANG_ASSERT(guid_TypeRef.type() == TypeDefOrRef::TypeRef);
39+
XLANG_ASSERT(guid_TypeRef.TypeRef().TypeNamespace() == "System");
40+
XLANG_ASSERT(guid_TypeRef.TypeRef().TypeName() == "Guid");
41+
}
42+
}
43+
}
44+
}
45+
return guid_TypeRef;
46+
}
1647

1748
void MetadataDiagnostic(DkmProcess* process, std::wstring const& status, std::filesystem::path const& path)
1849
{
@@ -118,8 +149,9 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie
118149
}
119150
}
120151

121-
TypeDef FindType(DkmProcess* process, std::string_view const& typeName)
152+
TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName)
122153
{
154+
XLANG_ASSERT(typeName.find('<') == std::string_view::npos);
123155
auto type = db_cache->find(typeName);
124156
if (!type)
125157
{
@@ -135,19 +167,104 @@ TypeDef FindType(DkmProcess* process, std::string_view const& typeName)
135167
return type;
136168
}
137169

138-
TypeDef FindType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName)
170+
TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName)
139171
{
172+
XLANG_ASSERT(typeName.find('<') == std::string_view::npos);
140173
auto type = db_cache->find(typeNamespace, typeName);
141174
if (!type)
142175
{
143176
std::string fullName(typeNamespace);
144177
fullName.append(".");
145178
fullName.append(typeName);
146-
FindType(process, fullName);
179+
FindSimpleType(process, fullName);
147180
}
148181
return type;
149182
}
150183

184+
std::vector<std::string> ParseTypeName(std::string_view name)
185+
{
186+
DWORD count;
187+
HSTRING* parts;
188+
auto wide_name = winrt::to_hstring(name);
189+
winrt::check_hresult(::RoParseTypeName(static_cast<HSTRING>(get_abi(wide_name)), &count, &parts));
190+
191+
winrt::com_array<winrt::hstring> wide_parts{ parts, count, winrt::take_ownership_from_abi };
192+
std::vector<std::string> result;
193+
for (auto&& part : wide_parts)
194+
{
195+
result.push_back(winrt::to_string(part));
196+
}
197+
return result;
198+
}
199+
200+
template <std::input_iterator iter, std::sentinel_for<iter> sent>
201+
TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end)
202+
{
203+
constexpr std::pair<std::string_view, ElementType> elementNames[] = {
204+
{"Boolean", ElementType::Boolean},
205+
{"Int8", ElementType::I1},
206+
{"Int16", ElementType::I2},
207+
{"Int32", ElementType::I4},
208+
{"Int64", ElementType::I8},
209+
{"UInt8", ElementType::U1},
210+
{"UInt16", ElementType::U2},
211+
{"UInt32", ElementType::U4},
212+
{"UInt64", ElementType::U8},
213+
{"Single", ElementType::R4},
214+
{"Double", ElementType::R8},
215+
{"String", ElementType::String},
216+
{"Char16", ElementType::Char},
217+
{"Object", ElementType::Object}
218+
};
219+
std::string_view partName = *it;
220+
auto basic_type_pos = std::find_if(std::begin(elementNames), std::end(elementNames), [&partName](auto&& elem) { return elem.first == partName; });
221+
if (basic_type_pos != std::end(elementNames))
222+
{
223+
return TypeSig{ basic_type_pos->second };
224+
}
225+
226+
if (partName == "Guid")
227+
{
228+
return TypeSig{ FindGuidType() };
229+
}
230+
231+
TypeDef type = FindSimpleType(process, partName);
232+
auto tickPos = partName.rfind('`');
233+
if (tickPos == partName.npos)
234+
{
235+
return TypeSig{ type.coded_index<TypeDefOrRef>() };
236+
}
237+
238+
int paramCount = 0;
239+
std::from_chars(partName.data() + tickPos + 1, partName.data() + partName.size(), paramCount);
240+
std::vector<TypeSig> genericArgs;
241+
for (int i = 0; i < paramCount; ++i)
242+
{
243+
genericArgs.push_back(ResolveGenericTypePart(process, ++it, end));
244+
}
245+
return TypeSig{ GenericTypeInstSig{ type.coded_index<TypeDefOrRef>(), std::move(genericArgs) } };
246+
}
247+
248+
TypeSig ResolveGenericType(DkmProcess* process, std::string_view genericName)
249+
{
250+
auto parts = ParseTypeName(genericName);
251+
auto begin = parts.begin();
252+
return ResolveGenericTypePart(process, begin, parts.end());
253+
}
254+
255+
TypeSig FindType(DkmProcess* process, std::string_view const& typeName)
256+
{
257+
auto paramIndex = typeName.find('<');
258+
if (paramIndex == std::string_view::npos)
259+
{
260+
return TypeSig{ FindSimpleType(process, typeName).coded_index<TypeDefOrRef>() };
261+
}
262+
else
263+
{
264+
return ResolveGenericType(process, typeName);
265+
}
266+
}
267+
151268
cppwinrt_visualizer::cppwinrt_visualizer()
152269
{
153270
try
@@ -187,13 +304,14 @@ cppwinrt_visualizer::cppwinrt_visualizer()
187304
cppwinrt_visualizer::~cppwinrt_visualizer()
188305
{
189306
ClearTypeResolver();
307+
guid_TypeRef = {};
190308
db_files.clear();
191309
db_cache.reset();
192310
}
193311

194312
HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression(
195313
_In_ DkmVisualizedExpression* pVisualizedExpression,
196-
_Deref_out_ DkmEvaluationResult** ppResultObject
314+
_COM_Outptr_result_maybenull_ DkmEvaluationResult** ppResultObject
197315
)
198316
{
199317
try
@@ -233,6 +351,7 @@ HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression(
233351
// unrecognized type
234352
NatvisDiagnostic(pVisualizedExpression,
235353
std::wstring(L"Unrecognized type: ") + (LPWSTR)bstrTypeName, NatvisDiagnosticLevel::Error);
354+
*ppResultObject = nullptr;
236355
return S_OK;
237356
}
238357

natvis/cppwinrt_visualizer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ struct cppwinrt_visualizer : winrt::implements<cppwinrt_visualizer,
88

99
STDMETHOD(EvaluateVisualizedExpression)(
1010
_In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression,
11-
_Deref_out_ Microsoft::VisualStudio::Debugger::Evaluation::DkmEvaluationResult** ppResultObject
11+
_COM_Outptr_result_maybenull_ Microsoft::VisualStudio::Debugger::Evaluation::DkmEvaluationResult** ppResultObject
1212
);
1313
STDMETHOD(UseDefaultEvaluationBehavior)(
1414
_In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression,

natvis/cppwinrtvisualizer.vcxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<Import Project="packages\Microsoft.Windows.WinMD.1.0.210629.2\build\native\Microsoft.Windows.WinMD.props" Condition="Exists('packages\Microsoft.Windows.WinMD.1.0.210629.2\build\native\Microsoft.Windows.WinMD.props')" />
3+
<Import Project="packages\Microsoft.Windows.WinMD.1.0.250131.1\build\native\Microsoft.Windows.WinMD.props" Condition="Exists('packages\Microsoft.Windows.WinMD.1.0.250131.1\build\native\Microsoft.Windows.WinMD.props')" />
44
<ItemGroup Label="ProjectConfigurations">
55
<ProjectConfiguration Include="Debug|ARM64">
66
<Configuration>Debug</Configuration>
@@ -321,6 +321,6 @@
321321
</PropertyGroup>
322322
<Error Condition="!Exists('packages\Microsoft.VSSDK.Debugger.VSDConfigTool.16.0.2012201-preview\build\Microsoft.VSSDK.Debugger.VSDConfigTool.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.VSSDK.Debugger.VSDConfigTool.16.0.2012201-preview\build\Microsoft.VSSDK.Debugger.VSDConfigTool.targets'))" />
323323
<Error Condition="!Exists('packages\Microsoft.VSSDK.Debugger.VSDebugEng.16.0.2012201-preview\Microsoft.VSSDK.Debugger.VSDebugEng.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.VSSDK.Debugger.VSDebugEng.16.0.2012201-preview\Microsoft.VSSDK.Debugger.VSDebugEng.targets'))" />
324-
<Error Condition="!Exists('packages\Microsoft.Windows.WinMD.1.0.210629.2\build\native\Microsoft.Windows.WinMD.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Windows.WinMD.1.0.210629.2\build\native\Microsoft.Windows.WinMD.props'))" />
324+
<Error Condition="!Exists('packages\Microsoft.Windows.WinMD.1.0.250131.1\build\native\Microsoft.Windows.WinMD.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Windows.WinMD.1.0.250131.1\build\native\Microsoft.Windows.WinMD.props'))" />
325325
</Target>
326326
</Project>

0 commit comments

Comments
 (0)