Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Natvis definitions for types defined in the core module of the windows crate #2023

Merged
merged 2 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,5 @@ jobs:
cargo clippy -p tool_msvc &&
cargo clippy -p tool_sys &&
cargo clippy -p tool_windows &&
cargo clippy -p tool_yml
cargo clippy -p tool_yml &&
cargo clippy -p test_debugger_visualizer
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ jobs:
cargo test --target ${{ matrix.target }} -p tool_windows &&
cargo test --target ${{ matrix.target }} -p tool_yml

- name: Test debugger_visualizer feature
run: cargo test --target ${{ matrix.target }} -p test_debugger_visualizer -- --test-threads=1
if: matrix.version == 'nightly' && endsWith(matrix.target, '-msvc')

- name: Check import libs
shell: pwsh
run: |
Expand Down
5 changes: 5 additions & 0 deletions crates/libs/windows/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -724,3 +724,8 @@ Win32_UI_WindowsAndMessaging = ["Win32_UI"]
Win32_UI_Wpf = ["Win32_UI"]
Win32_UI_Xaml = ["Win32_UI"]
Win32_UI_Xaml_Diagnostics = ["Win32_UI_Xaml"]

# UNSTABLE FEATURES (requires Rust nightly)
# Enable to use the #[debugger_visualizer] attribute.
debugger_visualizer = []

1 change: 1 addition & 0 deletions crates/libs/windows/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Learn more about Rust for Windows here: <https://github.com/microsoft/windows-rs

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

extern crate self as windows;
mod Windows;
Expand Down
82 changes: 82 additions & 0 deletions crates/libs/windows/windows.natvis
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="windows::core::array::Array&lt;*&gt;">
<DisplayString>{{ len={len} }}</DisplayString>

<Expand>
<Item Name="[len]">len</Item>
<ArrayItems>
<Size>len</Size>
<ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>

<Type Name="windows::core::error::Error">
<Expand>
<ExpandedItem>code</ExpandedItem>
<Item Name="[info]">info</Item>
</Expand>
</Type>

<Type Name="windows::core::hresult::HRESULT">
<DisplayString>{(HRESULT)__0}</DisplayString>
</Type>

<Type Name="windows::core::ref_count::RefCount">
<DisplayString>{__0}</DisplayString>
</Type>

<Type Name="windows::core::strings::hstring::HSTRING">
<DisplayString Condition="__0 == nullptr">""</DisplayString>
<DisplayString>{((char16_t*)__0->data),[__0->len]su}</DisplayString>

<Expand>
<Item Name="[len]">__0 == nullptr ? (unsigned int)0 : __0->len</Item>
<Item Name="[ref_count]" Condition="__0 != nullptr">__0->count</Item>
<Item Name="[flags]" Condition="__0 != nullptr">__0->flags</Item>
<Synthetic Name="[chars]" Condition="__0 != nullptr">
<Expand>
<ArrayItems>
<Size>__0->len</Size>
<ValuePointer>(char16_t*)__0->data</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>

<Type Name="windows::core::strings::pstr::PSTR">
<AlternativeType Name="windows::core::strings::pcstr::PCSTR" />
<Intrinsic Name="len" Expression="strlen(((char*)__0))" />
<DisplayString>{(char*)__0,[len()]s8}</DisplayString>
<Expand>
<Item Name="[len]">len()</Item>
<Synthetic Name="[chars]">
<Expand>
<ArrayItems>
<Size>len()</Size>
<ValuePointer>(char*)__0</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>

<Type Name="windows::core::strings::pwstr::PWSTR">
<AlternativeType Name="windows::core::strings::pcwstr::PCWSTR" />
<Intrinsic Name="len" Expression="wcslen(((WCHAR*)__0))" />
<DisplayString>{(char16_t*)__0,[len()]su}</DisplayString>

