зеркало из https://github.com/microsoft/com-rs.git
Production as a feature
This commit is contained in:
Родитель
184ab0e534
Коммит
55e77b52ab
|
@ -15,6 +15,9 @@ readme = "./README.md"
|
|||
[dependencies]
|
||||
com_macros = { version = "0.2", path = "macros" }
|
||||
|
||||
[features]
|
||||
production = []
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
path = "tests/progress.rs"
|
||||
|
|
|
@ -5,7 +5,7 @@ authors = ["Microsoft Corp"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
com = { path = "../../.." }
|
||||
com = { path = "../../..", features = ["production"] }
|
||||
interface = { path = "../interface" }
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -6,6 +6,6 @@ pub fn generate(struct_item: &CoClass) -> HelperTokenStream {
|
|||
let struct_ident = &struct_item.name;
|
||||
|
||||
quote! {
|
||||
unsafe impl com::CoClass for #struct_ident {}
|
||||
unsafe impl com::production::CoClass for #struct_ident {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use super::CoClass;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{Fields, Ident, ItemStruct};
|
||||
|
||||
/// The COM class object
|
||||
///
|
||||
|
@ -18,8 +17,10 @@ pub fn generate(co_class: &CoClass) -> TokenStream {
|
|||
let base_fields = gen_base_fields(&co_class.interfaces);
|
||||
let ref_count_field = gen_ref_count_field();
|
||||
let user_fields = &co_class.fields;
|
||||
let docs = &co_class.docs;
|
||||
|
||||
quote!(
|
||||
#(#docs)*
|
||||
#[repr(C)]
|
||||
#vis struct #struct_ident {
|
||||
#base_fields
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::CoClass;
|
||||
use proc_macro2::TokenStream as HelperTokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{Fields, Ident, ItemStruct};
|
||||
use syn::{Ident, ItemStruct};
|
||||
|
||||
pub fn generate(co_class: &CoClass) -> HelperTokenStream {
|
||||
let allocate_fn = gen_allocate_fn(co_class);
|
||||
|
|
|
@ -8,26 +8,32 @@ use syn::Ident;
|
|||
/// Takes into account the base interfaces exposed, as well as
|
||||
/// any interfaces exposed through an aggregated object.
|
||||
pub fn generate(co_class: &CoClass) -> TokenStream {
|
||||
let struct_ident = &co_class.name;
|
||||
let ident = quote::format_ident!("{}_IUNKNOWN", co_class.name.to_string().to_uppercase());
|
||||
|
||||
let add_ref = gen_add_ref();
|
||||
let release = gen_release(&co_class.interfaces, struct_ident);
|
||||
let query_interface = gen_query_interface(&co_class.interfaces);
|
||||
let add_ref = gen_add_ref(&co_class.name);
|
||||
let release = gen_release(&co_class.interfaces, &co_class.name);
|
||||
let query_interface = gen_query_interface(&co_class.name, &co_class.interfaces);
|
||||
|
||||
quote!(
|
||||
impl #struct_ident {
|
||||
quote! {
|
||||
const #ident: <::com::interfaces::IUnknown as ::com::ComInterface>::VTable = {
|
||||
#add_ref
|
||||
#release
|
||||
#query_interface
|
||||
}
|
||||
)
|
||||
|
||||
::com::interfaces::IUnknown::VTable {
|
||||
AddRef: add_ref ,
|
||||
Release: release,
|
||||
QueryInterface: query_interface,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_add_ref() -> TokenStream {
|
||||
pub fn gen_add_ref(name: &Ident) -> TokenStream {
|
||||
let add_ref_implementation = gen_add_ref_implementation();
|
||||
|
||||
quote! {
|
||||
unsafe extern "stdcall" fn add_ref(this_ptr: *mut *mut Self) -> u32 {
|
||||
extern "stdcall" fn add_ref(this_ptr: *mut *mut #name) -> u32 {
|
||||
assert!(!this_ptr.is_null());
|
||||
let this = &(*(*this_ptr));
|
||||
#add_ref_implementation
|
||||
|
@ -54,7 +60,7 @@ pub fn gen_release(interface_idents: &[syn::Path], name: &Ident) -> TokenStream
|
|||
let release_drops = gen_release_drops(interface_idents, name);
|
||||
|
||||
quote! {
|
||||
unsafe extern "stdcall" fn release(this_ptr: *mut *mut Self) -> u32 {
|
||||
unsafe extern "stdcall" fn release(this_ptr: *mut *mut #name) -> u32 {
|
||||
assert!(!this_ptr.is_null());
|
||||
let this = &(*(*this_ptr));
|
||||
#release_decrement
|
||||
|
@ -101,15 +107,12 @@ pub fn gen_release_drops(interface_idents: &[syn::Path], name: &Ident) -> TokenS
|
|||
}
|
||||
|
||||
fn gen_vptr_drops(interface_idents: &[syn::Path]) -> TokenStream {
|
||||
let vptr_drops = interface_idents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, interface)| {
|
||||
let vptr_field_ident = quote::format_ident!("__{}", index);
|
||||
quote!(
|
||||
Box::from_raw(this.#vptr_field_ident.as_ptr());
|
||||
)
|
||||
});
|
||||
let vptr_drops = interface_idents.iter().enumerate().map(|(index, _)| {
|
||||
let vptr_field_ident = quote::format_ident!("__{}", index);
|
||||
quote!(
|
||||
Box::from_raw(this.#vptr_field_ident.as_ptr());
|
||||
)
|
||||
});
|
||||
|
||||
quote!(#(#vptr_drops)*)
|
||||
}
|
||||
|
@ -120,13 +123,13 @@ fn gen_com_object_drop(name: &Ident) -> TokenStream {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn gen_query_interface(interface_idents: &[syn::Path]) -> TokenStream {
|
||||
pub fn gen_query_interface(name: &Ident, interface_idents: &[syn::Path]) -> TokenStream {
|
||||
// Generate match arms for implemented interfaces
|
||||
let base_match_arms = gen_base_match_arms(interface_idents);
|
||||
|
||||
quote! {
|
||||
unsafe extern "stdcall" fn query_interface(
|
||||
this_ptr: *mut *mut Self,
|
||||
this_ptr: *mut *mut #name,
|
||||
riid: *const com::sys::IID,
|
||||
ppv: *mut *mut std::ffi::c_void
|
||||
) -> com::sys::HRESULT {
|
||||
|
@ -141,7 +144,7 @@ pub fn gen_query_interface(interface_idents: &[syn::Path]) -> TokenStream {
|
|||
return com::sys::E_NOINTERFACE;
|
||||
}
|
||||
|
||||
Self::add_ref(this_ptr);
|
||||
#name::add_ref(this_ptr);
|
||||
com::sys::NOERROR
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use proc_macro2::{Ident, TokenStream};
|
||||
use syn::{AttributeArgs, ItemStruct};
|
||||
|
||||
use std::iter::FromIterator;
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use super::vtable_macro;
|
||||
use super::Interface;
|
||||
use crate::com_interface::{iid, vtable};
|
||||
|
||||
|
@ -9,7 +8,7 @@ pub fn generate(interface: &Interface) -> TokenStream {
|
|||
let interface_ident = &interface.name;
|
||||
let vtable_ident = vtable::ident(&interface_ident.to_string());
|
||||
let iid_ident = iid::ident(interface_ident);
|
||||
let vtable_macro = vtable_macro::ident(&interface_ident);
|
||||
// let vtable_macro = vtable_macro::ident(&interface_ident);
|
||||
let parent = if let Some(p) = &interface.parent {
|
||||
quote! { #p }
|
||||
} else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use quote::format_ident;
|
||||
use syn::{AttributeArgs, Ident, Meta, NestedMeta};
|
||||
use syn::Ident;
|
||||
|
||||
pub fn class_factory_ident(class_ident: &Ident) -> Ident {
|
||||
format_ident!("{}ClassFactory", class_ident)
|
||||
|
@ -8,44 +8,3 @@ pub fn class_factory_ident(class_ident: &Ident) -> Ident {
|
|||
pub fn ref_count_ident() -> Ident {
|
||||
format_ident!("__refcnt")
|
||||
}
|
||||
|
||||
pub fn vptr_field_ident(interface_ident: &Ident) -> Ident {
|
||||
format_ident!("__{}vptr", interface_ident.to_string().to_lowercase())
|
||||
}
|
||||
|
||||
pub fn base_interface_idents(attr_args: &AttributeArgs) -> Vec<Ident> {
|
||||
let mut base_interface_idents = Vec::new();
|
||||
|
||||
for attr_arg in attr_args {
|
||||
if let NestedMeta::Meta(Meta::List(ref attr)) = attr_arg {
|
||||
if attr
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.expect("Invalid attribute syntax")
|
||||
.ident
|
||||
!= "implements"
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for item in &attr.nested {
|
||||
if let NestedMeta::Meta(Meta::Path(p)) = item {
|
||||
assert!(
|
||||
p.segments.len() == 1,
|
||||
"Incapable of handling multiple path segments yet."
|
||||
);
|
||||
base_interface_idents.push(
|
||||
p.segments
|
||||
.last()
|
||||
.expect("Implemented interface is empty path")
|
||||
.ident
|
||||
.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base_interface_idents
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
use crate::interfaces::IUnknown;
|
||||
use crate::sys::IID;
|
||||
|
||||
/// A COM compliant interface pointer
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The struct implementing this trait must provide a valid vtable as the
|
||||
/// associated VTable type. A vtable is valid if:
|
||||
/// * it is `#[repr(C)]`
|
||||
/// * the type only contains `extern "stdcall" fn" definitions
|
||||
///
|
||||
/// The implementor must be a transparrently equivalent to a valid interface pointer
|
||||
/// for the interface `T`. An interface pointer as the name suggests points to an
|
||||
/// interface. A valid interface is itself trivial castable to a `*mut T::VTable`.
|
||||
/// In other words, the implementing type must also be equal to `*mut *const T::VTable`
|
||||
pub unsafe trait ComInterface: Sized + 'static {
|
||||
/// A COM compatible V-Table
|
||||
type VTable;
|
||||
/// The interface that this interface inherits from
|
||||
type Super: ComInterface;
|
||||
/// The associated id for this interface
|
||||
const IID: IID;
|
||||
|
||||
/// Check whether a given IID is in the inheritance hierarchy of this interface
|
||||
fn is_iid_in_inheritance_chain(riid: &IID) -> bool {
|
||||
riid == &Self::IID
|
||||
|| (Self::IID != <IUnknown as ComInterface>::IID
|
||||
&& <Self::Super as ComInterface>::is_iid_in_inheritance_chain(riid))
|
||||
}
|
||||
|
||||
/// Cast the interface pointer to a pointer to IUnknown.
|
||||
fn as_iunknown(&self) -> &IUnknown {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
|
||||
/// Cast the COM interface pointer to a raw pointer
|
||||
///
|
||||
/// The returned pointer is only guranteed valid for as long
|
||||
/// as the reference to self id valid.
|
||||
fn as_raw(&self) -> std::ptr::NonNull<std::ptr::NonNull<Self::VTable>> {
|
||||
unsafe { std::mem::transmute_copy(self) }
|
||||
}
|
||||
}
|
100
src/lib.rs
100
src/lib.rs
|
@ -19,109 +19,29 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
mod abi_transferable;
|
||||
mod com_interface;
|
||||
pub mod interfaces;
|
||||
#[doc(hidden)]
|
||||
pub mod offset;
|
||||
mod param;
|
||||
#[doc(hidden)]
|
||||
pub mod registration;
|
||||
pub mod runtime;
|
||||
pub mod sys;
|
||||
|
||||
#[cfg(feature = "production")]
|
||||
/// Functionality for producing COM classes
|
||||
pub mod production;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use abi_transferable::AbiTransferable;
|
||||
use interfaces::IUnknown;
|
||||
#[doc(inline)]
|
||||
pub use com_interface::ComInterface;
|
||||
#[doc(inline)]
|
||||
pub use param::Param;
|
||||
#[doc(inline)]
|
||||
pub use sys::{CLSID, IID};
|
||||
|
||||
/// A COM compliant interface pointer
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The struct implementing this trait must provide a valid vtable as the
|
||||
/// associated VTable type. A vtable is valid if:
|
||||
/// * it is `#[repr(C)]`
|
||||
/// * the type only contains `extern "stdcall" fn" definitions
|
||||
///
|
||||
/// The implementor must be a transparrently equivalent to a valid interface pointer
|
||||
/// for the interface `T`. An interface pointer as the name suggests points to an
|
||||
/// interface. A valid interface is itself trivial castable to a `*mut T::VTable`.
|
||||
/// In other words, the implementing type must also be equal to `*mut *const T::VTable`
|
||||
pub unsafe trait ComInterface: Sized + 'static {
|
||||
/// A COM compatible V-Table
|
||||
type VTable;
|
||||
/// The interface that this interface inherits from
|
||||
type Super: ComInterface;
|
||||
/// The associated id for this interface
|
||||
const IID: IID;
|
||||
pub use com_macros::com_interface;
|
||||
|
||||
/// Check whether a given IID is in the inheritance hierarchy of this interface
|
||||
fn is_iid_in_inheritance_chain(riid: &IID) -> bool {
|
||||
riid == &Self::IID
|
||||
|| (Self::IID != <IUnknown as ComInterface>::IID
|
||||
&& <Self::Super as ComInterface>::is_iid_in_inheritance_chain(riid))
|
||||
}
|
||||
|
||||
/// Cast the interface pointer to a pointer to IUnknown.
|
||||
fn as_iunknown(&self) -> &IUnknown {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
|
||||
/// Cast the COM interface pointer to a raw pointer
|
||||
///
|
||||
/// The returned pointer is only guranteed valid for as long
|
||||
/// as the reference to self id valid.
|
||||
fn as_raw(&self) -> std::ptr::NonNull<std::ptr::NonNull<Self::VTable>> {
|
||||
unsafe { std::mem::transmute_copy(self) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A COM compliant class
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The implementing struct must have the following properties:
|
||||
/// * it is `#[repr(C)]`
|
||||
/// * The first fields of the struct are pointers to the backing VTables for
|
||||
/// each of the COM Interfaces the class implements
|
||||
pub unsafe trait CoClass {}
|
||||
|
||||
/// A COM interface that will be exposed in a COM server
|
||||
pub trait ProductionComInterface<T>: ComInterface {
|
||||
/// Get the vtable for a particular COM interface
|
||||
fn vtable<O: offset::Offset>() -> Self::VTable;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! vtable {
|
||||
($class:ident: $interface:ident, $offset:ident) => {
|
||||
<$interface as $crate::ProductionComInterface<$class>>::vtable::<$crate::offset::$offset>();
|
||||
};
|
||||
($class:ident: $interface:ident, 4usize) => {
|
||||
$crate::vtable!($class: $interface, Four)
|
||||
};
|
||||
($class:ident: $interface:ident, 3usize) => {
|
||||
$crate::vtable!($class: $interface, Three)
|
||||
};
|
||||
($class:ident: $interface:ident, 2usize) => {
|
||||
$crate::vtable!($class: $interface, Two)
|
||||
};
|
||||
($class:ident: $interface:ident, 1usize) => {
|
||||
$crate::vtable!($class: $interface, One)
|
||||
};
|
||||
($class:ident: $interface:ident, 0usize) => {
|
||||
$crate::vtable!($class: $interface, Zero)
|
||||
};
|
||||
($class:ident: $interface:ident) => {
|
||||
$crate::vtable!($class: $interface, Zero)
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use com_macros::{co_class, com_interface, VTable};
|
||||
#[cfg(feature = "production")]
|
||||
pub use com_macros::{co_class, VTable};
|
||||
|
||||
// this allows for the crate to refer to itself as `com` to keep macros consistent
|
||||
// whether they are used by some other crate or internally
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
mod co_class;
|
||||
#[doc(hidden)]
|
||||
pub mod offset;
|
||||
#[doc(hidden)]
|
||||
pub mod registration;
|
||||
|
||||
pub use co_class::CoClass;
|
||||
pub use co_class::ProductionComInterface;
|
|
@ -0,0 +1,46 @@
|
|||
use super::offset;
|
||||
use crate::ComInterface;
|
||||
|
||||
/// A COM compliant class
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The implementing struct must have the following properties:
|
||||
/// * it is `#[repr(C)]`
|
||||
/// * The first fields of the struct are pointers to the backing VTables for
|
||||
/// each of the COM Interfaces the class implements
|
||||
pub unsafe trait CoClass {}
|
||||
|
||||
/// A COM interface that will be exposed in a COM server
|
||||
pub trait ProductionComInterface<T>: ComInterface {
|
||||
/// Get the vtable for a particular COM interface
|
||||
fn vtable<O: offset::Offset>() -> Self::VTable;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! vtable {
|
||||
($class:ident: $interface:ident, $offset:ident) => {
|
||||
<$interface as $crate::production::ProductionComInterface<$class>>::vtable::<
|
||||
$crate::production::offset::$offset,
|
||||
>();
|
||||
};
|
||||
($class:ident: $interface:ident, 4usize) => {
|
||||
$crate::vtable!($class: $interface, Four)
|
||||
};
|
||||
($class:ident: $interface:ident, 3usize) => {
|
||||
$crate::vtable!($class: $interface, Three)
|
||||
};
|
||||
($class:ident: $interface:ident, 2usize) => {
|
||||
$crate::vtable!($class: $interface, Two)
|
||||
};
|
||||
($class:ident: $interface:ident, 1usize) => {
|
||||
$crate::vtable!($class: $interface, One)
|
||||
};
|
||||
($class:ident: $interface:ident, 0usize) => {
|
||||
$crate::vtable!($class: $interface, Zero)
|
||||
};
|
||||
($class:ident: $interface:ident) => {
|
||||
$crate::vtable!($class: $interface, Zero)
|
||||
};
|
||||
}
|
Загрузка…
Ссылка в новой задаче