diff --git a/crates/libs/windows/src/core/heap.rs b/crates/libs/windows/src/core/heap.rs index e1a3bc77c9..3657423a62 100644 --- a/crates/libs/windows/src/core/heap.rs +++ b/crates/libs/windows/src/core/heap.rs @@ -14,6 +14,13 @@ pub fn heap_alloc(bytes: usize) -> Result { if ptr.is_null() { Err(E_OUTOFMEMORY.into()) } else { + // HeapAlloc is not guaranteed to return zero memory but usually does. This just ensures that + // it predictably returns non-zero memory for testing purposes. This is similar to what MSVC's + // debug allocator does for the same reason. + #[cfg(debug_assertions)] + unsafe { + std::ptr::write_bytes(ptr, 0xCC, bytes); + } Ok(ptr) } } diff --git a/crates/tests/core/Cargo.toml b/crates/tests/core/Cargo.toml index 9a783d6a56..d678a66d1a 100644 --- a/crates/tests/core/Cargo.toml +++ b/crates/tests/core/Cargo.toml @@ -4,5 +4,16 @@ version = "0.0.0" authors = ["Microsoft"] edition = "2018" -[dependencies] -windows = { path = "../../libs/windows" } +[dependencies.windows] +path = "../../libs/windows" +features = [ + "alloc", + "Win32_Foundation", + "Win32_System_WinRT", +] + +[dependencies.windows-sys] +path = "../../libs/sys" +features = [ + "Win32_System_WinRT", +] diff --git a/crates/tests/core/tests/hstrings.rs b/crates/tests/core/tests/hstrings.rs index 0f4109bf01..7f117e033f 100644 --- a/crates/tests/core/tests/hstrings.rs +++ b/crates/tests/core/tests/hstrings.rs @@ -1,14 +1,13 @@ use std::convert::TryFrom; use windows::core::*; -type StringType = HSTRING; #[test] fn hstring_works() { - let empty = StringType::new(); + let empty = HSTRING::new(); assert!(empty.is_empty()); assert!(empty.is_empty()); - let mut hello = StringType::from("Hello"); + let mut hello = HSTRING::from("Hello"); assert!(!hello.is_empty()); assert!(hello.len() == 5); @@ -24,32 +23,32 @@ fn hstring_works() { assert!(!hello2.is_empty()); assert!(hello2.len() == 5); - assert!(StringType::from("Hello") == StringType::from("Hello")); - assert!(StringType::from("Hello") != StringType::from("World")); + assert!(HSTRING::from("Hello") == HSTRING::from("Hello")); + assert!(HSTRING::from("Hello") != HSTRING::from("World")); - assert!(StringType::from("Hello") == "Hello"); - assert!(StringType::from("Hello") != "Hello "); - assert!(StringType::from("Hello") != "Hell"); - assert!(StringType::from("Hello") != "World"); + assert!(HSTRING::from("Hello") == "Hello"); + assert!(HSTRING::from("Hello") != "Hello "); + assert!(HSTRING::from("Hello") != "Hell"); + assert!(HSTRING::from("Hello") != "World"); - assert!(StringType::from("Hello").to_string() == String::from("Hello")); + assert!(HSTRING::from("Hello").to_string() == String::from("Hello")); } #[test] fn display_format() { - let value = StringType::from("Hello world"); + let value = HSTRING::from("Hello world"); assert!(format!("{}", value) == "Hello world"); } #[test] fn debug_format() { - let value = StringType::from("Hello world"); + let value = HSTRING::from("Hello world"); assert!(format!("{:?}", value) == "Hello world"); } #[test] fn from_empty_string() { - let h = StringType::from(""); + let h = HSTRING::from(""); assert!(format!("{}", h).is_empty()); } @@ -58,14 +57,14 @@ fn from_os_string_string() { let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; use std::os::windows::prelude::OsStringExt; let o = std::ffi::OsString::from_wide(wide_data); - let h = StringType::from(o); - let d = StringType::from_wide(wide_data); + let h = HSTRING::from(o); + let d = HSTRING::from_wide(wide_data); assert_eq!(h, d); } #[test] fn hstring_to_string() { - let h = StringType::from("test"); + let h = HSTRING::from("test"); let s = String::try_from(h).unwrap(); assert!(s == "test"); } @@ -74,7 +73,7 @@ fn hstring_to_string() { fn hstring_to_string_err() { // š¯„˛muic let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; - let h = StringType::from_wide(wide_data); + let h = HSTRING::from_wide(wide_data); let err = String::try_from(h); assert!(err.is_err()); } @@ -83,7 +82,7 @@ fn hstring_to_string_err() { fn hstring_to_string_lossy() { // š¯„˛muic let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; - let h = StringType::from_wide(wide_data); + let h = HSTRING::from_wide(wide_data); let s = h.to_string_lossy(); assert_eq!(s, "š¯„˛muļæ½ic"); } @@ -92,7 +91,7 @@ fn hstring_to_string_lossy() { fn hstring_to_os_string() { // š¯„˛muic let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; - let h = StringType::from_wide(wide_data); + let h = HSTRING::from_wide(wide_data); let s = h.to_os_string(); use std::os::windows::prelude::OsStringExt; assert_eq!(s, std::ffi::OsString::from_wide(wide_data)); @@ -100,7 +99,7 @@ fn hstring_to_os_string() { #[test] fn hstring_equality_combinations() { - let h = StringType::from("test"); + let h = HSTRING::from("test"); let s = String::from("test"); let ss: &str = "test"; @@ -128,7 +127,7 @@ fn hstring_equality_combinations() { #[test] fn hstring_osstring_equality_combinations() { let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063]; - let h = StringType::from_wide(wide_data); + let h = HSTRING::from_wide(wide_data); use std::os::windows::prelude::OsStringExt; let s = std::ffi::OsString::from_wide(wide_data); let ss = s.as_os_str(); @@ -153,3 +152,65 @@ fn hstring_osstring_equality_combinations() { assert_eq!(ss, h); assert_eq!(ss, &h); } + +#[test] +fn hstring_compat() -> Result<()> { + unsafe { + use windows::Win32::System::WinRT::*; + let hey = HSTRING::from("Hey"); + let world = HSTRING::from("World"); + assert_eq!(WindowsCompareStringOrdinal(&hey, &world)?, -1); + assert_eq!(WindowsCompareStringOrdinal("Hey", "World")?, -1); + + assert_eq!(WindowsConcatString(&hey, &world)?, "HeyWorld"); + assert_eq!(WindowsConcatString("Hey", "World")?, "HeyWorld"); + + assert_eq!(WindowsCreateString(hey.as_wide())?, "Hey"); + + assert_eq!(WindowsDuplicateString(&hey)?, "Hey"); + assert_eq!(WindowsDuplicateString("Hey")?, "Hey"); + + assert_eq!(WindowsGetStringLen(&hey), 3); + assert_eq!(WindowsGetStringLen("Hey"), 3); + assert_eq!(WindowsGetStringLen(&world), 5); + assert_eq!(WindowsGetStringLen("World"), 5); + + assert_eq!(WindowsIsStringEmpty(HSTRING::new()), true); + assert_eq!(WindowsIsStringEmpty(""), true); + assert_eq!(WindowsIsStringEmpty(&world), false); + assert_eq!(WindowsIsStringEmpty("World"), false); + + let mut len = 0; + let buffer = WindowsGetStringRawBuffer(&world, &mut len); + assert_eq!(len, 5); + // Adding +1 to the length of the slice to validate that it is null terminated. + assert_eq!(std::slice::from_raw_parts(buffer.0, 6), [87, 111, 114, 108, 100, 0]); + + // We need to drop to windows-sys to call the raw WindowsDeleteString function to avoid double-freeing the HSTRING, + // but this test is important as it ensures that the allocators match. + let hresult = windows_sys::Win32::System::WinRT::WindowsDeleteString(std::mem::transmute_copy(&*std::mem::ManuallyDrop::new(hey))); + assert_eq!(hresult, 0); + + // An HSTRING reference a.k.a. "fast pass" string is a kind of stack-based HSTRING used by C++ callers + // to avoid the heap allocation in some cases. It's not used in Rust since it assumes a wide character + // string literal, which is inconvenient to create in Rust. Here we again use windows-sys to make one + // and thereby excercise the windows::core::HSTRING support for HSTRING reference duplication. + let mut header: windows_sys::Win32::System::WinRT::HSTRING_HEADER = std::mem::zeroed(); + let mut stack_hstring: windows_sys::core::HSTRING = std::mem::zeroed(); + let hresult = windows_sys::Win32::System::WinRT::WindowsCreateStringReference([87, 111, 114, 108, 100, 0].as_ptr(), 5, &mut header, &mut stack_hstring); + assert_eq!(hresult, 0); + assert_eq!(header.length, 5); + let stack_hstring: std::mem::ManuallyDrop = std::mem::transmute(stack_hstring); + let duplicate: HSTRING = (*stack_hstring).clone(); + assert_eq!(&duplicate, &*stack_hstring); + assert_eq!(duplicate, "World"); + + let mut len = 0; + let buffer = WindowsGetStringRawBuffer(&duplicate, &mut len); + assert_eq!(len, 5); + // Adding +1 to the length of the slice to validate that it is null terminated. + assert_eq!(std::slice::from_raw_parts(buffer.0, 6), [87, 111, 114, 108, 100, 0]); + + Ok(()) + } +} diff --git a/crates/tests/nightly_component/src/bindings.rs b/crates/tests/nightly_component/src/bindings.rs index 86bb047acd..aba3975ae5 100644 --- a/crates/tests/nightly_component/src/bindings.rs +++ b/crates/tests/nightly_component/src/bindings.rs @@ -115,7 +115,7 @@ impl ::windows::core::RuntimeName for IClass { } impl IClass_Vtbl { pub const fn new, Impl: IClass_Impl, const OFFSET: isize>() -> IClass_Vtbl { - unsafe extern "system" fn Property(this: *mut ::core::ffi::c_void, result__: *mut i32) -> ::windows::core::HRESULT { + unsafe extern "system" fn Property, Impl: IClass_Impl, const OFFSET: isize>(this: *mut ::core::ffi::c_void, result__: *mut i32) -> ::windows::core::HRESULT { let this = (this as *const *const ()).offset(OFFSET) as *const Identity; let this = (*this).get_impl(); match this.Property() {