From 91aa9984d810fb4d4febbdc5dd7de84c1b892822 Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Sun, 2 Jun 2024 16:40:26 -0700 Subject: [PATCH 1/2] basic implementation of impl-on-outer Switch syntax to use "@ Outer" for enabling outer impl update bindgen output for windows and sys more bindings updated revert generated code --- crates/libs/bindgen/src/rust/implements.rs | 23 +++- crates/libs/core/src/com_object.rs | 54 ++++++++ crates/libs/implement/src/lib.rs | 61 +++++++- crates/libs/interface/src/lib.rs | 80 +++++++---- .../json_validator_winrt/src/bindings.rs | 50 ++++--- crates/tests/component/src/bindings.rs | 130 +++++++++++------- .../tests/implement_core/src/impl_on_outer.rs | 63 +++++++++ crates/tests/implement_core/src/lib.rs | 1 + 8 files changed, 358 insertions(+), 104 deletions(-) create mode 100644 crates/tests/implement_core/src/impl_on_outer.rs diff --git a/crates/libs/bindgen/src/rust/implements.rs b/crates/libs/bindgen/src/rust/implements.rs index cccf8503a2..5d6a894dd5 100644 --- a/crates/libs/bindgen/src/rust/implements.rs +++ b/crates/libs/bindgen/src/rust/implements.rs @@ -97,10 +97,15 @@ pub fn writer(writer: &Writer, def: metadata::TypeDef) -> TokenStream { if has_unknown_base { quote! { - unsafe extern "system" fn #name<#constraints Identity: windows_core::IUnknownImpl, Impl: #impl_ident<#generic_names>, const OFFSET: isize> #vtbl_signature { + unsafe extern "system" fn #name< + #constraints + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, + const OFFSET: isize + > #vtbl_signature where OuterToImpl::Impl: #impl_ident<#generic_names> { // offset the `this` pointer by `OFFSET` times the size of a pointer and cast it as an IUnknown implementation - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); #invoke_upcall } } @@ -123,7 +128,7 @@ pub fn writer(writer: &Writer, def: metadata::TypeDef) -> TokenStream { Some(metadata::Type::TypeDef(def, generics)) => { let name = writer.type_def_name_imp(*def, generics, "_Vtbl"); if has_unknown_base { - methods.combine("e! { base__: #name::new::(), }); + methods.combine("e! { base__: #name::new::(), }); } else { methods.combine("e! { base__: #name::new::(), }); } @@ -136,7 +141,8 @@ pub fn writer(writer: &Writer, def: metadata::TypeDef) -> TokenStream { for method in def.methods() { let name = method_names.add(method); if has_unknown_base { - methods.combine("e! { #name: #name::<#generic_names Identity, Impl, OFFSET>, }); + methods + .combine("e! { #name: #name::<#generic_names Identity, OuterToImpl, OFFSET>, }); } else { methods.combine("e! { #name: #name::, }); } @@ -151,7 +157,12 @@ pub fn writer(writer: &Writer, def: metadata::TypeDef) -> TokenStream { #runtime_name #features impl<#constraints> #vtbl_ident<#generic_names> { - pub const fn new, Impl: #impl_ident<#generic_names>, const OFFSET: isize>() -> #vtbl_ident<#generic_names> { + pub const fn new< + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, + const OFFSET: isize + >() -> #vtbl_ident<#generic_names> + where OuterToImpl::Impl : #impl_ident<#generic_names> { #(#method_impls)* Self{ #methods diff --git a/crates/libs/core/src/com_object.rs b/crates/libs/core/src/com_object.rs index ede313071e..aa50ba7f54 100644 --- a/crates/libs/core/src/com_object.rs +++ b/crates/libs/core/src/com_object.rs @@ -330,3 +330,57 @@ impl Borrow for ComObject { self.get() } } + +/// Allows a COM object implementation to implement COM interfaces either on the "outer" type or +/// the "inner" type. +/// +/// This trait is part of the implementation of `windows-rs` and is not meant to be used directly +/// by user code. This trait is not stable and may change at any time. +#[doc(hidden)] +pub trait ComGetImpl { + /// The type that implements the COM interface. + type Impl; + + /// At runtime, casts from the outer object type to the implementation type. + fn get_impl(outer: &Outer) -> &Self::Impl; +} + +/// Selects the "inner" type of a COM object implementation. This implementation uses the +/// `IUnknownImpl` trait both to specify the type that implements the COM interface and to +/// cast from `&Outer` to `&Inner` (i.e. from `&MyApp_Impl` to `&MyApp`). +/// +/// This struct is part of the implementation of `windows-rs` and is not meant to be used directly +/// by user code. This trait is not stable and may change at any time. +#[doc(hidden)] +pub struct ComGetImplInner { + _marker: core::marker::PhantomData, +} + +impl ComGetImpl for ComGetImplInner +where + Outer: IUnknownImpl, +{ + type Impl = ::Impl; + + fn get_impl(outer: &Outer) -> &Self::Impl { + ::get_impl(outer) + } +} + +/// Selects the "outer" type of a COM object implementation. This is basically an identify function, +/// over types. +/// +/// This struct is part of the implementation of `windows-rs` and is not meant to be used directly +/// by user code. This trait is not stable and may change at any time. +#[doc(hidden)] +pub struct ComGetImplOuter { + _marker: core::marker::PhantomData, +} + +impl ComGetImpl for ComGetImplOuter { + type Impl = Outer; + + fn get_impl(outer: &Outer) -> &Self::Impl { + outer + } +} diff --git a/crates/libs/implement/src/lib.rs b/crates/libs/implement/src/lib.rs index 4fa01941a4..4e0f25d088 100644 --- a/crates/libs/implement/src/lib.rs +++ b/crates/libs/implement/src/lib.rs @@ -77,8 +77,16 @@ pub fn implement( .enumerate() .map(|(enumerate, implement)| { let vtbl_ident = implement.to_vtbl_ident(); + let outer_to_impl = match implement.impl_location { + ImplLocation::Outer => { + quote!(::windows_core::ComGetImplOuter<#impl_ident #generics>) + } + ImplLocation::Inner => { + quote!(::windows_core::ComGetImplInner<#impl_ident #generics>) + } + }; let offset = proc_macro2::Literal::isize_unsuffixed(-1 - enumerate as isize); - quote! { #vtbl_ident::new::() } + quote! { #vtbl_ident::new::() } }); let offset = attributes @@ -389,6 +397,18 @@ pub fn implement( struct ImplementType { type_name: String, generics: Vec, + impl_location: ImplLocation, +} + +/// Specifies whether a COM object implements COM interfaces on its "inner" or "outer" object. +/// +/// The default, for backward compatibility, is inner. In the long-term, arguably all COM objects +/// should switch to defining interfaces on the outer object. +#[derive(Copy, Clone, Eq, PartialEq, Default)] +enum ImplLocation { + #[default] + Inner, + Outer, } impl ImplementType { @@ -450,8 +470,9 @@ impl ImplementAttributes { namespace.push_str(&input.ident.to_string()); self.walk_implement(&input.tree, namespace)?; } - UseTree2::Name(_) => { - self.implement.push(tree.to_element_type(namespace)?); + UseTree2::Name(name) => { + self.implement + .push(tree.to_element_type(name.impl_location, namespace)?); } UseTree2::Group(input) => { for tree in &input.items { @@ -473,7 +494,11 @@ enum UseTree2 { } impl UseTree2 { - fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result { + fn to_element_type( + &self, + impl_location: ImplLocation, + namespace: &mut String, + ) -> syn::parse::Result { match self { UseTree2::Path(input) => { if !namespace.is_empty() { @@ -481,7 +506,7 @@ impl UseTree2 { } namespace.push_str(&input.ident.to_string()); - input.tree.to_element_type(namespace) + input.tree.to_element_type(impl_location, namespace) } UseTree2::Name(input) => { let mut type_name = input.ident.to_string(); @@ -493,12 +518,13 @@ impl UseTree2 { let mut generics = vec![]; for g in &input.generics { - generics.push(g.to_element_type(&mut String::new())?); + generics.push(g.to_element_type(impl_location, &mut String::new())?); } Ok(ImplementType { type_name, generics, + impl_location, }) } UseTree2::Group(input) => Err(syn::parse::Error::new( @@ -518,6 +544,7 @@ struct UsePath2 { struct UseName2 { pub ident: syn::Ident, pub generics: Vec, + pub impl_location: ImplLocation, } struct UseGroup2 { @@ -572,7 +599,27 @@ impl syn::parse::Parse for UseTree2 { Vec::new() }; - Ok(UseTree2::Name(UseName2 { ident, generics })) + // Check for a suffix of `@Outer`, which specifies that an interface chain is + // implemented on the outer (MyApp_Impl) type, not the inner type. + let mut impl_location = ImplLocation::Inner; + if input.peek(syn::Token![@]) { + input.parse::()?; + let ident = input.parse::()?; + if ident == "Outer" { + impl_location = ImplLocation::Outer; + } else { + return Err(syn::Error::new( + ident.span(), + "the only supported suffix is @ Outer", + )); + } + } + + Ok(UseTree2::Name(UseName2 { + ident, + generics, + impl_location, + })) } } else if lookahead.peek(syn::token::Brace) { let content; diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index e33379a921..680664359f 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -135,8 +135,8 @@ impl Interface { if m.is_result() { quote! { - #[inline(always)] - #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret { + #[inline(always)] + #vis unsafe fn #name<#(#generics),*>(&self, #(#params),*) #ret { (::windows_core::Interface::vtable(self).#name)(::windows_core::Interface::as_raw(self), #(#args),*).ok() } } @@ -214,7 +214,7 @@ impl Interface { let parent_vtable_generics = if self.parent_is_iunknown() { quote!(Identity, OFFSET) } else { - quote!(Identity, Impl, OFFSET) + quote!(Identity, OuterToImpl, OFFSET) }; let parent_vtable = self.parent_vtable(); @@ -253,13 +253,35 @@ impl Interface { if parent_vtable.is_some() { quote! { - unsafe extern "system" fn #name, Impl: #trait_name, const OFFSET: isize>(this: *mut ::core::ffi::c_void, #(#args),*) #ret { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this_impl: &Impl = (*this).get_impl(); + unsafe extern "system" fn #name< + Identity: ::windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, + const OFFSET: isize + >( + this: *mut ::core::ffi::c_void, // <-- This is the COM "this" pointer, which is not the same as &T or &T_Impl. + #(#args),* + ) #ret + where + OuterToImpl::Impl : #trait_name + { + // This step is essentially a virtual dispatch adjustor thunk. Its purpose is to adjust + // the "this" pointer from the address used by the COM interface to the root of the + // MyApp_Impl object. Since a given MyApp_Impl may implement more than one COM interface + // (and more than one COM interface chain), we need to know how to get from COM's "this" + // back to &MyApp_Impl. The OFFSET constant gives us the value (in pointer-sized units). + let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + + // This step selects the part of the MyApp_Impl object which implements a given COM interface, + // i.e. IFoo_Impl trait. There are really only two possibilities: either MyApp_Impl or MyApp + // implements a given IFoo_Impl trait. The ComGetImplInner and ComGetImplOuter types + // allow the code that specialized this function to select which one is used. + let this_impl: &OuterToImpl::Impl = OuterToImpl::get_impl(this_outer); + + // Last, we invoke the implementation function. // We use explicit so that we can select the correct method // for situations where IFoo3 derives from IFoo2 and both declare a method with // the same name. - ::#name(this_impl, #(#params),*).into() + ::#name(this_impl, #(#params),*).into() } } } else { @@ -274,24 +296,16 @@ impl Interface { }) .collect::>(); - let entries = self - .methods - .iter() - .map(|m| { - let name = &m.name; - if parent_vtable.is_some() { - quote! { - #name: #name:: - } - } else { - quote! { - #name: #name:: - } - } - }) - .collect::>(); - if let Some(parent_vtable) = parent_vtable { + let entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + quote!(#name: #name::) + }) + .collect::>(); + quote! { #[repr(C)] #[doc(hidden)] @@ -300,7 +314,14 @@ impl Interface { #(#vtable_entries)* } impl #vtable_name { - pub const fn new, Impl: #trait_name, const OFFSET: isize>() -> Self { + pub const fn new< + Identity: ::windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, + const OFFSET: isize, + >() -> Self + where + OuterToImpl::Impl : #trait_name + { #(#functions)* Self { base__: #parent_vtable::new::<#parent_vtable_generics>(), #(#entries),* } } @@ -313,6 +334,15 @@ impl Interface { } } } else { + let entries = self + .methods + .iter() + .map(|m| { + let name = &m.name; + quote!(#name: #name::) + }) + .collect::>(); + quote! { #[repr(C)] #[doc(hidden)] diff --git a/crates/samples/components/json_validator_winrt/src/bindings.rs b/crates/samples/components/json_validator_winrt/src/bindings.rs index 02f20d94d9..54b4ff7097 100644 --- a/crates/samples/components/json_validator_winrt/src/bindings.rs +++ b/crates/samples/components/json_validator_winrt/src/bindings.rs @@ -114,21 +114,28 @@ impl windows_core::RuntimeName for IJsonValidator { } impl IJsonValidator_Vtbl { pub const fn new< - Identity: windows_core::IUnknownImpl, - Impl: IJsonValidator_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, - >() -> IJsonValidator_Vtbl { + >() -> IJsonValidator_Vtbl + where + OuterToImpl::Impl: IJsonValidator_Impl, + { unsafe extern "system" fn Validate< - Identity: windows_core::IUnknownImpl, - Impl: IJsonValidator_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, value: core::mem::MaybeUninit, result__: *mut core::mem::MaybeUninit, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IJsonValidator_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); match IJsonValidator_Impl::Validate(this, core::mem::transmute(&value)) { Ok(ok__) => { core::ptr::write(result__, core::mem::transmute_copy(&ok__)); @@ -140,7 +147,7 @@ impl IJsonValidator_Vtbl { } Self { base__: windows_core::IInspectable_Vtbl::new::(), - Validate: Validate::, + Validate: Validate::, } } pub fn matches(iid: &windows_core::GUID) -> bool { @@ -156,21 +163,28 @@ impl windows_core::RuntimeName for IJsonValidatorFactory { } impl IJsonValidatorFactory_Vtbl { pub const fn new< - Identity: windows_core::IUnknownImpl, - Impl: IJsonValidatorFactory_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, - >() -> IJsonValidatorFactory_Vtbl { + >() -> IJsonValidatorFactory_Vtbl + where + OuterToImpl::Impl: IJsonValidatorFactory_Impl, + { unsafe extern "system" fn CreateInstance< - Identity: windows_core::IUnknownImpl, - Impl: IJsonValidatorFactory_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, schema: core::mem::MaybeUninit, result__: *mut *mut core::ffi::c_void, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IJsonValidatorFactory_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); match IJsonValidatorFactory_Impl::CreateInstance(this, core::mem::transmute(&schema)) { Ok(ok__) => { core::ptr::write(result__, core::mem::transmute_copy(&ok__)); @@ -183,7 +197,7 @@ impl IJsonValidatorFactory_Vtbl { Self { base__: windows_core::IInspectable_Vtbl::new::( ), - CreateInstance: CreateInstance::, + CreateInstance: CreateInstance::, } } pub fn matches(iid: &windows_core::GUID) -> bool { diff --git a/crates/tests/component/src/bindings.rs b/crates/tests/component/src/bindings.rs index 187a8710aa..1135c49674 100644 --- a/crates/tests/component/src/bindings.rs +++ b/crates/tests/component/src/bindings.rs @@ -50,24 +50,31 @@ pub mod Nested { } impl IThing_Vtbl { pub const fn new< - Identity: windows_core::IUnknownImpl, - Impl: IThing_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, - >() -> IThing_Vtbl { + >() -> IThing_Vtbl + where + OuterToImpl::Impl: IThing_Impl, + { unsafe extern "system" fn Method< - Identity: windows_core::IUnknownImpl, - Impl: IThing_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IThing_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); IThing_Impl::Method(this).into() } Self { base__: windows_core::IInspectable_Vtbl::new::(), - Method: Method::, + Method: Method::, } } pub fn matches(iid: &windows_core::GUID) -> bool { @@ -433,20 +440,27 @@ impl windows_core::RuntimeName for IClass { } impl IClass_Vtbl { pub const fn new< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, - >() -> IClass_Vtbl { + >() -> IClass_Vtbl + where + OuterToImpl::Impl: IClass_Impl, + { unsafe extern "system" fn Property< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, 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(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IClass_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); match IClass_Impl::Property(this) { Ok(ok__) => { core::ptr::write(result__, core::mem::transmute_copy(&ok__)); @@ -456,27 +470,35 @@ impl IClass_Vtbl { } } unsafe extern "system" fn SetProperty< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, value: i32, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IClass_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); IClass_Impl::SetProperty(this, value).into() } unsafe extern "system" fn Flags< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, result__: *mut Flags, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IClass_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); match IClass_Impl::Flags(this) { Ok(ok__) => { core::ptr::write(result__, core::mem::transmute_copy(&ok__)); @@ -486,8 +508,8 @@ impl IClass_Vtbl { } } unsafe extern "system" fn Int32Array< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, @@ -499,9 +521,13 @@ impl IClass_Vtbl { c: *mut *mut i32, result_size__: *mut u32, result__: *mut *mut i32, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IClass_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); match IClass_Impl::Int32Array( this, core::slice::from_raw_parts(core::mem::transmute_copy(&a), a_array_size as usize), @@ -525,8 +551,8 @@ impl IClass_Vtbl { } } unsafe extern "system" fn StringArray< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, @@ -538,9 +564,13 @@ impl IClass_Vtbl { c: *mut *mut core::mem::MaybeUninit, result_size__: *mut u32, result__: *mut *mut core::mem::MaybeUninit, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IClass_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); match IClass_Impl::StringArray( this, core::slice::from_raw_parts(core::mem::transmute_copy(&a), a_array_size as usize), @@ -564,8 +594,8 @@ impl IClass_Vtbl { } } unsafe extern "system" fn Input< - Identity: windows_core::IUnknownImpl, - Impl: IClass_Impl, + Identity: windows_core::IUnknownImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >( this: *mut core::ffi::c_void, @@ -573,9 +603,13 @@ impl IClass_Vtbl { b: *mut core::ffi::c_void, c: *mut core::ffi::c_void, d: *mut core::ffi::c_void, - ) -> windows_core::HRESULT { - let this = (this as *const *const ()).offset(OFFSET) as *const Identity; - let this = (*this).get_impl(); + ) -> windows_core::HRESULT + where + OuterToImpl::Impl: IClass_Impl, + { + let this_outer: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this = OuterToImpl::get_impl(this_outer); IClass_Impl::Input( this, windows_core::from_raw_borrowed(&a), @@ -587,12 +621,12 @@ impl IClass_Vtbl { } Self { base__: windows_core::IInspectable_Vtbl::new::(), - Property: Property::, - SetProperty: SetProperty::, - Flags: Flags::, - Int32Array: Int32Array::, - StringArray: StringArray::, - Input: Input::, + Property: Property::, + SetProperty: SetProperty::, + Flags: Flags::, + Int32Array: Int32Array::, + StringArray: StringArray::, + Input: Input::, } } pub fn matches(iid: &windows_core::GUID) -> bool { diff --git a/crates/tests/implement_core/src/impl_on_outer.rs b/crates/tests/implement_core/src/impl_on_outer.rs new file mode 100644 index 0000000000..203515999e --- /dev/null +++ b/crates/tests/implement_core/src/impl_on_outer.rs @@ -0,0 +1,63 @@ +use windows_core::*; + +#[interface("cccccccc-0000-0000-0000-000000000001")] +unsafe trait IFoo: IUnknown { + fn hello(&self); +} + +#[interface("cccccccc-0000-0000-0000-000000000002")] +unsafe trait IFoo2: IFoo { + fn hello(&self); +} + +#[interface("cccccccc-0000-0000-0000-000000000003")] +unsafe trait IFoo3: IFoo2 { + fn hello(&self); +} + +#[interface("cccccccc-0000-0000-0000-000000000004")] +unsafe trait IBar: IUnknown { + fn goodbye(&self); +} + +// This tests that we can compile a COM object that has some COM interfaces implemented on the +// outer object and some on the inner object. +#[implement(IFoo3 @ Outer, IBar)] +struct MyApp {} + +impl IFoo_Impl for MyApp_Impl { + unsafe fn hello(&self) { + println!("MyApp as IFoo: hello"); + } +} +impl IFoo2_Impl for MyApp_Impl { + unsafe fn hello(&self) { + println!("MyApp as IFoo2: hello"); + } +} +impl IFoo3_Impl for MyApp_Impl { + unsafe fn hello(&self) { + println!("MyApp as IFoo3: hello"); + } +} + +impl IBar_Impl for MyApp { + unsafe fn goodbye(&self) { + println!("MyApp as IBar: goodbye"); + } +} + +#[test] +fn basic() { + let app = ComObject::new(MyApp {}); + let ifoo3: IFoo3 = app.cast().unwrap(); + let ifoo2: IFoo2 = app.cast().unwrap(); + let ifoo: IFoo = app.cast().unwrap(); + let ibar: IBar = app.cast().unwrap(); + unsafe { + ifoo.hello(); + ifoo2.hello(); + ifoo3.hello(); + ibar.goodbye(); + } +} diff --git a/crates/tests/implement_core/src/lib.rs b/crates/tests/implement_core/src/lib.rs index aa8f3bec53..39557c2b61 100644 --- a/crates/tests/implement_core/src/lib.rs +++ b/crates/tests/implement_core/src/lib.rs @@ -5,3 +5,4 @@ mod com_chain; mod com_object; +mod impl_on_outer; From 76d42276ad9c0b28896a646bebc4322572379996 Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Wed, 5 Jun 2024 15:12:13 -0700 Subject: [PATCH 2/2] Interface impls go on ComObject --- crates/libs/core/src/com_object.rs | 31 +++++++++---------- crates/libs/core/src/unknown.rs | 2 +- crates/libs/implement/src/lib.rs | 4 +-- crates/libs/interface/src/lib.rs | 11 +++++-- .../tests/implement_core/src/impl_on_outer.rs | 17 ++++++++-- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/crates/libs/core/src/com_object.rs b/crates/libs/core/src/com_object.rs index aa50ba7f54..0d84b6dcd7 100644 --- a/crates/libs/core/src/com_object.rs +++ b/crates/libs/core/src/com_object.rs @@ -337,12 +337,12 @@ impl Borrow for ComObject { /// This trait is part of the implementation of `windows-rs` and is not meant to be used directly /// by user code. This trait is not stable and may change at any time. #[doc(hidden)] -pub trait ComGetImpl { +pub trait ComGetImpl { /// The type that implements the COM interface. type Impl; /// At runtime, casts from the outer object type to the implementation type. - fn get_impl(outer: &Outer) -> &Self::Impl; + fn get_impl(object: &ComObject) -> &Self::Impl; } /// Selects the "inner" type of a COM object implementation. This implementation uses the @@ -352,18 +352,15 @@ pub trait ComGetImpl { /// This struct is part of the implementation of `windows-rs` and is not meant to be used directly /// by user code. This trait is not stable and may change at any time. #[doc(hidden)] -pub struct ComGetImplInner { - _marker: core::marker::PhantomData, +pub struct ComGetImplInner { + _marker: core::marker::PhantomData, } -impl ComGetImpl for ComGetImplInner -where - Outer: IUnknownImpl, -{ - type Impl = ::Impl; +impl ComGetImpl for ComGetImplInner { + type Impl = T; - fn get_impl(outer: &Outer) -> &Self::Impl { - ::get_impl(outer) + fn get_impl(object: &ComObject) -> &Self::Impl { + object.get_impl() } } @@ -373,14 +370,14 @@ where /// This struct is part of the implementation of `windows-rs` and is not meant to be used directly /// by user code. This trait is not stable and may change at any time. #[doc(hidden)] -pub struct ComGetImplOuter { - _marker: core::marker::PhantomData, +pub struct ComGetImplOuter { + _marker: core::marker::PhantomData, } -impl ComGetImpl for ComGetImplOuter { - type Impl = Outer; +impl ComGetImpl for ComGetImplOuter { + type Impl = ComObject; - fn get_impl(outer: &Outer) -> &Self::Impl { - outer + fn get_impl(object: &ComObject) -> &Self::Impl { + object } } diff --git a/crates/libs/core/src/unknown.rs b/crates/libs/core/src/unknown.rs index 1f16b35a0d..7574f2f11e 100644 --- a/crates/libs/core/src/unknown.rs +++ b/crates/libs/core/src/unknown.rs @@ -70,7 +70,7 @@ impl core::fmt::Debug for IUnknown { #[doc(hidden)] pub trait IUnknownImpl { /// The contained user type, e.g. `MyApp`. Also known as the "inner" type. - type Impl; + type Impl: ComObjectInner; /// Get a reference to the backing implementation. fn get_impl(&self) -> &Self::Impl; diff --git a/crates/libs/implement/src/lib.rs b/crates/libs/implement/src/lib.rs index 4e0f25d088..4d97883993 100644 --- a/crates/libs/implement/src/lib.rs +++ b/crates/libs/implement/src/lib.rs @@ -79,10 +79,10 @@ pub fn implement( let vtbl_ident = implement.to_vtbl_ident(); let outer_to_impl = match implement.impl_location { ImplLocation::Outer => { - quote!(::windows_core::ComGetImplOuter<#impl_ident #generics>) + quote!(::windows_core::ComGetImplOuter<#original_ident #generics>) } ImplLocation::Inner => { - quote!(::windows_core::ComGetImplInner<#impl_ident #generics>) + quote!(::windows_core::ComGetImplInner<#original_ident #generics>) } }; let offset = proc_macro2::Literal::isize_unsuffixed(-1 - enumerate as isize); diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index 680664359f..5f7a351c38 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -255,7 +255,7 @@ impl Interface { quote! { unsafe extern "system" fn #name< Identity: ::windows_core::IUnknownImpl, - OuterToImpl: ::windows_core::ComGetImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize >( this: *mut ::core::ffi::c_void, // <-- This is the COM "this" pointer, which is not the same as &T or &T_Impl. @@ -271,11 +271,16 @@ impl Interface { // back to &MyApp_Impl. The OFFSET constant gives us the value (in pointer-sized units). let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + let this_com_object: ::core::mem::ManuallyDrop<::windows_core::ComObject> + = core::mem::transmute(this_outer); + + let this_com_object_ref: &::windows_core::ComObject = &this_com_object; + // This step selects the part of the MyApp_Impl object which implements a given COM interface, // i.e. IFoo_Impl trait. There are really only two possibilities: either MyApp_Impl or MyApp // implements a given IFoo_Impl trait. The ComGetImplInner and ComGetImplOuter types // allow the code that specialized this function to select which one is used. - let this_impl: &OuterToImpl::Impl = OuterToImpl::get_impl(this_outer); + let this_impl: &OuterToImpl::Impl = OuterToImpl::get_impl(this_com_object_ref); // Last, we invoke the implementation function. // We use explicit so that we can select the correct method @@ -316,7 +321,7 @@ impl Interface { impl #vtable_name { pub const fn new< Identity: ::windows_core::IUnknownImpl, - OuterToImpl: ::windows_core::ComGetImpl, + OuterToImpl: ::windows_core::ComGetImpl, const OFFSET: isize, >() -> Self where diff --git a/crates/tests/implement_core/src/impl_on_outer.rs b/crates/tests/implement_core/src/impl_on_outer.rs index 203515999e..8df9934be0 100644 --- a/crates/tests/implement_core/src/impl_on_outer.rs +++ b/crates/tests/implement_core/src/impl_on_outer.rs @@ -3,6 +3,7 @@ use windows_core::*; #[interface("cccccccc-0000-0000-0000-000000000001")] unsafe trait IFoo: IUnknown { fn hello(&self); + fn self_as_bar(&self) -> IBar; } #[interface("cccccccc-0000-0000-0000-000000000002")] @@ -25,17 +26,22 @@ unsafe trait IBar: IUnknown { #[implement(IFoo3 @ Outer, IBar)] struct MyApp {} -impl IFoo_Impl for MyApp_Impl { +impl IFoo_Impl for ComObject { unsafe fn hello(&self) { println!("MyApp as IFoo: hello"); } + + unsafe fn self_as_bar(&self) -> IBar { + println!("MyApp as IFoo::self_as_bar"); + self.to_interface() + } } -impl IFoo2_Impl for MyApp_Impl { +impl IFoo2_Impl for ComObject { unsafe fn hello(&self) { println!("MyApp as IFoo2: hello"); } } -impl IFoo3_Impl for MyApp_Impl { +impl IFoo3_Impl for ComObject { unsafe fn hello(&self) { println!("MyApp as IFoo3: hello"); } @@ -60,4 +66,9 @@ fn basic() { ifoo3.hello(); ibar.goodbye(); } + + unsafe { + let ibar = ifoo3.self_as_bar(); + ibar.goodbye(); + } }