Skip to content

Commit 29e5510

Browse files
Add Natvis definitions for types defined in the core module of the windows crate (#2023)
1 parent e018d41 commit 29e5510

File tree

11 files changed

+492
-4
lines changed

11 files changed

+492
-4
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,5 @@ jobs:
195195
cargo clippy -p tool_msvc &&
196196
cargo clippy -p tool_sys &&
197197
cargo clippy -p tool_windows &&
198-
cargo clippy -p tool_yml
198+
cargo clippy -p tool_yml &&
199+
cargo clippy -p test_debugger_visualizer

.github/workflows/test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ jobs:
181181
cargo test --target ${{ matrix.target }} -p tool_windows &&
182182
cargo test --target ${{ matrix.target }} -p tool_yml
183183
184+
- name: Test debugger_visualizer feature
185+
run: cargo test --target ${{ matrix.target }} -p test_debugger_visualizer -- --test-threads=1
186+
if: matrix.version == 'nightly' && endsWith(matrix.target, '-msvc')
187+
184188
- name: Check import libs
185189
shell: pwsh
186190
run: |

crates/libs/windows/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,8 @@ Win32_UI_WindowsAndMessaging = ["Win32_UI"]
724724
Win32_UI_Wpf = ["Win32_UI"]
725725
Win32_UI_Xaml = ["Win32_UI"]
726726
Win32_UI_Xaml_Diagnostics = ["Win32_UI_Xaml"]
727+
728+
# UNSTABLE FEATURES (requires Rust nightly)
729+
# Enable to use the #[debugger_visualizer] attribute.
730+
debugger_visualizer = []
731+

crates/libs/windows/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs
44

55
#![doc(html_no_source)]
66
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, clashing_extern_declarations, unused_variables, dead_code, clippy::all)]
7+
#![cfg_attr(feature = "debugger_visualizer", feature(debugger_visualizer), debugger_visualizer(natvis_file = "../windows.natvis"))]
78

89
extern crate self as windows;
910
mod Windows;

