diff --git a/crates/tests/bindgen/src/reference_dependency_flat.rs b/crates/tests/bindgen/src/reference_dependency_flat.rs index 67c65db2ea..00a5b0c379 100644 --- a/crates/tests/bindgen/src/reference_dependency_flat.rs +++ b/crates/tests/bindgen/src/reference_dependency_flat.rs @@ -84,6 +84,16 @@ impl IMemoryBufferReference { .map(|| result__) } } + pub fn RemoveClosed(&self, cookie: i64) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).RemoveClosed)( + windows_core::Interface::as_raw(this), + cookie, + ) + .ok() + } + } pub fn Close(&self) -> windows_core::Result<()> { let this = &windows_core::Interface::cast::(self)?; unsafe { @@ -97,6 +107,7 @@ impl windows_core::RuntimeName for IMemoryBufferReference { } pub trait IMemoryBufferReference_Impl: IClosable_Impl { fn Capacity(&self) -> windows_core::Result; + fn RemoveClosed(&self, cookie: i64) -> windows_core::Result<()>; } impl IMemoryBufferReference_Vtbl { pub const fn new() -> Self { @@ -116,12 +127,22 @@ impl IMemoryBufferReference_Vtbl { Err(err) => err.into(), } } + unsafe extern "system" fn RemoveClosed< + Identity: IMemoryBufferReference_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + cookie: i64, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IMemoryBufferReference_Impl::RemoveClosed(this, cookie).into() + } Self { base__: windows_core::IInspectable_Vtbl::new::( ), Capacity: Capacity::, add_Closed: 0, - remove_Closed: 0, + RemoveClosed: RemoveClosed::, } } pub fn matches(iid: &windows_core::GUID) -> bool { @@ -134,5 +155,6 @@ pub struct IMemoryBufferReference_Vtbl { pub Capacity: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, add_Closed: usize, - remove_Closed: usize, + pub RemoveClosed: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, } diff --git a/crates/tests/bindgen/src/reference_dependency_full.rs b/crates/tests/bindgen/src/reference_dependency_full.rs index 10372dcbc4..a5bd43ad6a 100644 --- a/crates/tests/bindgen/src/reference_dependency_full.rs +++ b/crates/tests/bindgen/src/reference_dependency_full.rs @@ -89,6 +89,16 @@ pub mod Windows { .map(|| result__) } } + pub fn RemoveClosed(&self, cookie: i64) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).RemoveClosed)( + windows_core::Interface::as_raw(this), + cookie, + ) + .ok() + } + } pub fn Close(&self) -> windows_core::Result<()> { let this = &windows_core::Interface::cast::(self)?; unsafe { @@ -104,6 +114,7 @@ pub mod Windows { } pub trait IMemoryBufferReference_Impl: IClosable_Impl { fn Capacity(&self) -> windows_core::Result; + fn RemoveClosed(&self, cookie: i64) -> windows_core::Result<()>; } impl IMemoryBufferReference_Vtbl { pub const fn new() -> Self { @@ -124,6 +135,17 @@ pub mod Windows { Err(err) => err.into(), } } + unsafe extern "system" fn RemoveClosed< + Identity: IMemoryBufferReference_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + cookie: i64, + ) -> windows_core::HRESULT { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IMemoryBufferReference_Impl::RemoveClosed(this, cookie).into() + } Self { base__: windows_core::IInspectable_Vtbl::new::< Identity, @@ -132,7 +154,7 @@ pub mod Windows { >(), Capacity: Capacity::, add_Closed: 0, - remove_Closed: 0, + RemoveClosed: RemoveClosed::, } } pub fn matches(iid: &windows_core::GUID) -> bool { @@ -147,7 +169,8 @@ pub mod Windows { *mut u32, ) -> windows_core::HRESULT, add_Closed: usize, - remove_Closed: usize, + pub RemoveClosed: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, } } } diff --git a/crates/tests/bindgen/src/reference_dependency_skip_root.rs b/crates/tests/bindgen/src/reference_dependency_skip_root.rs index 10372dcbc4..a5bd43ad6a 100644 --- a/crates/tests/bindgen/src/reference_dependency_skip_root.rs +++ b/crates/tests/bindgen/src/reference_dependency_skip_root.rs @@ -89,6 +89,16 @@ pub mod Windows { .map(|| result__) } } + pub fn RemoveClosed(&self, cookie: i64) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).RemoveClosed)( + windows_core::Interface::as_raw(this), + cookie, + ) + .ok() + } + } pub fn Close(&self) -> windows_core::Result<()> { let this = &windows_core::Interface::cast::(self)?; unsafe { @@ -104,6 +114,7 @@ pub mod Windows { } pub trait IMemoryBufferReference_Impl: IClosable_Impl { fn Capacity(&self) -> windows_core::Result; + fn RemoveClosed(&self, cookie: i64) -> windows_core::Result<()>; } impl IMemoryBufferReference_Vtbl { pub const fn new() -> Self { @@ -124,6 +135,17 @@ pub mod Windows { Err(err) => err.into(), } } + unsafe extern "system" fn RemoveClosed< + Identity: IMemoryBufferReference_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + cookie: i64, + ) -> windows_core::HRESULT { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IMemoryBufferReference_Impl::RemoveClosed(this, cookie).into() + } Self { base__: windows_core::IInspectable_Vtbl::new::< Identity, @@ -132,7 +154,7 @@ pub mod Windows { >(), Capacity: Capacity::, add_Closed: 0, - remove_Closed: 0, + RemoveClosed: RemoveClosed::, } } pub fn matches(iid: &windows_core::GUID) -> bool { @@ -147,7 +169,8 @@ pub mod Windows { *mut u32, ) -> windows_core::HRESULT, add_Closed: usize, - remove_Closed: usize, + pub RemoveClosed: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, } } } diff --git a/crates/tests/winrt/event/Cargo.toml b/crates/tests/winrt/event_core/Cargo.toml similarity index 88% rename from crates/tests/winrt/event/Cargo.toml rename to crates/tests/winrt/event_core/Cargo.toml index ef21dac692..e29813c6cc 100644 --- a/crates/tests/winrt/event/Cargo.toml +++ b/crates/tests/winrt/event_core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "test_event" +name = "test_event_core" version = "0.0.0" edition = "2021" publish = false diff --git a/crates/tests/winrt/event/src/lib.rs b/crates/tests/winrt/event_core/src/lib.rs similarity index 100% rename from crates/tests/winrt/event/src/lib.rs rename to crates/tests/winrt/event_core/src/lib.rs diff --git a/crates/tests/winrt/event/tests/tests.rs b/crates/tests/winrt/event_core/tests/tests.rs similarity index 100% rename from crates/tests/winrt/event/tests/tests.rs rename to crates/tests/winrt/event_core/tests/tests.rs diff --git a/crates/tests/winrt/events/Cargo.toml b/crates/tests/winrt/events/Cargo.toml new file mode 100644 index 0000000000..8932808e24 --- /dev/null +++ b/crates/tests/winrt/events/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "test_events" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doc = false +doctest = false + +[build-dependencies.windows-bindgen] +workspace = true + +[dependencies.windows-core] +workspace = true + +[dependencies.windows] +workspace = true +features = [ + "Foundation", + "Win32_System_WinRT", +] diff --git a/crates/tests/winrt/events/build.rs b/crates/tests/winrt/events/build.rs new file mode 100644 index 0000000000..eac24b1b3d --- /dev/null +++ b/crates/tests/winrt/events/build.rs @@ -0,0 +1,37 @@ +fn main() { + println!("cargo:rerun-if-changed=src/metadata.idl"); + + 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", + "../../../libs/bindgen/default", + "--out", + "src/bindings.rs", + "--filter", + "test_events", + "--implement", + "--no-comment", + "--flat", + "--reference", + "windows,skip-root,Windows", + ]); +} diff --git a/crates/tests/winrt/events/src/bindings.rs b/crates/tests/winrt/events/src/bindings.rs new file mode 100644 index 0000000000..e53a67590e --- /dev/null +++ b/crates/tests/winrt/events/src/bindings.rs @@ -0,0 +1,154 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] + +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Class(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Class, windows_core::IUnknown, windows_core::IInspectable); +impl Class { + 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 = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + pub fn Signal(&self, value: i32) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Signal)( + windows_core::Interface::as_raw(this), + value, + &mut result__, + ) + .map(|| result__) + } + } + pub fn Event(&self, handler: P0) -> windows_core::Result + where + P0: windows_core::Param>, + { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Event)( + windows_core::Interface::as_raw(this), + handler.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn RemoveEvent(&self, token: i64) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).RemoveEvent)( + windows_core::Interface::as_raw(this), + token, + ) + .ok() + } + } +} +impl windows_core::RuntimeType for Class { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Class { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Class { + const NAME: &'static str = "test_events.Class"; +} +unsafe impl Send for Class {} +unsafe impl Sync for Class {} +windows_core::imp::define_interface!(IClass, IClass_Vtbl, 0xad3fd5e5_03a8_5c1e_ab60_efa5e9379730); +impl windows_core::RuntimeType for IClass { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +impl windows_core::RuntimeName for IClass { + const NAME: &'static str = "test_events.IClass"; +} +pub trait IClass_Impl: windows_core::IUnknownImpl { + fn Signal(&self, value: i32) -> windows_core::Result; + fn Event( + &self, + handler: Option<&windows::Foundation::TypedEventHandler>, + ) -> windows_core::Result; + fn RemoveEvent(&self, token: i64) -> windows_core::Result<()>; +} +impl IClass_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn Signal( + this: *mut core::ffi::c_void, + value: i32, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IClass_Impl::Signal(this, value) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn Event( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + result__: *mut i64, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IClass_Impl::Event(this, windows_core::from_raw_borrowed(&handler)) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn RemoveEvent( + this: *mut core::ffi::c_void, + token: i64, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IClass_Impl::RemoveEvent(this, token).into() + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Signal: Signal::, + Event: Event::, + RemoveEvent: RemoveEvent::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +#[repr(C)] +pub struct IClass_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Signal: + unsafe extern "system" fn(*mut core::ffi::c_void, i32, *mut i32) -> windows_core::HRESULT, + pub Event: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut i64, + ) -> windows_core::HRESULT, + pub RemoveEvent: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, +} diff --git a/crates/tests/winrt/events/src/lib.rs b/crates/tests/winrt/events/src/lib.rs new file mode 100644 index 0000000000..0addeb2aa1 --- /dev/null +++ b/crates/tests/winrt/events/src/lib.rs @@ -0,0 +1,57 @@ +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_events.Class" { + factory.write(Some(ClassFactory.into())).into() + } else { + _ = factory.write(None); + CLASS_E_CLASSNOTAVAILABLE + } +} + +#[implement(IActivationFactory)] +struct ClassFactory; + +impl IActivationFactory_Impl for ClassFactory_Impl { + fn ActivateInstance(&self) -> Result { + Ok(Class::new().into()) + } +} + +#[implement(bindings::Class)] +struct Class(Event>); + +impl bindings::IClass_Impl for Class_Impl { + fn Signal(&self, value: i32) -> Result { + let mut counter = 0; + self.0.call(|delegate| { + counter += 1; + delegate.Invoke(self.as_interface(), value) + }); + Ok(counter) + } + + fn Event( + &self, + handler: Option<&windows::Foundation::TypedEventHandler>, + ) -> windows_core::Result { + self.0.add(handler.unwrap()) + } + + fn RemoveEvent(&self, token: i64) -> windows_core::Result<()> { + self.0.remove(token); + Ok(()) + } +} + +impl Class { + fn new() -> Self { + Self(Event::new()) + } +} diff --git a/crates/tests/winrt/events/src/metadata.idl b/crates/tests/winrt/events/src/metadata.idl new file mode 100644 index 0000000000..97164058f7 --- /dev/null +++ b/crates/tests/winrt/events/src/metadata.idl @@ -0,0 +1,10 @@ +namespace test_events +{ + runtimeclass Class + { + Class(); + + Int32 Signal(Int32 value); + event Windows.Foundation.TypedEventHandler Event; + } +} diff --git a/crates/tests/winrt/events_client/Cargo.toml b/crates/tests/winrt/events_client/Cargo.toml new file mode 100644 index 0000000000..8f2ccbab9d --- /dev/null +++ b/crates/tests/winrt/events_client/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "test_events_client" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +doc = false +doctest = false + +[build-dependencies.windows-bindgen] +workspace = true + +[dependencies.windows-core] +workspace = true + +[dependencies.windows] +workspace = true +features = [ + "Foundation", + "Win32_Foundation", +] + +# The build needs the output (.dll) of the component. This causes a warning about lack of linkage target. +# Cargo doesn't understand cdylib targets. https://github.com/rust-lang/cargo/issues/7825 +[dependencies.test_events] +path = "../events" diff --git a/crates/tests/winrt/events_client/build.rs b/crates/tests/winrt/events_client/build.rs new file mode 100644 index 0000000000..fc5df736cf --- /dev/null +++ b/crates/tests/winrt/events_client/build.rs @@ -0,0 +1,17 @@ +fn main() { + println!("cargo:rerun-if-changed=../events/metadata.winmd"); + + windows_bindgen::bindgen([ + "--in", + "../events/metadata.winmd", + "../../../libs/bindgen/default", + "--out", + "src/bindings.rs", + "--filter", + "test_events", + "--no-comment", + "--flat", + "--reference", + "windows,skip-root,Windows", + ]); +} diff --git a/crates/tests/winrt/events_client/src/bindings.rs b/crates/tests/winrt/events_client/src/bindings.rs new file mode 100644 index 0000000000..5756a0eabd --- /dev/null +++ b/crates/tests/winrt/events_client/src/bindings.rs @@ -0,0 +1,95 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] + +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Class(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Class, windows_core::IUnknown, windows_core::IInspectable); +impl Class { + 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 = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + pub fn Signal(&self, value: i32) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Signal)( + windows_core::Interface::as_raw(this), + value, + &mut result__, + ) + .map(|| result__) + } + } + pub fn Event(&self, handler: P0) -> windows_core::Result + where + P0: windows_core::Param>, + { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Event)( + windows_core::Interface::as_raw(this), + handler.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn RemoveEvent(&self, token: i64) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).RemoveEvent)( + windows_core::Interface::as_raw(this), + token, + ) + .ok() + } + } +} +impl windows_core::RuntimeType for Class { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Class { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Class { + const NAME: &'static str = "test_events.Class"; +} +unsafe impl Send for Class {} +unsafe impl Sync for Class {} +windows_core::imp::define_interface!(IClass, IClass_Vtbl, 0xad3fd5e5_03a8_5c1e_ab60_efa5e9379730); +impl windows_core::RuntimeType for IClass { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IClass_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Signal: + unsafe extern "system" fn(*mut core::ffi::c_void, i32, *mut i32) -> windows_core::HRESULT, + pub Event: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut i64, + ) -> windows_core::HRESULT, + pub RemoveEvent: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, +} diff --git a/crates/tests/winrt/events_client/src/lib.rs b/crates/tests/winrt/events_client/src/lib.rs new file mode 100644 index 0000000000..a73648977b --- /dev/null +++ b/crates/tests/winrt/events_client/src/lib.rs @@ -0,0 +1,46 @@ +#![cfg(test)] + +mod bindings; +use bindings::*; +use windows::{core::*, Foundation::*}; + +#[test] +fn test() -> Result<()> { + // The static just simplifies testing identity in closures. + let class: &'static Class = Box::leak(Box::new(Class::new()?)); + + // Signal returns how many delegates were called. + // The signal value is passed to each delegate. + assert_eq!(0, class.Signal(1)?); + + let token = class.Event(&TypedEventHandler::new( + move |sender: &Option, args: &i32| { + assert_eq!(sender.as_ref().unwrap(), class); + assert_eq!(*args, 2); + Ok(()) + }, + ))?; + + assert_eq!(1, class.Signal(2)?); + class.RemoveEvent(token)?; + assert_eq!(0, class.Signal(3)?); + + class.Event(&TypedEventHandler::new( + move |sender: &Option, args: &i32| { + assert_eq!(sender.as_ref().unwrap(), class); + assert_eq!(*args, 4); + Ok(()) + }, + ))?; + + class.Event(&TypedEventHandler::new( + move |sender: &Option, args: &i32| { + assert_eq!(sender.as_ref().unwrap(), class); + assert_eq!(*args, 4); + Ok(()) + }, + ))?; + + assert_eq!(2, class.Signal(4)?); + Ok(()) +}