<Expand>
<Item Name="[len]">len()</Item>
<Synthetic Name="[chars]">
<Expand>
<ArrayItems>
<Size>len()</Size>
<ValuePointer>(char16_t*)__0</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>
18 changes: 18 additions & 0 deletions crates/tests/debugger_visualizer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "test_debugger_visualizer"
version = "0.0.0"
authors = ["Microsoft"]
edition = "2018"

[dependencies.windows]
path = "../../libs/windows"
features = [
"debugger_visualizer",
"implement",
"Win32_System_Com",
"Win32_Foundation",
]

[dev-dependencies]
debugger_test = "0.1.0"
debugger_test_parser = "0.1.0"
1 change: 1 addition & 0 deletions crates/tests/debugger_visualizer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

241 changes: 241 additions & 0 deletions crates/tests/debugger_visualizer/tests/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
use debugger_test::*;
use windows::core::*;
use windows::Win32::Foundation::*;
use windows::Win32::System::Com::*;

#[inline(never)]
fn __break() {}

#[implement(IErrorInfo)]
struct Test;

impl IErrorInfo_Impl for Test {
fn GetGUID(&self) -> Result<GUID> {
Err(Error::new(E_OUTOFMEMORY, "Out of memory message".into()))
}
fn GetSource(&self) -> Result<BSTR> {
Err(Error::new(E_INVALIDARG, "Invalid argument message".into()))
}
fn GetDescription(&self) -> Result<BSTR> {
Ok(BSTR::new())
}
fn GetHelpFile(&self) -> Result<BSTR> {
Ok(BSTR::new())
}
fn GetHelpContext(&self) -> Result<u32> {
Ok(1)
}
}

#[debugger_test(
debugger = "cdb",
commands = "
.nvlist
dx array

dx -r2 pstr
dx -r2 pcstr
dx -r2 pwstr
dx -r2 pcwstr

dx empty
dx -r2 hstring

dx out_of_memory_error
dx invalid_argument_error
",
expected_statements = r#"
array : { len=0xd } [Type: windows::core::array::Array<u8>]
[<Raw View>] [Type: windows::core::array::Array<u8>]
[len] : 0xd
[0] : 0x48 [Type: unsigned char]
[1] : 0x65 [Type: unsigned char]
[2] : 0x6c [Type: unsigned char]
[3] : 0x6c [Type: unsigned char]
[4] : 0x6f [Type: unsigned char]
[5] : 0x20 [Type: unsigned char]
[6] : 0x57 [Type: unsigned char]
[7] : 0x6f [Type: unsigned char]
[8] : 0x72 [Type: unsigned char]
[9] : 0x6c [Type: unsigned char]
[10] : 0x64 [Type: unsigned char]
[11] : 0x21 [Type: unsigned char]
[12] : 0x0 [Type: unsigned char]

pstr : "This is a PSTR" [Type: windows::core::strings::pstr::PSTR]
[<Raw View>] [Type: windows::core::strings::pstr::PSTR]
[len] : 0xe
[chars]
[0] : 84 'T' [Type: char]
[1] : 104 'h' [Type: char]
[2] : 105 'i' [Type: char]
[3] : 115 's' [Type: char]
[4] : 32 ' ' [Type: char]
[5] : 105 'i' [Type: char]
[6] : 115 's' [Type: char]
[7] : 32 ' ' [Type: char]
[8] : 97 'a' [Type: char]
[9] : 32 ' ' [Type: char]
[10] : 80 'P' [Type: char]
[11] : 83 'S' [Type: char]
[12] : 84 'T' [Type: char]
[13] : 82 'R' [Type: char]

