Skip to content

Commit

Permalink
Provide COM_INTERFACE macro to support COM interop; export dependencies
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
contextfree authored and Boddlnagg committed Aug 3, 2019
1 parent 079a326 commit 0dfa3de
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/comptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<Vtbl> Clone for ComAbi<Vtbl> {
/// Smart pointer for Windows Runtime objects. This pointer automatically maintains the
/// reference count of the underlying COM object.
#[repr(transparent)]
pub(crate) struct ComPtr<T: ComInterfaceAbi>(ptr::NonNull<T>);
pub struct ComPtr<T: ComInterfaceAbi>(ptr::NonNull<T>);

pub(crate) trait ComPtrHelpers {
/// Changes the type of the underlying COM object to a different interface without doing `QueryInterface`.
Expand Down Expand Up @@ -69,7 +69,7 @@ impl<T: ComInterfaceAbi> ComPtr<T> {
}

#[inline]
pub(crate) fn as_abi(&self) -> &T {
pub fn as_abi(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
Expand Down
84 changes: 84 additions & 0 deletions src/interop.rs
Original file line number Diff line number Diff line change
@@ -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) }
}
}
};
}
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down

0 comments on commit 0dfa3de

Please sign in to comment.