From 0dfa3de09cff83c4d7fdee4c2632cf8a0e0ad89c Mon Sep 17 00:00:00 2001 From: Max Strini Date: Sat, 27 Jul 2019 06:26:37 -0700 Subject: [PATCH] Provide COM_INTERFACE macro to support COM interop; export dependencies The implementation of the macro mostly closely mirrors RT_INTERFACE!; I started with a copy of that macro, removed support for WinRT-specific features (generics, statics, IInspectable special case) and Rt* trait impls, then added the method impls directly in the macro expansion since we can't rely on generated code here. The other major difference for the user is that those method impls are all marked unsafe and expose an unrefined direct translation of the ABI with raw pointers, exposed HRESULTs, out pointers for returns, PascalCasing, etc. The idea is that our COM interop support is intended to be purely a low-level FFI rather than a higher-level "projection," reflecting that projections are mostly a WinRT-specific concept. --- src/comptr.rs | 4 +-- src/interop.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 6 ++-- 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/interop.rs diff --git a/src/comptr.rs b/src/comptr.rs index 4a41939..03e6d83 100644 --- a/src/comptr.rs +++ b/src/comptr.rs @@ -29,7 +29,7 @@ impl Clone for ComAbi { /// Smart pointer for Windows Runtime objects. This pointer automatically maintains the /// reference count of the underlying COM object. #[repr(transparent)] -pub(crate) struct ComPtr(ptr::NonNull); +pub struct ComPtr(ptr::NonNull); pub(crate) trait ComPtrHelpers { /// Changes the type of the underlying COM object to a different interface without doing `QueryInterface`. @@ -69,7 +69,7 @@ impl ComPtr { } #[inline] - pub(crate) fn as_abi(&self) -> &T { + pub fn as_abi(&self) -> &T { unsafe { self.0.as_ref() } } } diff --git a/src/interop.rs b/src/interop.rs new file mode 100644 index 0000000..66637c5 --- /dev/null +++ b/src/interop.rs @@ -0,0 +1,84 @@ +#[allow(unused_macros)] +#[macro_export] +macro_rules! COM_INTERFACE { + // version with no methods + ($(#[$attr:meta])* interface $interface:ident ($vtbl:ident) : $pinterface:ident [$iid:ident] + {} + ) => { + #[repr(transparent)] #[allow(missing_copy_implementations)] #[doc(hidden)] + pub struct $vtbl { + pub parent: <<$pinterface as $crate::ComInterface>::TAbi as $crate::ComInterfaceAbi>::Vtbl + } + $(#[$attr])* #[repr(transparent)] #[derive(Clone)] + pub struct $interface($crate::ComPtr<$crate::ComAbi<$vtbl>>); + impl $crate::ComIid for $interface { + #[inline] fn iid() -> &'static crate::Guid { &$iid } + } + impl $crate::ComInterface for $interface { + type TAbi = $crate::ComAbi<$vtbl>; + #[inline] unsafe fn wrap_com(ptr: *mut Self::TAbi) -> Self { $interface($crate::ComPtr::wrap(ptr)) } + #[inline] fn get_abi(&self) -> &Self::TAbi { self.0.as_abi() } + } + impl std::ops::Deref for $interface { + type Target = $crate::$pinterface; + #[inline] + fn deref(&self) -> &$crate::$pinterface { + unsafe { std::mem::transmute(self) } + } + } + impl std::ops::DerefMut for $interface { + #[inline] + fn deref_mut(&mut self) -> &mut $crate::$pinterface { + unsafe { std::mem::transmute(self) } + } + } + }; + + // version with methods + ($(#[$attr:meta])* interface $interface:ident ($vtbl:ident) : $pinterface:ident [$iid:ident] + {$( + $(#[cfg($cond_attr:meta)])* fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty + ),+} + ) => { + #[repr(C)] #[allow(missing_copy_implementations)] #[doc(hidden)] + pub struct $vtbl { + pub parent: <<$pinterface as $crate::ComInterface>::TAbi as $crate::ComInterfaceAbi>::Vtbl + $(, $(#[cfg($cond_attr)])* pub $method: unsafe extern "system" fn( + This: *mut $interface + $(,$p: $t)* + ) -> $rtr)+ + } + $(#[$attr])* #[repr(transparent)] #[derive(Clone)] + pub struct $interface($crate::ComPtr<$crate::ComAbi<$vtbl>>); + impl $interface { + #[inline] + $(pub unsafe fn $method(&mut self $(,$p: $t)*) -> $rtr { + let abi = $crate::ComInterface::get_abi(&*self); + ((*$crate::ComInterfaceAbi::get_vtbl(&*abi)).$method)( + abi as *const _ as *mut _ $(,$p)* + ) + })+ + } + impl $crate::ComIid for $interface { + #[inline] fn iid() -> &'static crate::Guid { &$iid } + } + impl $crate::ComInterface for $interface { + type TAbi = $crate::ComAbi<$vtbl>; + #[inline] unsafe fn wrap_com(ptr: *mut Self::TAbi) -> Self { $interface($crate::ComPtr::wrap(ptr)) } + #[inline] fn get_abi(&self) -> &Self::TAbi { self.0.as_abi() } + } + impl std::ops::Deref for $interface { + type Target = $crate::$pinterface; + #[inline] + fn deref(&self) -> &$crate::$pinterface { + unsafe { std::mem::transmute(self) } + } + } + impl std::ops::DerefMut for $interface { + #[inline] + fn deref_mut(&mut self) -> &mut $crate::$pinterface { + unsafe { std::mem::transmute(self) } + } + } + }; +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 83102b7..590b8d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ pub use guid::Guid; pub type TrustLevel = w::winrt::inspectable::TrustLevel; // Compared to the DEFINE_GUID macro from winapi, this one creates a private const +#[macro_export] macro_rules! DEFINE_IID { ( $name:ident, $l:expr, $w1:expr, $w2:expr, $b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, @@ -57,12 +58,13 @@ mod bstr; pub use bstr::BStr; mod comptr; -pub(crate) use comptr::ComPtr; -pub use comptr::ComArray; +pub use comptr::{ComPtr, ComArray, ComAbi}; mod cominterfaces; pub use cominterfaces::{ComInterface, ComInterfaceAbi, ComIid, IUnknown, IRestrictedErrorInfo, IAgileObject}; +pub mod interop; + mod rt; pub use rt::{RtInterface, RtClassInterface, RtNamedClass, RtValueType, RtType, RtActivatable, RtDefaultConstructible, IInspectable, IInspectableVtbl, IActivationFactory,