pcstr : "This is a PCSTR" [Type: windows::core::strings::pcstr::PCSTR]
[<Raw View>] [Type: windows::core::strings::pcstr::PCSTR]
[len] : 0xf
[chars]
[0] : 84 'T' [Type: char]
[1] : 104 'h' [Type: char]
[2] : 105 'i' [Type: char]
[3] : 115 's' [Type: char]
[4] : 32 ' ' [Type: char]
[5] : 105 'i' [Type: char]
[6] : 115 's' [Type: char]
[7] : 32 ' ' [Type: char]
[8] : 97 'a' [Type: char]
[9] : 32 ' ' [Type: char]
[10] : 80 'P' [Type: char]
[11] : 67 'C' [Type: char]
[12] : 83 'S' [Type: char]
[13] : 84 'T' [Type: char]
[14] : 82 'R' [Type: char]

pwstr : "This is a PWSTR" [Type: windows::core::strings::pwstr::PWSTR]
[<Raw View>] [Type: windows::core::strings::pwstr::PWSTR]
[len] : 0xf
[chars]
[0] : 0x54 'T' [Type: char16_t]
[1] : 0x68 'h' [Type: char16_t]
[2] : 0x69 'i' [Type: char16_t]
[3] : 0x73 's' [Type: char16_t]
[4] : 0x20 ' ' [Type: char16_t]
[5] : 0x69 'i' [Type: char16_t]
[6] : 0x73 's' [Type: char16_t]
[7] : 0x20 ' ' [Type: char16_t]
[8] : 0x61 'a' [Type: char16_t]
[9] : 0x20 ' ' [Type: char16_t]
[10] : 0x50 'P' [Type: char16_t]
[11] : 0x57 'W' [Type: char16_t]
[12] : 0x53 'S' [Type: char16_t]
[13] : 0x54 'T' [Type: char16_t]
[14] : 0x52 'R' [Type: char16_t]

pcwstr : "This is a PCWSTR" [Type: windows::core::strings::pcwstr::PCWSTR]
[<Raw View>] [Type: windows::core::strings::pcwstr::PCWSTR]
[len] : 0x10
[chars]
[0] : 0x54 'T' [Type: char16_t]
[1] : 0x68 'h' [Type: char16_t]
[2] : 0x69 'i' [Type: char16_t]
[3] : 0x73 's' [Type: char16_t]
[4] : 0x20 ' ' [Type: char16_t]
[5] : 0x69 'i' [Type: char16_t]
[6] : 0x73 's' [Type: char16_t]
[7] : 0x20 ' ' [Type: char16_t]
[8] : 0x61 'a' [Type: char16_t]
[9] : 0x20 ' ' [Type: char16_t]
[10] : 0x50 'P' [Type: char16_t]
[11] : 0x43 'C' [Type: char16_t]
[12] : 0x57 'W' [Type: char16_t]
[13] : 0x53 'S' [Type: char16_t]
[14] : 0x54 'T' [Type: char16_t]
[15] : 0x52 'R' [Type: char16_t]

empty : "" [Type: windows::core::strings::hstring::HSTRING]
[<Raw View>] [Type: windows::core::strings::hstring::HSTRING]
[len] : 0x0 [Type: unsigned int]

hstring : "This is an HSTRING" [Type: windows::core::strings::hstring::HSTRING]
[<Raw View>] [Type: windows::core::strings::hstring::HSTRING]
[len] : 0x12 [Type: unsigned int]
[ref_count] : 1 [Type: windows::core::ref_count::RefCount]
[flags] : 0x0 [Type: unsigned int]
[chars]
[0] : 0x54 'T' [Type: char16_t]
[1] : 0x68 'h' [Type: char16_t]
[2] : 0x69 'i' [Type: char16_t]
[3] : 0x73 's' [Type: char16_t]
[4] : 0x20 ' ' [Type: char16_t]
[5] : 0x69 'i' [Type: char16_t]
[6] : 0x73 's' [Type: char16_t]
[7] : 0x20 ' ' [Type: char16_t]
[8] : 0x61 'a' [Type: char16_t]
[9] : 0x6e 'n' [Type: char16_t]
[10] : 0x20 ' ' [Type: char16_t]
[11] : 0x48 'H' [Type: char16_t]
[12] : 0x53 'S' [Type: char16_t]
[13] : 0x54 'T' [Type: char16_t]
[14] : 0x52 'R' [Type: char16_t]
[15] : 0x49 'I' [Type: char16_t]
[16] : 0x4e 'N' [Type: char16_t]
[17] : 0x47 'G' [Type: char16_t]

