Consistent allocation failure handling (#3209)
This commit is contained in:
Родитель
1e662ff4d1
Коммит
75a632dc4c
|
@ -133,8 +133,6 @@ jobs:
|
|||
run: cargo clippy -p test_array
|
||||
- name: Clippy test_bcrypt
|
||||
run: cargo clippy -p test_bcrypt
|
||||
- name: Clippy test_bstr
|
||||
run: cargo clippy -p test_bstr
|
||||
- name: Clippy test_calling_convention
|
||||
run: cargo clippy -p test_calling_convention
|
||||
- name: Clippy test_cfg_generic
|
||||
|
|
|
@ -159,8 +159,6 @@ jobs:
|
|||
run: cargo test -p test_array --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Test test_bcrypt
|
||||
run: cargo test -p test_bcrypt --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Test test_bstr
|
||||
run: cargo test -p test_bstr --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Test test_calling_convention
|
||||
run: cargo test -p test_calling_convention --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Test test_cfg_generic
|
||||
|
@ -255,10 +253,10 @@ jobs:
|
|||
run: cargo test -p test_standalone --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Test test_string_param
|
||||
run: cargo test -p test_string_param --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Clean
|
||||
run: cargo clean
|
||||
- name: Test test_strings
|
||||
run: cargo test -p test_strings --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Clean
|
||||
run: cargo clean
|
||||
- name: Test test_structs
|
||||
run: cargo test -p test_structs --target ${{ matrix.target }} ${{ matrix.etc }}
|
||||
- name: Test test_sys
|
||||
|
|
|
@ -43,7 +43,6 @@ pub const CO_E_NOTINITIALIZED: windows_core::HRESULT = windows_core::HRESULT(0x8
|
|||
pub const E_BOUNDS: windows_core::HRESULT = windows_core::HRESULT(0x8000000B_u32 as _);
|
||||
pub const E_INVALIDARG: windows_core::HRESULT = windows_core::HRESULT(0x80070057_u32 as _);
|
||||
pub const E_NOINTERFACE: windows_core::HRESULT = windows_core::HRESULT(0x80004002_u32 as _);
|
||||
pub const E_OUTOFMEMORY: windows_core::HRESULT = windows_core::HRESULT(0x8007000E_u32 as _);
|
||||
pub const E_POINTER: windows_core::HRESULT = windows_core::HRESULT(0x80004003_u32 as _);
|
||||
windows_core::imp::define_interface!(
|
||||
IAgileObject,
|
||||
|
|
|
@ -175,7 +175,7 @@ impl Key {
|
|||
return Err(invalid_data());
|
||||
}
|
||||
|
||||
let mut value = HStringBuilder::new(len / 2)?;
|
||||
let mut value = HStringBuilder::new(len / 2);
|
||||
unsafe { self.raw_get_bytes(name.as_raw(), value.as_bytes_mut())? };
|
||||
value.trim_end();
|
||||
Ok(value.into())
|
||||
|
|
|
@ -111,7 +111,7 @@ impl TryFrom<Value> for HSTRING {
|
|||
type Error = Error;
|
||||
fn try_from(from: Value) -> Result<Self> {
|
||||
match from.ty {
|
||||
Type::String | Type::ExpandString => Ok(Self::from_wide(trim(from.data.as_wide()))?),
|
||||
Type::String | Type::ExpandString => Ok(Self::from_wide(trim(from.data.as_wide()))),
|
||||
_ => Err(invalid_data()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,11 +21,6 @@ targets = []
|
|||
version = "0.52.6"
|
||||
path = "../targets"
|
||||
|
||||
[dependencies.windows-result]
|
||||
version = "0.2.0"
|
||||
path = "../result"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
|
|
@ -21,16 +21,14 @@ use windows_strings::*;
|
|||
const A: PCSTR = s!("ansi");
|
||||
const W: PCWSTR = w!("wide");
|
||||
|
||||
fn main() -> Result<()> {
|
||||
fn main() {
|
||||
let b = BSTR::from("bstr");
|
||||
let h = HSTRING::from("hstring");
|
||||
|
||||
assert_eq!(b, "bstr");
|
||||
assert_eq!(h, "hstring");
|
||||
|
||||
assert_eq!(unsafe { A.to_string()? }, "ansi");
|
||||
assert_eq!(unsafe { W.to_string()? }, "wide");
|
||||
|
||||
Ok(())
|
||||
assert_eq!(unsafe { A.to_string().unwrap() }, "ansi");
|
||||
assert_eq!(unsafe { W.to_string().unwrap() }, "wide");
|
||||
}
|
||||
```
|
||||
|
|
|
@ -13,8 +13,6 @@ windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BST
|
|||
windows_targets::link!("oleaut32.dll" "system" fn SysStringLen(pbstr : BSTR) -> u32);
|
||||
pub type BOOL = i32;
|
||||
pub type BSTR = *const u16;
|
||||
pub const E_OUTOFMEMORY: HRESULT = 0x8007000E_u32 as _;
|
||||
pub type HANDLE = *mut core::ffi::c_void;
|
||||
pub type HEAP_FLAGS = u32;
|
||||
pub type HRESULT = i32;
|
||||
pub type PCWSTR = *const u16;
|
||||
|
|
|
@ -43,23 +43,23 @@ impl BSTR {
|
|||
}
|
||||
|
||||
/// Create a `BSTR` from a slice of 16 bit characters (wchars).
|
||||
pub fn from_wide(value: &[u16]) -> Result<Self> {
|
||||
pub fn from_wide(value: &[u16]) -> Self {
|
||||
if value.is_empty() {
|
||||
return Ok(Self::new());
|
||||
return Self::new();
|
||||
}
|
||||
|
||||
let result = unsafe {
|
||||
Self(bindings::SysAllocStringLen(
|
||||
value.as_ptr(),
|
||||
value.len().try_into()?,
|
||||
value.len().try_into().unwrap(),
|
||||
))
|
||||
};
|
||||
|
||||
if result.is_empty() {
|
||||
Err(Error::from_hresult(HRESULT(bindings::E_OUTOFMEMORY)))
|
||||
} else {
|
||||
Ok(result)
|
||||
panic!("allocation failed");
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
|
@ -77,14 +77,14 @@ impl BSTR {
|
|||
|
||||
impl Clone for BSTR {
|
||||
fn clone(&self) -> Self {
|
||||
Self::from_wide(self.as_wide()).unwrap()
|
||||
Self::from_wide(self.as_wide())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for BSTR {
|
||||
fn from(value: &str) -> Self {
|
||||
let value: alloc::vec::Vec<u16> = value.encode_utf16().collect();
|
||||
Self::from_wide(&value).unwrap()
|
||||
Self::from_wide(&value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ impl HSTRING {
|
|||
}
|
||||
|
||||
/// Create a `HSTRING` from a slice of 16 bit characters (wchars).
|
||||
pub fn from_wide(value: &[u16]) -> Result<Self> {
|
||||
pub fn from_wide(value: &[u16]) -> Self {
|
||||
unsafe { Self::from_wide_iter(value.iter().copied(), value.len()) }
|
||||
}
|
||||
|
||||
|
@ -61,12 +61,12 @@ impl HSTRING {
|
|||
|
||||
/// # Safety
|
||||
/// len must not be less than the number of items in the iterator.
|
||||
unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Result<Self> {
|
||||
unsafe fn from_wide_iter<I: Iterator<Item = u16>>(iter: I, len: usize) -> Self {
|
||||
if len == 0 {
|
||||
return Ok(Self::new());
|
||||
return Self::new();
|
||||
}
|
||||
|
||||
let ptr = HStringHeader::alloc(len.try_into()?)?;
|
||||
let ptr = HStringHeader::alloc(len.try_into().unwrap());
|
||||
|
||||
// Place each utf-16 character into the buffer and
|
||||
// increase len as we go along.
|
||||
|
@ -79,7 +79,7 @@ impl HSTRING {
|
|||
|
||||
// Write a 0 byte to the end of the buffer.
|
||||
(*ptr).data.offset((*ptr).len as isize).write(0);
|
||||
Ok(Self(ptr))
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
fn as_header(&self) -> Option<&HStringHeader> {
|
||||
|
@ -96,7 +96,7 @@ impl Default for HSTRING {
|
|||
impl Clone for HSTRING {
|
||||
fn clone(&self) -> Self {
|
||||
if let Some(header) = self.as_header() {
|
||||
Self(header.duplicate().unwrap())
|
||||
Self(header.duplicate())
|
||||
} else {
|
||||
Self::new()
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ impl core::fmt::Debug for HSTRING {
|
|||
|
||||
impl From<&str> for HSTRING {
|
||||
fn from(value: &str) -> Self {
|
||||
unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()).unwrap() }
|
||||
unsafe { Self::from_wide_iter(value.encode_utf16(), value.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,6 @@ impl From<&std::ffi::OsStr> for HSTRING {
|
|||
std::os::windows::ffi::OsStrExt::encode_wide(value),
|
||||
value.len(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@ pub struct HStringBuilder(*mut HStringHeader);
|
|||
|
||||
impl HStringBuilder {
|
||||
/// Creates a preallocated `HSTRING` value.
|
||||
pub fn new(len: usize) -> Result<Self> {
|
||||
let header = HStringHeader::alloc(len.try_into()?)?;
|
||||
pub fn new(len: usize) -> Self {
|
||||
let header = HStringHeader::alloc(len.try_into().unwrap());
|
||||
|
||||
if len > 0 {
|
||||
unsafe { core::ptr::write_bytes((*header).data, 0, len) };
|
||||
}
|
||||
|
||||
Ok(Self(header))
|
||||
Self(header)
|
||||
}
|
||||
|
||||
/// Shortens the string by removing any trailing 0 characters.
|
||||
|
|
|
@ -14,9 +14,9 @@ pub struct HStringHeader {
|
|||
}
|
||||
|
||||
impl HStringHeader {
|
||||
pub fn alloc(len: u32) -> Result<*mut Self> {
|
||||
pub fn alloc(len: u32) -> *mut Self {
|
||||
if len == 0 {
|
||||
return Ok(core::ptr::null_mut());
|
||||
return core::ptr::null_mut();
|
||||
}
|
||||
|
||||
// Allocate enough space for header and two bytes per character.
|
||||
|
@ -27,7 +27,7 @@ impl HStringHeader {
|
|||
unsafe { bindings::HeapAlloc(bindings::GetProcessHeap(), 0, bytes) } as *mut Self;
|
||||
|
||||
if header.is_null() {
|
||||
return Err(Error::from_hresult(HRESULT(bindings::E_OUTOFMEMORY)));
|
||||
panic!("allocation failed");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
|
@ -38,7 +38,7 @@ impl HStringHeader {
|
|||
(*header).data = &mut (*header).buffer_start;
|
||||
}
|
||||
|
||||
Ok(header)
|
||||
header
|
||||
}
|
||||
|
||||
pub unsafe fn free(header: *mut Self) {
|
||||
|
@ -49,20 +49,20 @@ impl HStringHeader {
|
|||
bindings::HeapFree(bindings::GetProcessHeap(), 0, header as *mut _);
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> Result<*mut Self> {
|
||||
pub fn duplicate(&self) -> *mut Self {
|
||||
if self.flags & HSTRING_REFERENCE_FLAG == 0 {
|
||||
// If this is not a "fast pass" string then simply increment the reference count.
|
||||
self.count.add_ref();
|
||||
Ok(self as *const Self as *mut Self)
|
||||
self as *const Self as *mut Self
|
||||
} else {
|
||||
// Otherwise, allocate a new string and copy the value into the new string.
|
||||
let copy = Self::alloc(self.len)?;
|
||||
let copy = Self::alloc(self.len);
|
||||
// SAFETY: since we are duplicating the string it is safe to copy all data from self to the initialized `copy`.
|
||||
// We copy `len + 1` characters since `len` does not account for the terminating null character.
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(self.data, (*copy).data, self.len as usize + 1);
|
||||
}
|
||||
Ok(copy)
|
||||
copy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,6 @@
|
|||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
|
||||
pub use windows_result::Result;
|
||||
use windows_result::*;
|
||||
|
||||
mod bstr;
|
||||
pub use bstr::*;
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ impl PCWSTR {
|
|||
/// # Safety
|
||||
///
|
||||
/// See the safety information for `PCWSTR::as_wide`.
|
||||
pub unsafe fn to_hstring(&self) -> Result<HSTRING> {
|
||||
pub unsafe fn to_hstring(&self) -> HSTRING {
|
||||
HSTRING::from_wide(self.as_wide())
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ impl PWSTR {
|
|||
/// # Safety
|
||||
///
|
||||
/// See the safety information for `PWSTR::as_wide`.
|
||||
pub unsafe fn to_hstring(&self) -> Result<HSTRING> {
|
||||
pub unsafe fn to_hstring(&self) -> HSTRING {
|
||||
HSTRING::from_wide(self.as_wide())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
[package]
|
||||
name = "test_bstr"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
doc = false
|
||||
doctest = false
|
||||
|
||||
[dependencies.windows]
|
||||
path = "../../libs/windows"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
]
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
use windows::core::*;
|
||||
use windows::Win32::Foundation::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let b: BSTR = "hello".into();
|
||||
assert_eq!(b, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone() {
|
||||
let a: BSTR = "hello".into();
|
||||
assert!(!a.is_empty());
|
||||
assert!(a.len() == 5);
|
||||
let b = a.clone();
|
||||
assert_eq!(a, "hello");
|
||||
assert_eq!(b, "hello");
|
||||
assert_eq!("hello", a);
|
||||
|
||||
let a = BSTR::default();
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
let b = a.clone();
|
||||
assert_eq!(a, "");
|
||||
assert_eq!(b, "");
|
||||
|
||||
let a = BSTR::new();
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
assert_eq!(a.len(), 0);
|
||||
assert_eq!(a.as_wide().len(), 0);
|
||||
|
||||
let wide = &[0x68, 0x65, 0x6c, 0x6c, 0x6f];
|
||||
let a = BSTR::from_wide(wide).unwrap();
|
||||
assert!(!a.is_empty());
|
||||
assert!(a.len() == 5);
|
||||
assert_eq!(a.as_wide().len(), 5);
|
||||
assert_eq!(a.as_wide(), wide);
|
||||
assert_eq!(a, "hello");
|
||||
|
||||
let a: BSTR = "".into();
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
|
||||
let a: BSTR = unsafe { SysAllocStringLen(None) };
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
|
||||
let a = BSTR::from("a");
|
||||
assert_eq!(a, String::from("a"));
|
||||
assert_eq!(String::from("a"), a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interop() -> Result<()> {
|
||||
unsafe {
|
||||
let b: BSTR = "hello".into();
|
||||
SysAddRefString(&b)?;
|
||||
SysFreeString(&b);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
use windows::core::*;
|
||||
|
||||
#[test]
|
||||
fn hstring_works() {
|
||||
assert_eq!(std::mem::size_of::<HSTRING>(), std::mem::size_of::<usize>());
|
||||
let empty = HSTRING::new();
|
||||
assert!(empty.is_empty());
|
||||
assert!(empty.is_empty());
|
||||
|
||||
let hello = HSTRING::from("Hello");
|
||||
assert!(!hello.is_empty());
|
||||
assert!(hello.len() == 5);
|
||||
|
||||
let rust = hello.to_string();
|
||||
assert!(rust == "Hello");
|
||||
assert!(rust.len() == 5);
|
||||
|
||||
let hello2 = hello.clone();
|
||||
assert!(!hello2.is_empty());
|
||||
assert!(hello2.len() == 5);
|
||||
|
||||
assert!(HSTRING::from("Hello") == HSTRING::from("Hello"));
|
||||
assert!(HSTRING::from("Hello") != HSTRING::from("World"));
|
||||
|
||||
assert!(HSTRING::from("Hello") == "Hello");
|
||||
assert!(HSTRING::from("Hello") != "Hello ");
|
||||
assert!(HSTRING::from("Hello") != "Hell");
|
||||
assert!(HSTRING::from("Hello") != "World");
|
||||
|
||||
assert!(HSTRING::from("Hello").to_string() == String::from("Hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_format() {
|
||||
let value = HSTRING::from("Hello world");
|
||||
assert!(format!("{}", value) == "Hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_invalid_format() {
|
||||
let s = HSTRING::from_wide(&[
|
||||
0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834,
|
||||
])
|
||||
.unwrap();
|
||||
let d = format!("{}", s);
|
||||
assert_eq!(d, "𝄞mus<EFBFBD>ic<EFBFBD>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debug_format() {
|
||||
let value = HSTRING::from("Hello world");
|
||||
assert!(format!("{:?}", value) == r#""Hello world""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_empty_string() {
|
||||
let h = HSTRING::from("");
|
||||
assert!(format!("{}", h).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 = HSTRING::from(o);
|
||||
let d = HSTRING::from_wide(wide_data).unwrap();
|
||||
assert_eq!(h, d);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_os_str_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 o = o.as_os_str();
|
||||
let h = HSTRING::from(o);
|
||||
let d = HSTRING::from_wide(wide_data).unwrap();
|
||||
assert_eq!(h, d);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_path() {
|
||||
let p = std::path::Path::new("/foo/bar");
|
||||
let h = HSTRING::from(p);
|
||||
assert_eq!(h, "/foo/bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_string() {
|
||||
let h = HSTRING::from("test");
|
||||
let s = String::try_from(h).unwrap();
|
||||
assert!(s == "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_string_err() {
|
||||
// 𝄞mu<invalid>ic
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
let h = HSTRING::from_wide(wide_data).unwrap();
|
||||
let err = String::try_from(h);
|
||||
assert!(err.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_string_lossy() {
|
||||
// 𝄞mu<invalid>ic
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
let h = HSTRING::from_wide(wide_data).unwrap();
|
||||
let s = h.to_string_lossy();
|
||||
assert_eq!(s, "𝄞mu<EFBFBD>ic");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_os_string() {
|
||||
// 𝄞mu<invalid>ic
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
let h = HSTRING::from_wide(wide_data).unwrap();
|
||||
let s = h.to_os_string();
|
||||
use std::os::windows::prelude::OsStringExt;
|
||||
assert_eq!(s, std::ffi::OsString::from_wide(wide_data));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_hashing_equal_strings() {
|
||||
// Checks if two strings of identical contents have the same hash
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
let hstring_1 = HSTRING::from("Hello World");
|
||||
let hstring_2 = HSTRING::from("Hello World");
|
||||
|
||||
assert_eq!(hstring_1, hstring_2);
|
||||
|
||||
let mut hasher_1 = DefaultHasher::new();
|
||||
let mut hasher_2 = DefaultHasher::new();
|
||||
|
||||
hstring_1.hash(&mut hasher_1);
|
||||
hstring_2.hash(&mut hasher_2);
|
||||
|
||||
let h1_hash = hasher_1.finish();
|
||||
let h2_hash = hasher_2.finish();
|
||||
|
||||
assert_eq!(h1_hash, h2_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_hashing_different_strings() {
|
||||
// Checks if two strings of different contents have the same hash
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
let hstring_1 = HSTRING::from("Hello World");
|
||||
let hstring_2 = HSTRING::from("Hello World 2");
|
||||
|
||||
assert_ne!(hstring_1, hstring_2);
|
||||
|
||||
let mut hasher_1 = DefaultHasher::new();
|
||||
let mut hasher_2 = DefaultHasher::new();
|
||||
|
||||
hstring_1.hash(&mut hasher_1);
|
||||
hstring_2.hash(&mut hasher_2);
|
||||
|
||||
let h1_hash = hasher_1.finish();
|
||||
let h2_hash = hasher_2.finish();
|
||||
|
||||
assert_ne!(h1_hash, h2_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_equality_combinations() {
|
||||
let h = HSTRING::from("test");
|
||||
let s = String::from("test");
|
||||
let ss: &str = "test";
|
||||
|
||||
assert_eq!(h, s);
|
||||
assert_eq!(&h, s);
|
||||
assert_eq!(h, &s);
|
||||
assert_eq!(&h, &s);
|
||||
|
||||
assert_eq!(s, h);
|
||||
assert_eq!(s, &h);
|
||||
assert_eq!(&s, h);
|
||||
assert_eq!(&s, &h);
|
||||
|
||||
assert_eq!(h, *ss);
|
||||
assert_eq!(&h, *ss);
|
||||
assert_eq!(h, ss);
|
||||
assert_eq!(&h, ss);
|
||||
|
||||
assert_eq!(*ss, h);
|
||||
assert_eq!(*ss, &h);
|
||||
assert_eq!(ss, h);
|
||||
assert_eq!(ss, &h);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_osstring_equality_combinations() {
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
let h = HSTRING::from_wide(wide_data).unwrap();
|
||||
use std::os::windows::prelude::OsStringExt;
|
||||
let s = std::ffi::OsString::from_wide(wide_data);
|
||||
let ss = s.as_os_str();
|
||||
|
||||
assert_eq!(h, s);
|
||||
assert_eq!(&h, s);
|
||||
assert_eq!(h, &s);
|
||||
assert_eq!(&h, &s);
|
||||
|
||||
assert_eq!(s, h);
|
||||
assert_eq!(s, &h);
|
||||
assert_eq!(&s, h);
|
||||
assert_eq!(&s, &h);
|
||||
|
||||
assert_eq!(h, *ss);
|
||||
assert_eq!(&h, *ss);
|
||||
assert_eq!(h, ss);
|
||||
assert_eq!(&h, ss);
|
||||
|
||||
assert_eq!(*ss, h);
|
||||
assert_eq!(*ss, &h);
|
||||
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);
|
||||
|
||||
let result = WindowsConcatString(&hey, &world)?;
|
||||
assert_eq!(result, "HeyWorld");
|
||||
|
||||
let result = WindowsCreateString(Some(&hey.as_wide()))?;
|
||||
assert_eq!(result, "Hey");
|
||||
|
||||
let result = WindowsDuplicateString(&hey)?;
|
||||
assert_eq!(result, "Hey");
|
||||
|
||||
assert_eq!(WindowsGetStringLen(&hey), 3);
|
||||
assert_eq!(WindowsGetStringLen(&world), 5);
|
||||
|
||||
assert_eq!(WindowsIsStringEmpty(&HSTRING::new()), true);
|
||||
assert_eq!(WindowsIsStringEmpty(&HSTRING::default()), true);
|
||||
assert_eq!(WindowsIsStringEmpty(&world), false);
|
||||
|
||||
let mut len = 0;
|
||||
let buffer = WindowsGetStringRawBuffer(&world, Some(&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 raw bindings 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 =
|
||||
sys::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 raw bindings to make one
|
||||
// and thereby excercise the windows::core::HSTRING support for HSTRING reference duplication.
|
||||
let mut header: sys::HSTRING_HEADER = std::mem::zeroed();
|
||||
let mut stack_hstring: sys::HSTRING = std::mem::zeroed();
|
||||
let hresult = sys::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<HSTRING> = 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, Some(&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(())
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
windows_targets::link!("api-ms-win-core-winrt-string-l1-1-0.dll" "system" fn WindowsCreateStringReference(sourcestring: PCWSTR, length: u32, hstringheader: *mut HSTRING_HEADER, string: *mut HSTRING) -> HRESULT);
|
||||
windows_targets::link!("api-ms-win-core-winrt-string-l1-1-0.dll" "system" fn WindowsDeleteString(string: HSTRING) -> HRESULT);
|
||||
|
||||
pub type HRESULT = i32;
|
||||
pub type HSTRING = *mut core::ffi::c_void;
|
||||
pub type PCWSTR = *const u16;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HSTRING_HEADER {
|
||||
pub flags: u32,
|
||||
pub length: u32,
|
||||
pub padding1: u32,
|
||||
pub padding2: u32,
|
||||
pub data: isize,
|
||||
}
|
||||
}
|
|
@ -10,3 +10,16 @@ doctest = false
|
|||
|
||||
[dependencies.windows-strings]
|
||||
path = "../../libs/strings"
|
||||
|
||||
[dependencies.windows-targets]
|
||||
path = "../../libs/targets"
|
||||
|
||||
[dependencies.windows]
|
||||
path = "../../libs/windows"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_WinRT",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
helpers = { package = "test_helpers", path = "../helpers" }
|
||||
|
|
|
@ -1,9 +1,62 @@
|
|||
use windows::{core::Result, Win32::Foundation::*};
|
||||
use windows_strings::*;
|
||||
|
||||
#[test]
|
||||
fn bstr() -> Result<()> {
|
||||
let s = BSTR::from("hello");
|
||||
assert_eq!(s.len(), 5);
|
||||
|
||||
Ok(())
|
||||
fn test() {
|
||||
let b: BSTR = "hello".into();
|
||||
assert_eq!(b, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone() {
|
||||
let a: BSTR = "hello".into();
|
||||
assert!(!a.is_empty());
|
||||
assert!(a.len() == 5);
|
||||
let b = a.clone();
|
||||
assert_eq!(a, "hello");
|
||||
assert_eq!(b, "hello");
|
||||
assert_eq!("hello", a);
|
||||
|
||||
let a = BSTR::default();
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
let b = a.clone();
|
||||
assert_eq!(a, "");
|
||||
assert_eq!(b, "");
|
||||
|
||||
let a = BSTR::new();
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
assert_eq!(a.len(), 0);
|
||||
assert_eq!(a.as_wide().len(), 0);
|
||||
|
||||
let wide = &[0x68, 0x65, 0x6c, 0x6c, 0x6f];
|
||||
let a = BSTR::from_wide(wide);
|
||||
assert!(!a.is_empty());
|
||||
assert!(a.len() == 5);
|
||||
assert_eq!(a.as_wide().len(), 5);
|
||||
assert_eq!(a.as_wide(), wide);
|
||||
assert_eq!(a, "hello");
|
||||
|
||||
let a: BSTR = "".into();
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
|
||||
let a: BSTR = unsafe { SysAllocStringLen(None) };
|
||||
assert!(a.is_empty());
|
||||
assert!(a.len() == 0);
|
||||
|
||||
let a = BSTR::from("a");
|
||||
assert_eq!(a, String::from("a"));
|
||||
assert_eq!(String::from("a"), a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interop() -> Result<()> {
|
||||
unsafe {
|
||||
let b: BSTR = "hello".into();
|
||||
SysAddRefString(&b)?;
|
||||
SysFreeString(&b);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +1,311 @@
|
|||
use windows::core::Result;
|
||||
use windows_strings::*;
|
||||
|
||||
#[test]
|
||||
fn hstring() -> Result<()> {
|
||||
let s = HSTRING::from("hello");
|
||||
assert_eq!(s.len(), 5);
|
||||
assert_eq!(s.as_wide().len(), 5);
|
||||
fn hstring_works() {
|
||||
assert_eq!(std::mem::size_of::<HSTRING>(), std::mem::size_of::<usize>());
|
||||
let empty = HSTRING::new();
|
||||
assert!(empty.is_empty());
|
||||
assert!(empty.is_empty());
|
||||
|
||||
Ok(())
|
||||
let hello = HSTRING::from("Hello");
|
||||
assert!(!hello.is_empty());
|
||||
assert!(hello.len() == 5);
|
||||
|
||||
let rust = hello.to_string();
|
||||
assert!(rust == "Hello");
|
||||
assert!(rust.len() == 5);
|
||||
|
||||
let hello2 = hello.clone();
|
||||
assert!(!hello2.is_empty());
|
||||
assert!(hello2.len() == 5);
|
||||
|
||||
assert!(HSTRING::from("Hello") == HSTRING::from("Hello"));
|
||||
assert!(HSTRING::from("Hello") != HSTRING::from("World"));
|
||||
|
||||
assert!(HSTRING::from("Hello") == "Hello");
|
||||
assert!(HSTRING::from("Hello") != "Hello ");
|
||||
assert!(HSTRING::from("Hello") != "Hell");
|
||||
assert!(HSTRING::from("Hello") != "World");
|
||||
|
||||
assert!(HSTRING::from("Hello").to_string() == String::from("Hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_builder() -> Result<()> {
|
||||
// Dropping a builder is fine.
|
||||
_ = HStringBuilder::new(10)?;
|
||||
fn display_format() {
|
||||
let value = HSTRING::from("Hello world");
|
||||
assert!(format!("{}", value) == "Hello world");
|
||||
}
|
||||
|
||||
// A zero length builder is also fine.
|
||||
let b = HStringBuilder::new(0)?;
|
||||
let h: HSTRING = b.into();
|
||||
assert!(h.is_empty());
|
||||
#[test]
|
||||
fn display_invalid_format() {
|
||||
let s = HSTRING::from_wide(&[
|
||||
0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834,
|
||||
]);
|
||||
let d = format!("{}", s);
|
||||
assert_eq!(d, "𝄞mus<EFBFBD>ic<EFBFBD>");
|
||||
}
|
||||
|
||||
// Trimming a zero length builder is also fine.
|
||||
let mut b = HStringBuilder::new(0)?;
|
||||
b.trim_end();
|
||||
let h: HSTRING = b.into();
|
||||
assert!(h.is_empty());
|
||||
#[test]
|
||||
fn debug_format() {
|
||||
let value = HSTRING::from("Hello world");
|
||||
assert!(format!("{:?}", value) == r#""Hello world""#);
|
||||
}
|
||||
|
||||
// This depends on DerefMut.
|
||||
const HELLO: [u16; 5] = [0x48, 0x65, 0x6C, 0x6C, 0x6F];
|
||||
let mut b = HStringBuilder::new(5)?;
|
||||
b.copy_from_slice(&HELLO);
|
||||
let h: HSTRING = b.into();
|
||||
assert_eq!(&h, "Hello");
|
||||
#[test]
|
||||
fn from_empty_string() {
|
||||
let h = HSTRING::from("");
|
||||
assert!(format!("{}", h).is_empty());
|
||||
}
|
||||
|
||||
// HSTRING can handle embedded nulls.
|
||||
const HELLO00: [u16; 7] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00];
|
||||
let mut b = HStringBuilder::new(7)?;
|
||||
b.copy_from_slice(&HELLO00);
|
||||
let h: HSTRING = b.into();
|
||||
assert_eq!(h.len(), 7);
|
||||
assert_eq!(h.as_wide(), HELLO00);
|
||||
#[test]
|
||||
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 = HSTRING::from(o);
|
||||
let d = HSTRING::from_wide(wide_data);
|
||||
assert_eq!(h, d);
|
||||
}
|
||||
|
||||
// But trim_end can avoid that.
|
||||
let mut b = HStringBuilder::new(7)?;
|
||||
b.copy_from_slice(&HELLO00);
|
||||
b.trim_end();
|
||||
let h: HSTRING = b.into();
|
||||
assert_eq!(h.len(), 5);
|
||||
assert_eq!(h.as_wide(), HELLO);
|
||||
#[test]
|
||||
fn from_os_str_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 o = o.as_os_str();
|
||||
let h = HSTRING::from(o);
|
||||
let d = HSTRING::from_wide(wide_data);
|
||||
assert_eq!(h, d);
|
||||
}
|
||||
|
||||
// HStringBuilder will initialize memory to zero.
|
||||
let b = HStringBuilder::new(5)?;
|
||||
assert_eq!(*b, [0, 0, 0, 0, 0]);
|
||||
#[test]
|
||||
fn from_path() {
|
||||
let p = std::path::Path::new("/foo/bar");
|
||||
let h = HSTRING::from(p);
|
||||
assert_eq!(h, "/foo/bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_string() {
|
||||
let h = HSTRING::from("test");
|
||||
let s = String::try_from(h).unwrap();
|
||||
assert!(s == "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_string_err() {
|
||||
// 𝄞mu<invalid>ic
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
let h = HSTRING::from_wide(wide_data);
|
||||
let err = String::try_from(h);
|
||||
assert!(err.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_string_lossy() {
|
||||
// 𝄞mu<invalid>ic
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
let h = HSTRING::from_wide(wide_data);
|
||||
let s = h.to_string_lossy();
|
||||
assert_eq!(s, "𝄞mu<EFBFBD>ic");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_to_os_string() {
|
||||
// 𝄞mu<invalid>ic
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
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));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_hashing_equal_strings() {
|
||||
// Checks if two strings of identical contents have the same hash
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
let hstring_1 = HSTRING::from("Hello World");
|
||||
let hstring_2 = HSTRING::from("Hello World");
|
||||
|
||||
assert_eq!(hstring_1, hstring_2);
|
||||
|
||||
let mut hasher_1 = DefaultHasher::new();
|
||||
let mut hasher_2 = DefaultHasher::new();
|
||||
|
||||
hstring_1.hash(&mut hasher_1);
|
||||
hstring_2.hash(&mut hasher_2);
|
||||
|
||||
let h1_hash = hasher_1.finish();
|
||||
let h2_hash = hasher_2.finish();
|
||||
|
||||
assert_eq!(h1_hash, h2_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_hashing_different_strings() {
|
||||
// Checks if two strings of different contents have the same hash
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
|
||||
let hstring_1 = HSTRING::from("Hello World");
|
||||
let hstring_2 = HSTRING::from("Hello World 2");
|
||||
|
||||
assert_ne!(hstring_1, hstring_2);
|
||||
|
||||
let mut hasher_1 = DefaultHasher::new();
|
||||
let mut hasher_2 = DefaultHasher::new();
|
||||
|
||||
hstring_1.hash(&mut hasher_1);
|
||||
hstring_2.hash(&mut hasher_2);
|
||||
|
||||
let h1_hash = hasher_1.finish();
|
||||
let h2_hash = hasher_2.finish();
|
||||
|
||||
assert_ne!(h1_hash, h2_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_equality_combinations() {
|
||||
let h = HSTRING::from("test");
|
||||
let s = String::from("test");
|
||||
let ss: &str = "test";
|
||||
|
||||
assert_eq!(h, s);
|
||||
assert_eq!(&h, s);
|
||||
assert_eq!(h, &s);
|
||||
assert_eq!(&h, &s);
|
||||
|
||||
assert_eq!(s, h);
|
||||
assert_eq!(s, &h);
|
||||
assert_eq!(&s, h);
|
||||
assert_eq!(&s, &h);
|
||||
|
||||
assert_eq!(h, *ss);
|
||||
assert_eq!(&h, *ss);
|
||||
assert_eq!(h, ss);
|
||||
assert_eq!(&h, ss);
|
||||
|
||||
assert_eq!(*ss, h);
|
||||
assert_eq!(*ss, &h);
|
||||
assert_eq!(ss, h);
|
||||
assert_eq!(ss, &h);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_osstring_equality_combinations() {
|
||||
let wide_data = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
|
||||
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();
|
||||
|
||||
assert_eq!(h, s);
|
||||
assert_eq!(&h, s);
|
||||
assert_eq!(h, &s);
|
||||
assert_eq!(&h, &s);
|
||||
|
||||
assert_eq!(s, h);
|
||||
assert_eq!(s, &h);
|
||||
assert_eq!(&s, h);
|
||||
assert_eq!(&s, &h);
|
||||
|
||||
assert_eq!(h, *ss);
|
||||
assert_eq!(&h, *ss);
|
||||
assert_eq!(h, ss);
|
||||
assert_eq!(&h, ss);
|
||||
|
||||
assert_eq!(*ss, h);
|
||||
assert_eq!(*ss, &h);
|
||||
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);
|
||||
|
||||
let result = WindowsConcatString(&hey, &world)?;
|
||||
assert_eq!(result, "HeyWorld");
|
||||
|
||||
let result = WindowsCreateString(Some(&hey.as_wide()))?;
|
||||
assert_eq!(result, "Hey");
|
||||
|
||||
let result = WindowsDuplicateString(&hey)?;
|
||||
assert_eq!(result, "Hey");
|
||||
|
||||
assert_eq!(WindowsGetStringLen(&hey), 3);
|
||||
assert_eq!(WindowsGetStringLen(&world), 5);
|
||||
|
||||
assert_eq!(WindowsIsStringEmpty(&HSTRING::new()), true);
|
||||
assert_eq!(WindowsIsStringEmpty(&HSTRING::default()), true);
|
||||
assert_eq!(WindowsIsStringEmpty(&world), false);
|
||||
|
||||
let mut len = 0;
|
||||
let buffer = WindowsGetStringRawBuffer(&world, Some(&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 raw bindings 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 =
|
||||
sys::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 raw bindings to make one
|
||||
// and thereby excercise the windows::core::HSTRING support for HSTRING reference duplication.
|
||||
let mut header: sys::HSTRING_HEADER = std::mem::zeroed();
|
||||
let mut stack_hstring: sys::HSTRING = std::mem::zeroed();
|
||||
let hresult = sys::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<HSTRING> = 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, Some(&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(())
|
||||
}
|
||||
}
|
||||
|
||||
mod sys {
|
||||
windows_targets::link!("api-ms-win-core-winrt-string-l1-1-0.dll" "system" fn WindowsCreateStringReference(sourcestring: PCWSTR, length: u32, hstringheader: *mut HSTRING_HEADER, string: *mut HSTRING) -> HRESULT);
|
||||
windows_targets::link!("api-ms-win-core-winrt-string-l1-1-0.dll" "system" fn WindowsDeleteString(string: HSTRING) -> HRESULT);
|
||||
|
||||
pub type HRESULT = i32;
|
||||
pub type HSTRING = *mut core::ffi::c_void;
|
||||
pub type PCWSTR = *const u16;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HSTRING_HEADER {
|
||||
pub flags: u32,
|
||||
pub length: u32,
|
||||
pub padding1: u32,
|
||||
pub padding2: u32,
|
||||
pub data: isize,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
use windows_strings::*;
|
||||
|
||||
#[test]
|
||||
fn hstring() {
|
||||
let s = HSTRING::from("hello");
|
||||
assert_eq!(s.len(), 5);
|
||||
assert_eq!(s.as_wide().len(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hstring_builder() {
|
||||
// Dropping a builder is fine.
|
||||
_ = HStringBuilder::new(10);
|
||||
|
||||
// A zero length builder is also fine.
|
||||
let b = HStringBuilder::new(0);
|
||||
let h: HSTRING = b.into();
|
||||
assert!(h.is_empty());
|
||||
|
||||
// Trimming a zero length builder is also fine.
|
||||
let mut b = HStringBuilder::new(0);
|
||||
b.trim_end();
|
||||
let h: HSTRING = b.into();
|
||||
assert!(h.is_empty());
|
||||
|
||||
// This depends on DerefMut.
|
||||
const HELLO: [u16; 5] = [0x48, 0x65, 0x6C, 0x6C, 0x6F];
|
||||
let mut b = HStringBuilder::new(5);
|
||||
b.copy_from_slice(&HELLO);
|
||||
let h: HSTRING = b.into();
|
||||
assert_eq!(&h, "Hello");
|
||||
|
||||
// HSTRING can handle embedded nulls.
|
||||
const HELLO00: [u16; 7] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x00];
|
||||
let mut b = HStringBuilder::new(7);
|
||||
b.copy_from_slice(&HELLO00);
|
||||
let h: HSTRING = b.into();
|
||||
assert_eq!(h.len(), 7);
|
||||
assert_eq!(h.as_wide(), HELLO00);
|
||||
|
||||
// But trim_end can avoid that.
|
||||
let mut b = HStringBuilder::new(7);
|
||||
b.copy_from_slice(&HELLO00);
|
||||
b.trim_end();
|
||||
let h: HSTRING = b.into();
|
||||
assert_eq!(h.len(), 5);
|
||||
assert_eq!(h.as_wide(), HELLO);
|
||||
|
||||
// HStringBuilder will initialize memory to zero.
|
||||
let b = HStringBuilder::new(5);
|
||||
assert_eq!(*b, [0, 0, 0, 0, 0]);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use windows::core::Result;
|
||||
use windows_strings::*;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -19,7 +19,7 @@ fn test() -> Result<()> {
|
|||
);
|
||||
|
||||
let p: PCWSTR = w!("world");
|
||||
let s: HSTRING = unsafe { p.to_hstring()? };
|
||||
let s: HSTRING = unsafe { p.to_hstring() };
|
||||
assert_eq!("world", s);
|
||||
|
||||
Ok(())
|
|
@ -19,7 +19,7 @@ fn test() -> Result<()> {
|
|||
);
|
||||
|
||||
let p = PWSTR::from_raw(w!("world").as_ptr() as *mut _);
|
||||
let s: HSTRING = unsafe { p.to_hstring()? };
|
||||
let s: HSTRING = unsafe { p.to_hstring() };
|
||||
assert_eq!("world", s);
|
||||
|
||||
Ok(())
|
|
@ -8,7 +8,6 @@
|
|||
Windows.Win32.Foundation.E_BOUNDS
|
||||
Windows.Win32.Foundation.E_INVALIDARG
|
||||
Windows.Win32.Foundation.E_NOINTERFACE
|
||||
Windows.Win32.Foundation.E_OUTOFMEMORY
|
||||
Windows.Win32.Foundation.E_POINTER
|
||||
Windows.Win32.Foundation.JSCRIPT_E_CANTEXECUTE
|
||||
Windows.Win32.Foundation.RPC_E_DISCONNECTED
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
--config flatten sys minimal no-bindgen-comment
|
||||
|
||||
--filter
|
||||
Windows.Win32.Foundation.E_OUTOFMEMORY
|
||||
Windows.Win32.Foundation.SysAllocStringLen
|
||||
Windows.Win32.Foundation.SysFreeString
|
||||
Windows.Win32.Foundation.SysStringLen
|
||||
|
|
Загрузка…
Ссылка в новой задаче