crates/libs/windows/windows.natvis

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
2+
<Type Name="windows::core::array::Array&lt;*&gt;">
3+
<DisplayString>{{ len={len} }}</DisplayString>
4+
5+
<Expand>
6+
<Item Name="[len]">len</Item>
7+
<ArrayItems>
8+
<Size>len</Size>
9+
<ValuePointer>data</ValuePointer>
10+
</ArrayItems>
11+
</Expand>
12+
</Type>
13+
14+
<Type Name="windows::core::error::Error">
15+
<Expand>
16+
<ExpandedItem>code</ExpandedItem>
17+
<Item Name="[info]">info</Item>
18+
</Expand>
19+
</Type>
20+
21+
<Type Name="windows::core::hresult::HRESULT">
22+
<DisplayString>{(HRESULT)__0}</DisplayString>
23+
</Type>
24+
25+
<Type Name="windows::core::ref_count::RefCount">
26+
<DisplayString>{__0}</DisplayString>
27+
</Type>
28+
29+
<Type Name="windows::core::strings::hstring::HSTRING">
30+
<DisplayString Condition="__0 == nullptr">""</DisplayString>
31+
<DisplayString>{((char16_t*)__0->data),[__0->len]su}</DisplayString>
32+
33+
<Expand>
34+
<Item Name="[len]">__0 == nullptr ? (unsigned int)0 : __0->len</Item>
35+
<Item Name="[ref_count]" Condition="__0 != nullptr">__0->count</Item>
36+
<Item Name="[flags]" Condition="__0 != nullptr">__0->flags</Item>
37+
<Synthetic Name="[chars]" Condition="__0 != nullptr">
38+
<Expand>
39+
<ArrayItems>
40+
<Size>__0->len</Size>
41+
<ValuePointer>(char16_t*)__0->data</ValuePointer>
42+
</ArrayItems>
43+
</Expand>
44+
</Synthetic>
45+
</Expand>
46+
</Type>
47+
48+
<Type Name="windows::core::strings::pstr::PSTR">
49+
<AlternativeType Name="windows::core::strings::pcstr::PCSTR" />
50+
<Intrinsic Name="len" Expression="strlen(((char*)__0))" />
51+
<DisplayString>{(char*)__0,[len()]s8}</DisplayString>
52+
<Expand>
53+
<Item Name="[len]">len()</Item>
54+
<Synthetic Name="[chars]">
55+
<Expand>
56+
<ArrayItems>
57+
<Size>len()</Size>
58+
<ValuePointer>(char*)__0</ValuePointer>
59+
</ArrayItems>
60+
</Expand>
61+
</Synthetic>
62+
</Expand>
63+
</Type>
64+
65+
<Type Name="windows::core::strings::pwstr::PWSTR">
66+
<AlternativeType Name="windows::core::strings::pcwstr::PCWSTR" />
67+
<Intrinsic Name="len" Expression="wcslen(((WCHAR*)__0))" />
68+
<DisplayString>{(char16_t*)__0,[len()]su}</DisplayString>
69+
70+
<Expand>
71+
<Item Name="[len]">len()</Item>
72+
<Synthetic Name="[chars]">
73+
<Expand>
74+
<ArrayItems>
75+
<Size>len()</Size>
76+
<ValuePointer>(char16_t*)__0</ValuePointer>
77+
</ArrayItems>
78+
</Expand>
79+
</Synthetic>
80+
</Expand>
81+
</Type>
82+
</AutoVisualizer>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "test_debugger_visualizer"
3+
version = "0.0.0"
4+
authors = ["Microsoft"]
5+
edition = "2018"
6+
7+
[dependencies.windows]
8+
path = "../../libs/windows"
9+
features = [
10+
"debugger_visualizer",
11+
"implement",
12+
"Win32_System_Com",
13+
"Win32_Foundation",
14+
]
15+
16+
[dev-dependencies]
17+
debugger_test = "0.1.0"
18+
debugger_test_parser = "0.1.0"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
use debugger_test::*;
2+
use windows::core::*;
3+
use windows::Win32::Foundation::*;
4+
use windows::Win32::System::Com::*;
5+
6+
#[inline(never)]
7+
fn __break() {}
8+
9+
#[implement(IErrorInfo)]
10+
struct Test;
11+
12+
impl IErrorInfo_Impl for Test {
13+
fn GetGUID(&self) -> Result<GUID> {
14+
Err(Error::new(E_OUTOFMEMORY, "Out of memory message".into()))
15+
}
16+
fn GetSource(&self) -> Result<BSTR> {
17+
Err(Error::new(E_INVALIDARG, "Invalid argument message".into()))
18+
}
19+
fn GetDescription(&self) -> Result<BSTR> {
20+
Ok(BSTR::new())
21+
}
22+
fn GetHelpFile(&self) -> Result<BSTR> {
23+
Ok(BSTR::new())
24+
}
25+
fn GetHelpContext(&self) -> Result<u32> {
26+
Ok(1)
27+
}
28+
}
29+
30+
#[debugger_test(
31+
debugger = "cdb",
32+
commands = "
33+
.nvlist
34+
dx array
35+
36+
dx -r2 pstr
37+
dx -r2 pcstr
38+
dx -r2 pwstr
39+
dx -r2 pcwstr
40+
41+
dx empty
42+
dx -r2 hstring
43+
44+
dx out_of_memory_error
45+
dx invalid_argument_error
46+
",
47+
expected_statements = r#"
48+
array : { len=0xd } [Type: windows::core::array::Array<u8>]
49+
[<Raw View>] [Type: windows::core::array::Array<u8>]
50+
[len] : 0xd
51+
[0] : 0x48 [Type: unsigned char]
52+
[1] : 0x65 [Type: unsigned char]
53+
[2] : 0x6c [Type: unsigned char]
54+
[3] : 0x6c [Type: unsigned char]
55+
[4] : 0x6f [Type: unsigned char]
56+
[5] : 0x20 [Type: unsigned char]
57+
[6] : 0x57 [Type: unsigned char]
58+
[7] : 0x6f [Type: unsigned char]
59+
[8] : 0x72 [Type: unsigned char]
60+
[9] : 0x6c [Type: unsigned char]
61+
[10] : 0x64 [Type: unsigned char]
62+
[11] : 0x21 [Type: unsigned char]
63+
[12] : 0x0 [Type: unsigned char]
64+
65+
pstr : "This is a PSTR" [Type: windows::core::strings::pstr::PSTR]
66+
[<Raw View>] [Type: windows::core::strings::pstr::PSTR]
67+
[len] : 0xe
68+
[chars]
69+
[0] : 84 'T' [Type: char]
70+
[1] : 104 'h' [Type: char]
71+
[2] : 105 'i' [Type: char]
72+
[3] : 115 's' [Type: char]
73+
[4] : 32 ' ' [Type: char]
74+
[5] : 105 'i' [Type: char]
75+
[6] : 115 's' [Type: char]
76+
[7] : 32 ' ' [Type: char]
77+
[8] : 97 'a' [Type: char]
78+
[9] : 32 ' ' [Type: char]
79+
[10] : 80 'P' [Type: char]
80+
[11] : 83 'S' [Type: char]
81+
[12] : 84 'T' [Type: char]
82+
[13] : 82 'R' [Type: char]
83+
84+
pcstr : "This is a PCSTR" [Type: windows::core::strings::pcstr::PCSTR]
85+
[<Raw View>] [Type: windows::core::strings::pcstr::PCSTR]
86+
[len] : 0xf
87+
[chars]
88+
[0] : 84 'T' [Type: char]
89+
[1] : 104 'h' [Type: char]
90+
[2] : 105 'i' [Type: char]
91+
[3] : 115 's' [Type: char]
92+
[4] : 32 ' ' [Type: char]
93+
[5] : 105 'i' [Type: char]
94+
[6] : 115 's' [Type: char]
95+
[7] : 32 ' ' [Type: char]
96+
[8] : 97 'a' [Type: char]
97+
[9] : 32 ' ' [Type: char]
98+
[10] : 80 'P' [Type: char]
99+
[11] : 67 'C' [Type: char]
100+
[12] : 83 'S' [Type: char]
101+
[13] : 84 'T' [Type: char]
102+
[14] : 82 'R' [Type: char]
103+
104+
pwstr : "This is a PWSTR" [Type: windows::core::strings::pwstr::PWSTR]
105+
[<Raw View>] [Type: windows::core::strings::pwstr::PWSTR]
106+
[len] : 0xf
107+
[chars]
108+
[0] : 0x54 'T' [Type: char16_t]
109+
[1] : 0x68 'h' [Type: char16_t]
110+
[2] : 0x69 'i' [Type: char16_t]
111+
[3] : 0x73 's' [Type: char16_t]
112+
[4] : 0x20 ' ' [Type: char16_t]
113+
[5] : 0x69 'i' [Type: char16_t]
114+
[6] : 0x73 's' [Type: char16_t]
115+
[7] : 0x20 ' ' [Type: char16_t]
116+
[8] : 0x61 'a' [Type: char16_t]
117+
[9] : 0x20 ' ' [Type: char16_t]
118+
[10] : 0x50 'P' [Type: char16_t]
119+
[11] : 0x57 'W' [Type: char16_t]
120+
[12] : 0x53 'S' [Type: char16_t]
121+
[13] : 0x54 'T' [Type: char16_t]
122+
[14] : 0x52 'R' [Type: char16_t]
123+
124+
pcwstr : "This is a PCWSTR" [Type: windows::core::strings::pcwstr::PCWSTR]
125+
[<Raw View>] [Type: windows::core::strings::pcwstr::PCWSTR]
126+
[len] : 0x10
127+
[chars]
128+
[0] : 0x54 'T' [Type: char16_t]
129+
[1] : 0x68 'h' [Type: char16_t]
130+
[2] : 0x69 'i' [Type: char16_t]
131+
[3] : 0x73 's' [Type: char16_t]
132+
[4] : 0x20 ' ' [Type: char16_t]
133+
[5] : 0x69 'i' [Type: char16_t]
134+
[6] : 0x73 's' [Type: char16_t]
135+
[7] : 0x20 ' ' [Type: char16_t]
136+
[8] : 0x61 'a' [Type: char16_t]
137+
[9] : 0x20 ' ' [Type: char16_t]
138+
[10] : 0x50 'P' [Type: char16_t]
139+
[11] : 0x43 'C' [Type: char16_t]
140+
[12] : 0x57 'W' [Type: char16_t]
141+
[13] : 0x53 'S' [Type: char16_t]
142+
[14] : 0x54 'T' [Type: char16_t]
143+
[15] : 0x52 'R' [Type: char16_t]
144+
145+
empty : "" [Type: windows::core::strings::hstring::HSTRING]
146+
[<Raw View>] [Type: windows::core::strings::hstring::HSTRING]
147+
[len] : 0x0 [Type: unsigned int]
148+
149+
hstring : "This is an HSTRING" [Type: windows::core::strings::hstring::HSTRING]
150+
[<Raw View>] [Type: windows::core::strings::hstring::HSTRING]
151+
[len] : 0x12 [Type: unsigned int]
152+
[ref_count] : 1 [Type: windows::core::ref_count::RefCount]
153+
[flags] : 0x0 [Type: unsigned int]
154+
[chars]
155+
[0] : 0x54 'T' [Type: char16_t]
156+
[1] : 0x68 'h' [Type: char16_t]
157+
[2] : 0x69 'i' [Type: char16_t]
158+
[3] : 0x73 's' [Type: char16_t]
159+
[4] : 0x20 ' ' [Type: char16_t]
160+
[5] : 0x69 'i' [Type: char16_t]
161+
[6] : 0x73 's' [Type: char16_t]
162+
[7] : 0x20 ' ' [Type: char16_t]
163+
[8] : 0x61 'a' [Type: char16_t]
164+
[9] : 0x6e 'n' [Type: char16_t]
165+
[10] : 0x20 ' ' [Type: char16_t]
166+
[11] : 0x48 'H' [Type: char16_t]
167+
[12] : 0x53 'S' [Type: char16_t]
168+
[13] : 0x54 'T' [Type: char16_t]
169+
[14] : 0x52 'R' [Type: char16_t]
170+
[15] : 0x49 'I' [Type: char16_t]
171+
[16] : 0x4e 'N' [Type: char16_t]
172+
[17] : 0x47 'G' [Type: char16_t]
173+
174+
out_of_memory_error : 0x8007000e (Not enough memory resources are available to complete this operation.) [Type: windows::core::error::Error]
175+
[<Raw View>] [Type: windows::core::error::Error]
176+
[info] : Some [Type: enum2$<core::option::Option<windows::core::bindings::IRestrictedErrorInfo> >]
177+
178+
invalid_argument_error : 0x80070057 (The parameter is incorrect.) [Type: windows::core::error::Error]
179+
[<Raw View>] [Type: windows::core::error::Error]
180+
[info] : Some [Type: enum2$<core::option::Option<windows::core::bindings::IRestrictedErrorInfo> >]
181+
"#
182+
)]
183+
fn test_debugger_visualizer() {
184+
let string = "Hello World!\0".to_string();
185+
let mut array = Array::<u8>::with_len(string.len());
186+
for (i, ch) in string.as_bytes().iter().enumerate() {
187+
array[i] = ch.clone();
188+
}
189+
190+
// Test debugger visualizations for PSTR
191+
let mut pstr_string = "This is a PSTR\0".to_string();
192+
let pstr = PSTR::from_raw(pstr_string.as_mut_ptr());
193+
unsafe {
194+
assert_eq!(&pstr_string.as_bytes()[..(pstr_string.len() - 1)], pstr.as_bytes());
195+
}
196+
197+
// Test debugger visualizations for PCSTR
198+
let pcstr_string = "This is a PCSTR\0".to_string();
199+
let pcstr = PCSTR::from_raw(pcstr_string.as_ptr());
200+
unsafe {
201+
assert_eq!(&pcstr_string.as_bytes()[..(pcstr_string.len() - 1)], pcstr.as_bytes());
202+
}
203+
204+
// Test debugger visualizations for PWSTR
205+
let mut pwstr_string: Vec<u16> = vec![84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 80, 87, 83, 84, 82, 0];
206+
let pwstr = PWSTR::from_raw(pwstr_string.as_mut_ptr());
207+
unsafe {
208+
assert_eq!(&pwstr_string.as_slice()[..(pwstr_string.len() - 1)], pwstr.as_wide());
209+
}
210+
211+
// Test debugger visualizations for PCWSTR
212+
let pcwstr_string: Vec<u16> = vec![84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 80, 67, 87, 83, 84, 82, 0];
213+
let pcwstr = PCWSTR::from_raw(pcwstr_string.as_ptr());
214+
unsafe {
215+
assert_eq!(&pcwstr_string.as_slice()[..(pcwstr_string.len() - 1)], pcwstr.as_wide());
216+
}
217+
218+
// Test debugger visualizations for HSTRING
219+
let empty = HSTRING::new();
220+
assert!(empty.is_empty());
221+
222+
let hstring = HSTRING::from("This is an HSTRING");
223+
assert!(!hstring.is_empty());
224+
assert!(hstring.len() == 18);
225+
226+
let test: IErrorInfo = Test.into();
227+
228+
unsafe {
229+
// Test debugger visualizations for Error
230+
let result = test.GetGUID();
231+
let out_of_memory_error = result.unwrap_err();
232+
assert_eq!(out_of_memory_error.code(), E_OUTOFMEMORY);
233+
assert_eq!(out_of_memory_error.message(), "Out of memory message");
234+
235+
let result = test.GetSource();
236+
let invalid_argument_error = result.unwrap_err();
237+
assert_eq!(invalid_argument_error.code(), E_INVALIDARG);
238+
assert_eq!(invalid_argument_error.message(), "Invalid argument message");
239+
__break();
240+
}
241+
}

crates/tools/windows/src/main.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ interface = ["windows-interface"]
116116
}
117117
}
118118

119+
file.write_all(
120+
r#"
121+
# UNSTABLE FEATURES (requires Rust nightly)
122+
# Enable to use the #[debugger_visualizer] attribute.
123+
debugger_visualizer = []
124+
125+
"#
126+
.as_bytes(),
127+
)
128+
.unwrap();
129+
119130
std::fs::copy("license-mit", "crates/libs/windows/license-mit").unwrap();
120131
std::fs::copy("license-apache-2.0", "crates/libs/windows/license-apache-2.0").unwrap();
121132
}

0 commit comments

Comments
 (0)