From 3ee83d5d87114091f774e1b534afcb122eb1590d Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Mon, 8 Jan 2024 17:13:29 -0600 Subject: [PATCH] Implement `From` and `From<&T>` for interface hierarchies (#2779) --- crates/libs/core/src/imp/generic_factory.rs | 10 +- crates/libs/core/src/imp/mod.rs | 10 ++ crates/libs/core/src/inspectable.rs | 4 +- crates/libs/core/src/unknown.rs | 4 - crates/tests/implement/tests/identity.rs | 2 +- crates/tests/implement/tests/identity_from.rs | 97 +++++++++++++++++++ 6 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 crates/tests/implement/tests/identity_from.rs diff --git a/crates/libs/core/src/imp/generic_factory.rs b/crates/libs/core/src/imp/generic_factory.rs index dee1bad4fd..7601a6a81f 100644 --- a/crates/libs/core/src/imp/generic_factory.rs +++ b/crates/libs/core/src/imp/generic_factory.rs @@ -2,9 +2,8 @@ use crate::Interface; // A streamlined version of the IActivationFactory interface used by WinRT class factories used internally by the windows crate // to simplify code generation. Components should implement the `IActivationFactory` interface published by the windows crate. -#[repr(transparent)] -#[derive(Clone, PartialEq, Eq)] -pub struct IGenericFactory(crate::IUnknown); +super::com_interface!(IGenericFactory, IGenericFactory_Vtbl, 0x00000035_0000_0000_c000_000000000046); +super::interface_hierarchy!(IGenericFactory, crate::IUnknown, crate::IInspectable); impl IGenericFactory { pub fn ActivateInstance(&self) -> crate::Result { @@ -20,8 +19,3 @@ pub struct IGenericFactory_Vtbl { pub base__: crate::IInspectable_Vtbl, pub ActivateInstance: unsafe extern "system" fn(this: *mut std::ffi::c_void, instance: *mut *mut std::ffi::c_void) -> crate::HRESULT, } - -unsafe impl Interface for IGenericFactory { - type Vtable = IGenericFactory_Vtbl; - const IID: crate::GUID = crate::GUID::from_u128(0x00000035_0000_0000_c000_000000000046); -} diff --git a/crates/libs/core/src/imp/mod.rs b/crates/libs/core/src/imp/mod.rs index 7ed833360d..80d8bb8a00 100644 --- a/crates/libs/core/src/imp/mod.rs +++ b/crates/libs/core/src/imp/mod.rs @@ -35,6 +35,16 @@ pub fn wide_trim_end(mut wide: &[u16]) -> &[u16] { macro_rules! interface_hierarchy { ($child:ident, $parent:ty) => { impl ::windows_core::CanInto<$parent> for $child {} + impl ::core::convert::From<&$child> for &$parent { + fn from(value: &$child) -> Self { + unsafe { ::core::mem::transmute(value) } + } + } + impl ::core::convert::From<$child> for $parent { + fn from(value: $child) -> Self { + unsafe { ::core::mem::transmute(value) } + } + } }; ($child:ident, $first:ty, $($rest:ty),+) => { $crate::imp::interface_hierarchy!($child, $first); diff --git a/crates/libs/core/src/inspectable.rs b/crates/libs/core/src/inspectable.rs index 19719050dd..f03bbb136e 100644 --- a/crates/libs/core/src/inspectable.rs +++ b/crates/libs/core/src/inspectable.rs @@ -8,6 +8,8 @@ use super::*; #[derive(Clone, PartialEq, Eq)] pub struct IInspectable(pub IUnknown); +interface_hierarchy!(IInspectable, IUnknown); + impl IInspectable { /// Returns the canonical type name for the underlying object. pub fn GetRuntimeClassName(&self) -> Result { @@ -42,8 +44,6 @@ unsafe impl Interface for IInspectable { const IID: GUID = GUID::from_u128(0xaf86e2e0_b12d_4c6a_9c5a_d7aa65101e90); } -impl CanInto for IInspectable {} - impl RuntimeType for IInspectable { const SIGNATURE: crate::imp::ConstBuffer = crate::imp::ConstBuffer::from_slice(b"cinterface(IInspectable)"); } diff --git a/crates/libs/core/src/unknown.rs b/crates/libs/core/src/unknown.rs index 67261ccf51..be5ae80726 100644 --- a/crates/libs/core/src/unknown.rs +++ b/crates/libs/core/src/unknown.rs @@ -15,10 +15,6 @@ pub struct IUnknown_Vtbl { pub Release: unsafe extern "system" fn(this: *mut std::ffi::c_void) -> u32, } -// impl TypeKind for IUnknown { -// type TypeKind = ReferenceType; -// } - unsafe impl Interface for IUnknown { type Vtable = IUnknown_Vtbl; const IID: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046); diff --git a/crates/tests/implement/tests/identity.rs b/crates/tests/implement/tests/identity.rs index 5f8bdb05f0..59119992f2 100644 --- a/crates/tests/implement/tests/identity.rs +++ b/crates/tests/implement/tests/identity.rs @@ -73,7 +73,7 @@ fn identity() -> Result<()> { assert_eq!(c.GetRuntimeClassName()?, "Windows.Foundation.IStringable"); let d: IClosable = a.cast()?; - let e: &IInspectable = std::mem::transmute(&d); + let e: &IInspectable = (&d).into(); assert_eq!(e.GetRuntimeClassName()?, "Windows.Foundation.IClosable"); let f: IInspectable = e.cast()?; diff --git a/crates/tests/implement/tests/identity_from.rs b/crates/tests/implement/tests/identity_from.rs new file mode 100644 index 0000000000..3985ca8f23 --- /dev/null +++ b/crates/tests/implement/tests/identity_from.rs @@ -0,0 +1,97 @@ +#![allow(non_snake_case)] + +use windows::{core::*, Foundation::*}; + +#[implement(IStringable, IClosable)] +struct Test; + +impl IStringable_Impl for Test { + fn ToString(&self) -> Result { + todo!() + } +} + +impl IClosable_Impl for Test { + fn Close(&self) -> Result<()> { + todo!() + } +} + +// This tests that the interface_hierarchy macro correctly implement From and From<&T> for interfaces. +#[test] +fn identity_from() -> Result<()> { + { + let inspectable: IInspectable = Test.into(); + assert_eq!( + inspectable.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + + let unknown = Into::<&IUnknown>::into(&inspectable); + assert_eq!( + unknown.cast::()?.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + + let unknown = Into::::into(inspectable); + assert_eq!( + unknown.cast::()?.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + } + { + let stringable: IStringable = Test.into(); + + let inspectable = Into::<&IInspectable>::into(&stringable); + assert_eq!( + inspectable.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + + let inspectable = Into::::into(stringable.clone()); + assert_eq!( + inspectable.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + + let unknown = Into::<&IUnknown>::into(&stringable); + assert_eq!( + unknown.cast::()?.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + + let unknown = Into::::into(stringable); + assert_eq!( + unknown.cast::()?.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + } + { + let closable: IClosable = Test.into(); + + let inspectable = Into::<&IInspectable>::into(&closable); + assert_eq!( + inspectable.GetRuntimeClassName()?, + "Windows.Foundation.IClosable" + ); + + let inspectable = Into::::into(closable.clone()); + assert_eq!( + inspectable.GetRuntimeClassName()?, + "Windows.Foundation.IClosable" + ); + + let unknown = Into::<&IUnknown>::into(&closable); + assert_eq!( + unknown.cast::()?.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + + let unknown = Into::::into(closable); + assert_eq!( + unknown.cast::()?.GetRuntimeClassName()?, + "Windows.Foundation.IStringable" + ); + } + Ok(()) +}