diff --git a/crates/libs/core/src/imp/bindings.rs b/crates/libs/core/src/imp/bindings.rs index 5c4b4e2d5f..1148de01b7 100644 --- a/crates/libs/core/src/imp/bindings.rs +++ b/crates/libs/core/src/imp/bindings.rs @@ -21,6 +21,9 @@ windows_targets::link!("ole32.dll" "system" fn PropVariantClear(pvar : *mut PROP windows_targets::link!("ole32.dll" "system" fn PropVariantCopy(pvardest : *mut PROPVARIANT, pvarsrc : *const PROPVARIANT) -> HRESULT); windows_targets::link!("oleaut32.dll" "system" fn VariantClear(pvarg : *mut VARIANT) -> HRESULT); windows_targets::link!("oleaut32.dll" "system" fn VariantCopy(pvargdest : *mut VARIANT, pvargsrc : *const VARIANT) -> HRESULT); +windows_targets::link!("oleaut32.dll" "system" fn SafeArrayCreateVector(vt : VARENUM, llbound : i32, celements : u32) -> *mut SAFEARRAY); +windows_targets::link!("oleaut32.dll" "system" fn SafeArrayDestroy(psa : *const SAFEARRAY) -> HRESULT); +windows_targets::link!("oleaut32.dll" "system" fn SafeArrayPutElement(psa : *const SAFEARRAY, rgindices : *const i32, pv : *const core::ffi::c_void) -> HRESULT); windows_targets::link!("propsys.dll" "system" fn PropVariantCompareEx(propvar1 : *const PROPVARIANT, propvar2 : *const PROPVARIANT, unit : PROPVAR_COMPARE_UNIT, flags : PROPVAR_COMPARE_FLAGS) -> i32); windows_targets::link!("propsys.dll" "system" fn PropVariantToBSTR(propvar : *const PROPVARIANT, pbstrout : *mut BSTR) -> HRESULT); windows_targets::link!("propsys.dll" "system" fn PropVariantToBoolean(propvarin : *const PROPVARIANT, pfret : *mut BOOL) -> HRESULT); @@ -654,6 +657,7 @@ pub struct VERSIONEDSTREAM { pub guidVersion: GUID, pub pStream: *mut core::ffi::c_void, } +pub const VT_ARRAY: VARENUM = 8192u16; pub const VT_BOOL: VARENUM = 11u16; pub const VT_BSTR: VARENUM = 8u16; pub const VT_EMPTY: VARENUM = 0u16; @@ -661,6 +665,7 @@ pub const VT_I1: VARENUM = 16u16; pub const VT_I2: VARENUM = 2u16; pub const VT_I4: VARENUM = 3u16; pub const VT_I8: VARENUM = 20u16; +pub const VT_NULL: VARENUM = 1u16; pub const VT_R4: VARENUM = 4u16; pub const VT_R8: VARENUM = 5u16; pub const VT_UI1: VARENUM = 17u16; diff --git a/crates/libs/core/src/variant.rs b/crates/libs/core/src/variant.rs index ca85aa0c10..018cd381e0 100644 --- a/crates/libs/core/src/variant.rs +++ b/crates/libs/core/src/variant.rs @@ -43,12 +43,28 @@ impl Clone for PROPVARIANT { impl Drop for VARIANT { fn drop(&mut self) { + let var = unsafe { &mut self.0.Anonymous.Anonymous }; + + if var.vt == imp::VT_BSTR { + drop(unsafe { BSTR::from_raw(var.Anonymous.bstrVal) }); + } else if var.vt & imp::VT_ARRAY != 0 { + let _r = unsafe { imp::SafeArrayDestroy(var.Anonymous.parray) }; + } + unsafe { imp::VariantClear(&mut self.0) }; } } impl Drop for PROPVARIANT { fn drop(&mut self) { + let var = unsafe { &mut self.0.Anonymous.Anonymous }; + + if var.vt == imp::VT_BSTR { + drop(unsafe { BSTR::from_raw(var.Anonymous.bstrVal) }); + } else if var.vt & imp::VT_ARRAY != 0 { + let _r = unsafe { imp::SafeArrayDestroy(var.Anonymous.parray) }; + } + unsafe { imp::PropVariantClear(&mut self.0) }; } } @@ -133,10 +149,10 @@ impl Eq for VARIANT {} impl Eq for PROPVARIANT {} impl VARIANT { - /// Create an empty `VARIANT`. + /// Creates an empty `VARIANT`. /// /// This function does not allocate memory. - pub fn new() -> Self { + pub const fn new() -> Self { unsafe { core::mem::zeroed() } } @@ -145,26 +161,41 @@ impl VARIANT { unsafe { self.0.Anonymous.Anonymous.vt == imp::VT_EMPTY } } + /// Creates a null `VARIANT`. + /// + /// This is [`Self::new`] with the `VARENUM` set to `VT_NULL`. Similarly, + /// it does not allocate memory either. + pub const fn null() -> Self { + let mut vt = Self::new(); + vt.0.Anonymous.Anonymous.vt = imp::VT_NULL; + vt + } + + /// Returns true if the `VARIANT` is `VT_NULL`. + pub const fn is_null(&self) -> bool { + unsafe { self.0.Anonymous.Anonymous.vt == imp::VT_NULL } + } + /// Creates a `VARIANT` by taking ownership of the raw data. /// /// # Safety /// /// The raw data must be owned by the caller and represent a valid `VARIANT` data structure. - pub unsafe fn from_raw(raw: imp::VARIANT) -> Self { + pub const unsafe fn from_raw(raw: imp::VARIANT) -> Self { Self(raw) } /// Returns the underlying raw data for the `VARIANT`. - pub fn as_raw(&self) -> &imp::VARIANT { + pub const fn as_raw(&self) -> &imp::VARIANT { &self.0 } } impl PROPVARIANT { - /// Create an empty `PROPVARIANT`. + /// Creates an empty `PROPVARIANT`. /// /// This function does not allocate memory. - pub fn new() -> Self { + pub const fn new() -> Self { unsafe { core::mem::zeroed() } } @@ -173,17 +204,32 @@ impl PROPVARIANT { unsafe { self.0.Anonymous.Anonymous.vt == imp::VT_EMPTY } } + /// Creates a null `PROPVARIANT`. + /// + /// This is [`Self::new`] with the `VARENUM` set to `VT_NULL`. Similarly, + /// it does not allocate memory either. + pub const fn null() -> Self { + let mut vt = Self::new(); + vt.0.Anonymous.Anonymous.vt = imp::VT_NULL; + vt + } + + /// Returns true if the `PROPVARIANT` is `VT_NULL`. + pub const fn is_null(&self) -> bool { + unsafe { self.0.Anonymous.Anonymous.vt == imp::VT_NULL } + } + /// Creates a `PROPVARIANT` by taking ownership of the raw data. /// /// # Safety /// /// The raw data must be owned by the caller and represent a valid `PROPVARIANT` data structure. - pub unsafe fn from_raw(raw: imp::PROPVARIANT) -> Self { + pub const unsafe fn from_raw(raw: imp::PROPVARIANT) -> Self { Self(raw) } /// Returns the underlying raw data for the `PROPVARIANT`. - pub fn as_raw(&self) -> &imp::PROPVARIANT { + pub const fn as_raw(&self) -> &imp::PROPVARIANT { &self.0 } } @@ -328,6 +374,18 @@ impl From<&str> for PROPVARIANT { } } +impl From for VARIANT { + fn from(value: String) -> Self { + BSTR::from(value).into() + } +} + +impl From for PROPVARIANT { + fn from(value: String) -> Self { + BSTR::from(value).into() + } +} + impl TryFrom<&VARIANT> for BSTR { type Error = Error; fn try_from(from: &VARIANT) -> Result { @@ -850,3 +908,233 @@ impl TryFrom<&PROPVARIANT> for f64 { HRESULT(unsafe { imp::PropVariantToDouble(&from.0, &mut value) }).map(|| value) } } + +// VT_ARRAY | VT_BSTR + +impl TryFrom<&[&str]> for VARIANT { + type Error = Error; + + fn try_from(value: &[&str]) -> Result { + if value.is_empty() { + return Ok(Self::new()); + } + + let parray = unsafe { imp::SafeArrayCreateVector(imp::VT_BSTR, 0, value.len() as u32) }; + if parray.is_null() { + return Err(imp::E_OUTOFMEMORY.into()); + } + + for (i, v) in value.iter().enumerate() { + let v = BSTR::from(*v); + let res = + unsafe { imp::SafeArrayPutElement(parray, &(i as i32), v.into_raw() as *const _) }; + + if let Err(err) = HRESULT(res).ok() { + let _r = unsafe { imp::SafeArrayDestroy(parray) }; + return Err(err); + } + } + + Ok(Self(imp::VARIANT { + Anonymous: imp::VARIANT_0 { + Anonymous: imp::VARIANT_0_0 { + vt: imp::VT_ARRAY | imp::VT_BSTR, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: imp::VARIANT_0_0_0 { parray }, + }, + }, + })) + } +} + +impl TryFrom<&[&str]> for PROPVARIANT { + type Error = Error; + + fn try_from(value: &[&str]) -> Result { + if value.is_empty() { + return Ok(Self::new()); + } + + let parray = unsafe { imp::SafeArrayCreateVector(imp::VT_BSTR, 0, value.len() as u32) }; + if parray.is_null() { + return Err(imp::E_OUTOFMEMORY.into()); + } + + for (i, v) in value.iter().enumerate() { + let v = BSTR::from(*v); + let res = + unsafe { imp::SafeArrayPutElement(parray, &(i as i32), v.into_raw() as *const _) }; + + if let Err(err) = HRESULT(res).ok() { + let _r = unsafe { imp::SafeArrayDestroy(parray) }; + return Err(err); + } + } + + Ok(Self(imp::PROPVARIANT { + Anonymous: imp::PROPVARIANT_0 { + Anonymous: imp::PROPVARIANT_0_0 { + vt: imp::VT_ARRAY | imp::VT_BSTR, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: imp::PROPVARIANT_0_0_0 { parray }, + }, + }, + })) + } +} + +impl TryFrom<&[String]> for VARIANT { + type Error = Error; + + fn try_from(value: &[String]) -> Result { + if value.is_empty() { + return Ok(Self::new()); + } + + let parray = unsafe { imp::SafeArrayCreateVector(imp::VT_BSTR, 0, value.len() as u32) }; + if parray.is_null() { + return Err(imp::E_OUTOFMEMORY.into()); + } + + for (i, v) in value.iter().enumerate() { + let v = BSTR::from(v); + let res = + unsafe { imp::SafeArrayPutElement(parray, &(i as i32), v.into_raw() as *const _) }; + + if let Err(err) = HRESULT(res).ok() { + let _r = unsafe { imp::SafeArrayDestroy(parray) }; + return Err(err); + } + } + + Ok(Self(imp::VARIANT { + Anonymous: imp::VARIANT_0 { + Anonymous: imp::VARIANT_0_0 { + vt: imp::VT_ARRAY | imp::VT_BSTR, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: imp::VARIANT_0_0_0 { parray }, + }, + }, + })) + } +} + +impl TryFrom<&[String]> for PROPVARIANT { + type Error = Error; + + fn try_from(value: &[String]) -> Result { + if value.is_empty() { + return Ok(Self::new()); + } + + let parray = unsafe { imp::SafeArrayCreateVector(imp::VT_BSTR, 0, value.len() as u32) }; + if parray.is_null() { + return Err(imp::E_OUTOFMEMORY.into()); + } + + for (i, v) in value.iter().enumerate() { + let v = BSTR::from(v); + let res = + unsafe { imp::SafeArrayPutElement(parray, &(i as i32), v.into_raw() as *const _) }; + + if let Err(err) = HRESULT(res).ok() { + let _r = unsafe { imp::SafeArrayDestroy(parray) }; + return Err(err); + } + } + + Ok(Self(imp::PROPVARIANT { + Anonymous: imp::PROPVARIANT_0 { + Anonymous: imp::PROPVARIANT_0_0 { + vt: imp::VT_ARRAY | imp::VT_BSTR, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: imp::PROPVARIANT_0_0_0 { parray }, + }, + }, + })) + } +} + +// VT_ARRAY | VT_UI1 + +impl TryFrom<&[u8]> for VARIANT { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + if value.is_empty() { + return Ok(Self::new()); + } + + let parray = unsafe { imp::SafeArrayCreateVector(imp::VT_UI1, 0, value.len() as u32) }; + if parray.is_null() { + return Err(imp::E_OUTOFMEMORY.into()); + } + + for (i, v) in value.iter().enumerate() { + let res = + unsafe { imp::SafeArrayPutElement(parray, &(i as i32), v as *const _ as *const _) }; + + if let Err(err) = HRESULT(res).ok() { + let _r = unsafe { imp::SafeArrayDestroy(parray) }; + return Err(err); + } + } + + Ok(Self(imp::VARIANT { + Anonymous: imp::VARIANT_0 { + Anonymous: imp::VARIANT_0_0 { + vt: imp::VT_ARRAY | imp::VT_UI1, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: imp::VARIANT_0_0_0 { parray }, + }, + }, + })) + } +} + +impl TryFrom<&[u8]> for PROPVARIANT { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + if value.is_empty() { + return Ok(Self::new()); + } + + let parray = unsafe { imp::SafeArrayCreateVector(imp::VT_UI1, 0, value.len() as u32) }; + if parray.is_null() { + return Err(imp::E_OUTOFMEMORY.into()); + } + + for (i, v) in value.iter().enumerate() { + let res = + unsafe { imp::SafeArrayPutElement(parray, &(i as i32), v as *const _ as *const _) }; + + if let Err(err) = HRESULT(res).ok() { + let _r = unsafe { imp::SafeArrayDestroy(parray) }; + return Err(err); + } + } + + Ok(Self(imp::PROPVARIANT { + Anonymous: imp::PROPVARIANT_0 { + Anonymous: imp::PROPVARIANT_0_0 { + vt: imp::VT_ARRAY | imp::VT_UI1, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: imp::PROPVARIANT_0_0_0 { parray }, + }, + }, + })) + } +} diff --git a/crates/tests/variant/tests/tests.rs b/crates/tests/variant/tests/tests.rs index 4ba9cfcfad..a705c44e8f 100644 --- a/crates/tests/variant/tests/tests.rs +++ b/crates/tests/variant/tests/tests.rs @@ -9,12 +9,19 @@ fn test_variant() -> Result<()> { let empty: VARIANT = VARIANT::new(); assert!(empty.is_empty()); + assert!(!empty.is_null()); let v = VARIANT::default(); assert!(v.is_empty()); + assert!(!v.is_null()); assert_eq!(VARIANT::new(), VARIANT::default()); + let v = VARIANT::null(); + assert!(v.is_null()); + assert!(!v.is_empty()); + assert_ne!(v, VARIANT::new()); + let v = VARIANT::from(true); assert!(!v.is_empty()); assert_eq!(bool::try_from(&v)?, true); @@ -115,6 +122,17 @@ fn test_variant() -> Result<()> { assert_eq!(VARIANT::from("hello"), VARIANT::from("hello")); assert_ne!(VARIANT::from("hello"), VARIANT::from("goodbye")); + let v = VARIANT::from("hello".to_owned()); + assert_eq!(BSTR::try_from(&v)?, "hello"); + assert_eq!( + VARIANT::from("hello".to_owned()), + VARIANT::from("hello".to_owned()) + ); + assert_ne!( + VARIANT::from("hello".to_owned()), + VARIANT::from("goodbye".to_owned()) + ); + let v = VARIANT::from(3.5f64); assert_eq!(BSTR::try_from(&v)?, "3.5"); @@ -126,6 +144,70 @@ fn test_variant() -> Result<()> { assert_eq!(v, VARIANT::from(3.5f64)); assert_ne!(v, VARIANT::from(true)); + let v = VARIANT::try_from(&["abc", "def", "xyz", "123"][..])?; + assert!(!v.is_empty()); + assert!(!v.is_null()); + assert_eq!( + VARIANT::try_from(&["abc", "def", "xyz", "123"][..])?, + VARIANT::try_from(&["abc", "def", "xyz", "123"][..])? + ); + assert_ne!( + VARIANT::try_from(&["abc", "def", "xyz", "123"][..])?, + VARIANT::try_from(&["hello", "world"][..])? + ); + + let v = VARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..], + )?; + assert!(!v.is_empty()); + assert!(!v.is_null()); + assert_eq!( + VARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..] + )?, + VARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..] + )? + ); + assert_ne!( + VARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..] + )?, + VARIANT::try_from(&["hello".to_owned(), "world".to_owned()][..])? + ); + + let v = VARIANT::try_from(&[1, 2, 3, 4][..])?; + assert!(!v.is_empty()); + assert!(!v.is_null()); + assert_eq!( + VARIANT::try_from(&[10, 20, 30, 40][..])?, + VARIANT::try_from(&[10, 20, 30, 40][..])? + ); + assert_ne!( + VARIANT::try_from(&[1, 2, 3, 4][..])?, + VARIANT::try_from(&[123, 210][..])? + ); + Ok(()) } @@ -135,12 +217,19 @@ fn test_propvariant() -> Result<()> { let empty: PROPVARIANT = PROPVARIANT::new(); assert!(empty.is_empty()); + assert!(!empty.is_null()); let v = PROPVARIANT::default(); assert!(v.is_empty()); + assert!(!v.is_null()); assert_eq!(PROPVARIANT::new(), PROPVARIANT::default()); + let v = PROPVARIANT::null(); + assert!(v.is_null()); + assert!(!v.is_empty()); + assert_ne!(v, PROPVARIANT::new()); + let v = PROPVARIANT::from(true); assert!(!v.is_empty()); assert_eq!(bool::try_from(&v)?, true); @@ -247,6 +336,17 @@ fn test_propvariant() -> Result<()> { assert_eq!(PROPVARIANT::from("hello"), PROPVARIANT::from("hello")); assert_ne!(PROPVARIANT::from("hello"), PROPVARIANT::from("goodbye")); + let v = PROPVARIANT::from("hello".to_owned()); + assert_eq!(BSTR::try_from(&v)?, "hello"); + assert_eq!( + PROPVARIANT::from("hello".to_owned()), + PROPVARIANT::from("hello".to_owned()) + ); + assert_ne!( + PROPVARIANT::from("hello".to_owned()), + PROPVARIANT::from("goodbye".to_owned()) + ); + let v = PROPVARIANT::from(3.5f64); assert_eq!(BSTR::try_from(&v)?, "3.5"); @@ -258,5 +358,69 @@ fn test_propvariant() -> Result<()> { assert_eq!(v, PROPVARIANT::from(3.5f64)); assert_ne!(v, PROPVARIANT::from(true)); + let v = PROPVARIANT::try_from(&["abc", "def", "xyz", "123"][..])?; + assert!(!v.is_empty()); + assert!(!v.is_null()); + assert_eq!( + PROPVARIANT::try_from(&["abc", "def", "xyz", "123"][..])?, + PROPVARIANT::try_from(&["abc", "def", "xyz", "123"][..])? + ); + assert_ne!( + PROPVARIANT::try_from(&["abc", "def", "xyz", "123"][..])?, + PROPVARIANT::try_from(&["hello", "world"][..])? + ); + + let v = PROPVARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..], + )?; + assert!(!v.is_empty()); + assert!(!v.is_null()); + assert_eq!( + PROPVARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..] + )?, + PROPVARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..] + )? + ); + assert_ne!( + PROPVARIANT::try_from( + &[ + "abc".to_owned(), + "def".to_owned(), + "xyz".to_owned(), + "123".to_owned(), + ][..] + )?, + PROPVARIANT::try_from(&["hello".to_owned(), "world".to_owned()][..])? + ); + + let v = PROPVARIANT::try_from(&[1, 2, 3, 4][..])?; + assert!(!v.is_empty()); + assert!(!v.is_null()); + assert_eq!( + PROPVARIANT::try_from(&[10, 20, 30, 40][..])?, + PROPVARIANT::try_from(&[10, 20, 30, 40][..])? + ); + assert_ne!( + PROPVARIANT::try_from(&[1, 2, 3, 4][..])?, + PROPVARIANT::try_from(&[123, 210][..])? + ); + Ok(()) } diff --git a/crates/tools/bindings/src/core.txt b/crates/tools/bindings/src/core.txt index 0e624d5057..7f71ff59b6 100644 --- a/crates/tools/bindings/src/core.txt +++ b/crates/tools/bindings/src/core.txt @@ -28,6 +28,9 @@ Windows.Win32.System.LibraryLoader.GetProcAddress Windows.Win32.System.LibraryLoader.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS Windows.Win32.System.LibraryLoader.LoadLibraryExA + Windows.Win32.System.Ole.SafeArrayCreateVector + Windows.Win32.System.Ole.SafeArrayDestroy + Windows.Win32.System.Ole.SafeArrayPutElement Windows.Win32.System.Threading.CreateEventW Windows.Win32.System.Threading.SetEvent Windows.Win32.System.Threading.WaitForSingleObject @@ -42,6 +45,7 @@ Windows.Win32.System.Variant.VariantToUInt16 Windows.Win32.System.Variant.VariantToUInt32 Windows.Win32.System.Variant.VariantToUInt64 + Windows.Win32.System.Variant.VT_ARRAY Windows.Win32.System.Variant.VT_BOOL Windows.Win32.System.Variant.VT_BSTR Windows.Win32.System.Variant.VT_EMPTY @@ -49,6 +53,7 @@ Windows.Win32.System.Variant.VT_I2 Windows.Win32.System.Variant.VT_I4 Windows.Win32.System.Variant.VT_I8 + Windows.Win32.System.Variant.VT_NULL Windows.Win32.System.Variant.VT_R4 Windows.Win32.System.Variant.VT_R8 Windows.Win32.System.Variant.VT_UI1