out_of_memory_error : 0x8007000e (Not enough memory resources are available to complete this operation.) [Type: windows::core::error::Error]
[<Raw View>] [Type: windows::core::error::Error]
[info] : Some [Type: enum2$<core::option::Option<windows::core::bindings::IRestrictedErrorInfo> >]

invalid_argument_error : 0x80070057 (The parameter is incorrect.) [Type: windows::core::error::Error]
[<Raw View>] [Type: windows::core::error::Error]
[info] : Some [Type: enum2$<core::option::Option<windows::core::bindings::IRestrictedErrorInfo> >]
"#
)]
fn test_debugger_visualizer() {
let string = "Hello World!\0".to_string();
let mut array = Array::<u8>::with_len(string.len());
for (i, ch) in string.as_bytes().iter().enumerate() {
array[i] = ch.clone();
}

// Test debugger visualizations for PSTR
let mut pstr_string = "This is a PSTR\0".to_string();
let pstr = PSTR::from_raw(pstr_string.as_mut_ptr());
unsafe {
assert_eq!(&pstr_string.as_bytes()[..(pstr_string.len() - 1)], pstr.as_bytes());
}

// Test debugger visualizations for PCSTR
let pcstr_string = "This is a PCSTR\0".to_string();
let pcstr = PCSTR::from_raw(pcstr_string.as_ptr());
unsafe {
assert_eq!(&pcstr_string.as_bytes()[..(pcstr_string.len() - 1)], pcstr.as_bytes());
}

// Test debugger visualizations for PWSTR
let mut pwstr_string: Vec<u16> = vec![84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 80, 87, 83, 84, 82, 0];
let pwstr = PWSTR::from_raw(pwstr_string.as_mut_ptr());
unsafe {
assert_eq!(&pwstr_string.as_slice()[..(pwstr_string.len() - 1)], pwstr.as_wide());
}

// Test debugger visualizations for PCWSTR
let pcwstr_string: Vec<u16> = vec![84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 80, 67, 87, 83, 84, 82, 0];
let pcwstr = PCWSTR::from_raw(pcwstr_string.as_ptr());
unsafe {
assert_eq!(&pcwstr_string.as_slice()[..(pcwstr_string.len() - 1)], pcwstr.as_wide());
}

// Test debugger visualizations for HSTRING
let empty = HSTRING::new();
assert!(empty.is_empty());

let hstring = HSTRING::from("This is an HSTRING");
assert!(!hstring.is_empty());
assert!(hstring.len() == 18);

let test: IErrorInfo = Test.into();

unsafe {
// Test debugger visualizations for Error
let result = test.GetGUID();
let out_of_memory_error = result.unwrap_err();
assert_eq!(out_of_memory_error.code(), E_OUTOFMEMORY);
assert_eq!(out_of_memory_error.message(), "Out of memory message");

let result = test.GetSource();
let invalid_argument_error = result.unwrap_err();
assert_eq!(invalid_argument_error.code(), E_INVALIDARG);
assert_eq!(invalid_argument_error.message(), "Invalid argument message");
__break();
}
}
11 changes: 11 additions & 0 deletions crates/tools/windows/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ interface = ["windows-interface"]
}
}

file.write_all(
r#"
# UNSTABLE FEATURES (requires Rust nightly)
# Enable to use the #[debugger_visualizer] attribute.
debugger_visualizer = []

"#
.as_bytes(),
)
.unwrap();

std::fs::copy("license-mit", "crates/libs/windows/license-mit").unwrap();
std::fs::copy("license-apache-2.0", "crates/libs/windows/license-apache-2.0").unwrap();
}
Expand Down
Loading