diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 87c6f849a0..db83ae29d7 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -155,6 +155,10 @@ jobs: run: cargo clippy -p test_const_params - name: Clippy test_const_ptrs run: cargo clippy -p test_const_ptrs + - name: Clippy test_constructors + run: cargo clippy -p test_constructors + - name: Clippy test_constructors_client + run: cargo clippy -p test_constructors_client - name: Clippy test_core run: cargo clippy -p test_core - name: Clippy test_debug diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f020e46af8..0589234aee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -181,6 +181,10 @@ jobs: run: cargo test -p test_const_params --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_const_ptrs run: cargo test -p test_const_ptrs --target ${{ matrix.target }} ${{ matrix.etc }} + - name: Test test_constructors + run: cargo test -p test_constructors --target ${{ matrix.target }} ${{ matrix.etc }} + - name: Test test_constructors_client + run: cargo test -p test_constructors_client --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_core run: cargo test -p test_core --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_debug @@ -251,12 +255,12 @@ jobs: run: cargo test -p test_resources --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_result run: cargo test -p test_result --target ${{ matrix.target }} ${{ matrix.etc }} + - name: Clean + run: cargo clean - name: Test test_return_handle run: cargo test -p test_return_handle --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_return_struct run: cargo test -p test_return_struct --target ${{ matrix.target }} ${{ matrix.etc }} - - name: Clean - run: cargo clean - name: Test test_riddle run: cargo test -p test_riddle --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_standalone diff --git a/crates/libs/bindgen/src/metadata.rs b/crates/libs/bindgen/src/metadata.rs index bddacc45ec..544eebc45e 100644 --- a/crates/libs/bindgen/src/metadata.rs +++ b/crates/libs/bindgen/src/metadata.rs @@ -13,6 +13,7 @@ pub enum InterfaceKind { Default, Overridable, Static, + Composable, Base, } @@ -594,24 +595,25 @@ pub fn type_interfaces(ty: &Type) -> Vec { walk(&mut result, &Type::TypeDef(base, Vec::new()), true); } for attribute in row.attributes() { - match attribute.name() { - "StaticAttribute" | "ActivatableAttribute" => { - for (_, arg) in attribute.args() { - if let Value::TypeName(type_name) = arg { - let def = row - .reader() - .get_type_def(type_name.namespace(), type_name.name()) - .next() - .expect("Type not found"); - result.push(Interface { - ty: Type::TypeDef(def, Vec::new()), - kind: InterfaceKind::Static, - }); - break; - } - } + let kind = match attribute.name() { + "StaticAttribute" | "ActivatableAttribute" => InterfaceKind::Static, + "ComposableAttribute" => InterfaceKind::Composable, + _ => continue, + }; + + for (_, arg) in attribute.args() { + if let Value::TypeName(type_name) = arg { + let def = row + .reader() + .get_type_def(type_name.namespace(), type_name.name()) + .next() + .expect("Type not found"); + result.push(Interface { + ty: Type::TypeDef(def, Vec::new()), + kind, + }); + break; } - _ => {} } } } diff --git a/crates/libs/bindgen/src/rust/classes.rs b/crates/libs/bindgen/src/rust/classes.rs index 9d170bdd03..a7165f8f63 100644 --- a/crates/libs/bindgen/src/rust/classes.rs +++ b/crates/libs/bindgen/src/rust/classes.rs @@ -67,7 +67,7 @@ fn gen_class(writer: &Writer, def: metadata::TypeDef) -> TokenStream { } let factories = interfaces.iter().filter_map(|interface| match interface.kind { - metadata::InterfaceKind::Static => { + metadata::InterfaceKind::Static | metadata::InterfaceKind::Composable => { if let metadata::Type::TypeDef(def, generics) = &interface.ty { if def.methods().next().is_some() { let interface_type = writer.type_name(&interface.ty); diff --git a/crates/libs/bindgen/src/rust/winrt_methods.rs b/crates/libs/bindgen/src/rust/winrt_methods.rs index eafe2bc569..2dd4c433c2 100644 --- a/crates/libs/bindgen/src/rust/winrt_methods.rs +++ b/crates/libs/bindgen/src/rust/winrt_methods.rs @@ -11,8 +11,18 @@ pub fn writer( virtual_names: &mut MethodNames, ) -> TokenStream { let signature = metadata::method_def_signature(def.namespace(), method, generic_types); - let params = &signature.params; - let name = method_names.add(method); + let params = if kind == metadata::InterfaceKind::Composable { + &signature.params[..signature.params.len() - 2] + } else { + &signature.params + }; + + let name = if kind == metadata::InterfaceKind::Composable && params.is_empty() { + quote!(new) + } else { + method_names.add(method) + }; + let interface_name = writer.type_def_name(def, generic_types); let vname = virtual_names.add(method); let generics = writer.constraint_generics(params); @@ -20,7 +30,14 @@ pub fn writer( let mut cfg = cfg::signature_cfg(writer, method); cfg::type_def_cfg_combine(writer, def, generic_types, &mut cfg); let features = writer.cfg_features(&cfg); - let args = gen_winrt_abi_args(writer, params); + + let args = if kind == metadata::InterfaceKind::Composable { + let args = gen_winrt_abi_args(writer, params); + quote! { #args core::ptr::null_mut(), &mut core::ptr::null_mut(), } + } else { + gen_winrt_abi_args(writer, params) + }; + let params = gen_winrt_params(writer, params); let noexcept = metadata::method_def_is_noexcept(method); @@ -141,7 +158,7 @@ pub fn writer( } } } - metadata::InterfaceKind::Static => { + metadata::InterfaceKind::Static | metadata::InterfaceKind::Composable => { quote! { #features pub fn #name<#generics>(#params) #return_type_tokens #where_clause { diff --git a/crates/tests/constructors/Cargo.toml b/crates/tests/constructors/Cargo.toml new file mode 100644 index 0000000000..b61e9e1bd4 --- /dev/null +++ b/crates/tests/constructors/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "test_constructors" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doc = false +doctest = false + +[build-dependencies.windows-bindgen] +path = "../../libs/bindgen" + +[dependencies.windows-core] +path = "../../libs/core" + +[dependencies.windows] +path = "../../libs/windows" +features = [ + "implement", + "Foundation", + "Win32_System_WinRT", +] diff --git a/crates/tests/constructors/build.rs b/crates/tests/constructors/build.rs new file mode 100644 index 0000000000..7f24102c10 --- /dev/null +++ b/crates/tests/constructors/build.rs @@ -0,0 +1,33 @@ +fn main() { + let mut command = std::process::Command::new("midlrt.exe"); + command.args([ + "/winrt", + "/nomidl", + "/h", + "nul", + "/metadata_dir", + "../../libs/bindgen/default", + "/reference", + "../../libs/bindgen/default/Windows.winmd", + "/winmd", + "metadata.winmd", + "src/metadata.idl", + ]); + + if !command.status().unwrap().success() { + panic!("Failed to run midlrt"); + } + + windows_bindgen::bindgen([ + "--in", + "metadata.winmd", + "--out", + "src/bindings.rs", + "--filter", + "test_constructors", + "--config", + "implement", + "no-bindgen-comment", + ]) + .unwrap(); +} diff --git a/crates/tests/constructors/src/bindings.rs b/crates/tests/constructors/src/bindings.rs new file mode 100644 index 0000000000..6c6090c9f3 --- /dev/null +++ b/crates/tests/constructors/src/bindings.rs @@ -0,0 +1,389 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +windows_core::imp::define_interface!( + IActivatable, + IActivatable_Vtbl, + 0xe566522b_9c26_582b_950d_177b05d36efd +); +impl windows_core::RuntimeType for IActivatable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IActivatable_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Property: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IActivatableFactory, + IActivatableFactory_Vtbl, + 0xafc5aee9_aa78_5da6_85a2_69e67b45c620 +); +impl windows_core::RuntimeType for IActivatableFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IActivatableFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub WithValue: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IComposable, + IComposable_Vtbl, + 0xff2595d6_461d_5118_9296_f2a2b1e64544 +); +impl windows_core::RuntimeType for IComposable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IComposable_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Property: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IComposableFactory, + IComposableFactory_Vtbl, + 0x6a461099_83c0_5810_9e20_2e8b9521d143 +); +impl windows_core::RuntimeType for IComposableFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IComposableFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CreateInstance: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub WithValue: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Activatable(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Activatable, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Activatable { + pub fn new() -> windows_core::Result { + Self::IActivationFactory(|f| f.ActivateInstance::()) + } + fn IActivationFactory< + R, + F: FnOnce(&windows_core::imp::IGenericFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + Activatable, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + pub fn Property(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Property)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn WithValue(arg: i32) -> windows_core::Result { + Self::IActivatableFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).WithValue)( + windows_core::Interface::as_raw(this), + arg, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + #[doc(hidden)] + pub fn IActivatableFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for Activatable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Activatable { + type Vtable = IActivatable_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Activatable { + const NAME: &'static str = "test_constructors.Activatable"; +} +unsafe impl Send for Activatable {} +unsafe impl Sync for Activatable {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Composable(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Composable, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Composable { + pub fn Property(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Property)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn new() -> windows_core::Result { + Self::IComposableFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn WithValue(arg: i32) -> windows_core::Result { + Self::IComposableFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).WithValue)( + windows_core::Interface::as_raw(this), + arg, + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + #[doc(hidden)] + pub fn IComposableFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for Composable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Composable { + type Vtable = IComposable_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Composable { + const NAME: &'static str = "test_constructors.Composable"; +} +unsafe impl Send for Composable {} +unsafe impl Sync for Composable {} +pub trait IActivatable_Impl: Sized + windows_core::IUnknownImpl { + fn Property(&self) -> windows_core::Result; +} +impl windows_core::RuntimeName for IActivatable { + const NAME: &'static str = "test_constructors.IActivatable"; +} +impl IActivatable_Vtbl { + pub const fn new() -> IActivatable_Vtbl { + unsafe extern "system" fn Property( + this: *mut core::ffi::c_void, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IActivatable_Impl::Property(this) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Property: Property::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IActivatableFactory_Impl: Sized + windows_core::IUnknownImpl { + fn WithValue(&self, arg: i32) -> windows_core::Result; +} +impl windows_core::RuntimeName for IActivatableFactory { + const NAME: &'static str = "test_constructors.IActivatableFactory"; +} +impl IActivatableFactory_Vtbl { + pub const fn new( + ) -> IActivatableFactory_Vtbl { + unsafe extern "system" fn WithValue< + Identity: IActivatableFactory_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + arg: i32, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IActivatableFactory_Impl::WithValue(this, arg) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + WithValue: WithValue::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IComposable_Impl: Sized + windows_core::IUnknownImpl { + fn Property(&self) -> windows_core::Result; +} +impl windows_core::RuntimeName for IComposable { + const NAME: &'static str = "test_constructors.IComposable"; +} +impl IComposable_Vtbl { + pub const fn new() -> IComposable_Vtbl { + unsafe extern "system" fn Property( + this: *mut core::ffi::c_void, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IComposable_Impl::Property(this) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Property: Property::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IComposableFactory_Impl: Sized + windows_core::IUnknownImpl { + fn CreateInstance( + &self, + baseinterface: Option<&windows_core::IInspectable>, + innerinterface: &mut Option, + ) -> windows_core::Result; + fn WithValue( + &self, + arg: i32, + baseinterface: Option<&windows_core::IInspectable>, + innerinterface: &mut Option, + ) -> windows_core::Result; +} +impl windows_core::RuntimeName for IComposableFactory { + const NAME: &'static str = "test_constructors.IComposableFactory"; +} +impl IComposableFactory_Vtbl { + pub const fn new( + ) -> IComposableFactory_Vtbl { + unsafe extern "system" fn CreateInstance< + Identity: IComposableFactory_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + baseinterface: *mut core::ffi::c_void, + innerinterface: *mut *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IComposableFactory_Impl::CreateInstance( + this, + windows_core::from_raw_borrowed(&baseinterface), + core::mem::transmute_copy(&innerinterface), + ) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn WithValue< + Identity: IComposableFactory_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + arg: i32, + baseinterface: *mut core::ffi::c_void, + innerinterface: *mut *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IComposableFactory_Impl::WithValue( + this, + arg, + windows_core::from_raw_borrowed(&baseinterface), + core::mem::transmute_copy(&innerinterface), + ) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + CreateInstance: CreateInstance::, + WithValue: WithValue::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} diff --git a/crates/tests/constructors/src/lib.rs b/crates/tests/constructors/src/lib.rs new file mode 100644 index 0000000000..49daa30a76 --- /dev/null +++ b/crates/tests/constructors/src/lib.rs @@ -0,0 +1,104 @@ +mod bindings; + +use windows::{core::*, Win32::Foundation::*, Win32::System::WinRT::*}; + +#[no_mangle] +unsafe extern "system" fn DllGetActivationFactory( + name: Ref, + factory: OutRef, +) -> HRESULT { + if *name == "test_constructors.Activatable" { + factory.write(Some(ActivatableFactory.into())).into() + } else if *name == "test_constructors.Composable" { + factory.write(Some(ComposableFactory.into())).into() + } else { + _ = factory.write(None); + CLASS_E_CLASSNOTAVAILABLE + } +} + +#[implement(IActivationFactory, bindings::IActivatableFactory)] +struct ActivatableFactory; + +impl IActivationFactory_Impl for ActivatableFactory_Impl { + // Activatable types implement their default constructors using `IActivationFactory::ActivateInstance`. + fn ActivateInstance(&self) -> Result { + Ok(Activatable::new(0).into()) + } +} + +impl bindings::IActivatableFactory_Impl for ActivatableFactory_Impl { + fn WithValue(&self, arg: i32) -> Result { + Ok(Activatable::new(arg).into()) + } +} + +#[implement(bindings::Activatable)] +struct Activatable(i32); + +impl bindings::IActivatable_Impl for Activatable_Impl { + fn Property(&self) -> Result { + Ok(self.0) + } +} + +impl Activatable { + fn new(arg: i32) -> Self { + Self(arg) + } +} + +#[implement(IActivationFactory, bindings::IComposableFactory)] +struct ComposableFactory; + +impl IActivationFactory_Impl for ComposableFactory_Impl { + // Composable types implement their default constructors using custom composable factory interfaces. + // `IComposableFactory::CreateInstance` in this case. + fn ActivateInstance(&self) -> Result { + Err(E_NOTIMPL.into()) + } +} + +impl bindings::IComposableFactory_Impl for ComposableFactory_Impl { + fn CreateInstance( + &self, + base: Option<&windows_core::IInspectable>, + inner: &mut Option, + ) -> Result { + // windows-rs doesn't support binary composition + if base.is_some() || inner.is_some() { + Err(CLASS_E_NOAGGREGATION.into()) + } else { + Ok(Composable::new(0).into()) + } + } + + fn WithValue( + &self, + arg: i32, + base: Option<&windows_core::IInspectable>, + inner: &mut Option, + ) -> Result { + // windows-rs doesn't support binary composition + if base.is_some() || inner.is_some() { + Err(CLASS_E_NOAGGREGATION.into()) + } else { + Ok(Composable::new(arg).into()) + } + } +} + +#[implement(bindings::Composable)] +struct Composable(i32); + +impl bindings::IComposable_Impl for Composable_Impl { + fn Property(&self) -> Result { + Ok(self.0) + } +} + +impl Composable { + fn new(arg: i32) -> Self { + Self(arg) + } +} diff --git a/crates/tests/constructors/src/metadata.idl b/crates/tests/constructors/src/metadata.idl new file mode 100644 index 0000000000..a25379cc67 --- /dev/null +++ b/crates/tests/constructors/src/metadata.idl @@ -0,0 +1,22 @@ +namespace test_constructors +{ + runtimeclass Activatable + { + Activatable(); + + [method_name("WithValue")] + Activatable(Int32 arg); + + Int32 Property { get; }; + } + + unsealed runtimeclass Composable + { + Composable(); + + [method_name("WithValue")] + Composable(Int32 arg); + + Int32 Property { get; }; + } +} diff --git a/crates/tests/constructors_client/Cargo.toml b/crates/tests/constructors_client/Cargo.toml new file mode 100644 index 0000000000..3ddec0e501 --- /dev/null +++ b/crates/tests/constructors_client/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "test_constructors_client" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +doc = false +doctest = false + +[build-dependencies.windows-bindgen] +path = "../../libs/bindgen" + +[dependencies.windows-core] +path = "../../libs/core" + +[dependencies.windows] +path = "../../libs/windows" +features = [ + "implement", + "Foundation", + "Win32_Foundation", +] + +# TODO: this causes a warning about lack of linkage target. The point is to ensure that this binary dependency is built first but +# Cargo doesn't respect cdylib targets. https://github.com/rust-lang/cargo/issues/7825 +[dependencies.test_constructors] +path = "../constructors" diff --git a/crates/tests/constructors_client/build.rs b/crates/tests/constructors_client/build.rs new file mode 100644 index 0000000000..01851a480c --- /dev/null +++ b/crates/tests/constructors_client/build.rs @@ -0,0 +1,13 @@ +fn main() { + windows_bindgen::bindgen([ + "--in", + "../constructors/metadata.winmd", + "--out", + "src/bindings.rs", + "--filter", + "test_constructors", + "--config", + "no-bindgen-comment", + ]) + .unwrap(); +} diff --git a/crates/tests/constructors_client/src/bindings.rs b/crates/tests/constructors_client/src/bindings.rs new file mode 100644 index 0000000000..b8c2cdcab8 --- /dev/null +++ b/crates/tests/constructors_client/src/bindings.rs @@ -0,0 +1,216 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +windows_core::imp::define_interface!( + IActivatable, + IActivatable_Vtbl, + 0xe566522b_9c26_582b_950d_177b05d36efd +); +impl windows_core::RuntimeType for IActivatable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IActivatable_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Property: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IActivatableFactory, + IActivatableFactory_Vtbl, + 0xafc5aee9_aa78_5da6_85a2_69e67b45c620 +); +impl windows_core::RuntimeType for IActivatableFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IActivatableFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub WithValue: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IComposable, + IComposable_Vtbl, + 0xff2595d6_461d_5118_9296_f2a2b1e64544 +); +impl windows_core::RuntimeType for IComposable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IComposable_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Property: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IComposableFactory, + IComposableFactory_Vtbl, + 0x6a461099_83c0_5810_9e20_2e8b9521d143 +); +impl windows_core::RuntimeType for IComposableFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IComposableFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CreateInstance: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub WithValue: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Activatable(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Activatable, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Activatable { + pub fn new() -> windows_core::Result { + Self::IActivationFactory(|f| f.ActivateInstance::()) + } + fn IActivationFactory< + R, + F: FnOnce(&windows_core::imp::IGenericFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + Activatable, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + pub fn Property(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Property)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn WithValue(arg: i32) -> windows_core::Result { + Self::IActivatableFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).WithValue)( + windows_core::Interface::as_raw(this), + arg, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + #[doc(hidden)] + pub fn IActivatableFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for Activatable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Activatable { + type Vtable = IActivatable_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Activatable { + const NAME: &'static str = "test_constructors.Activatable"; +} +unsafe impl Send for Activatable {} +unsafe impl Sync for Activatable {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Composable(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Composable, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Composable { + pub fn Property(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Property)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn new() -> windows_core::Result { + Self::IComposableFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn WithValue(arg: i32) -> windows_core::Result { + Self::IComposableFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).WithValue)( + windows_core::Interface::as_raw(this), + arg, + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + #[doc(hidden)] + pub fn IComposableFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for Composable { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Composable { + type Vtable = IComposable_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Composable { + const NAME: &'static str = "test_constructors.Composable"; +} +unsafe impl Send for Composable {} +unsafe impl Sync for Composable {} diff --git a/crates/tests/constructors_client/src/lib.rs b/crates/tests/constructors_client/src/lib.rs new file mode 100644 index 0000000000..875d7f3903 --- /dev/null +++ b/crates/tests/constructors_client/src/lib.rs @@ -0,0 +1,22 @@ +#![cfg(test)] + +mod bindings; +use bindings::*; +use windows::core::*; + +#[test] +fn test() -> Result<()> { + let activatable = Activatable::new()?; + assert_eq!(activatable.Property()?, 0); + + let activatable = Activatable::WithValue(123)?; + assert_eq!(activatable.Property()?, 123); + + let composable = Composable::new()?; + assert_eq!(composable.Property()?, 0); + + let composable = Composable::WithValue(456)?; + assert_eq!(composable.Property()?, 456); + + Ok(()) +}