derive_com_impl/lib.rs
1#![recursion_limit = "1024"]
2
3//! Implements a COM Object struct with automatic reference counting and implements
4//! IUnknown for you. This covers the most common use cases of creating COM objects
5//! from Rust. Supports generic parameters!
6//!
7//! ```
8//! #[macro_use]
9//! extern crate derive_com_impl;
10//!
11//! extern crate com_impl;
12//! extern crate winapi;
13//! extern crate wio;
14//!
15//! use com_impl::{VTable, Refcount};
16//! use winapi::ctypes::c_void;
17//! use winapi::shared::winerror::{ERROR_INVALID_INDEX, HRESULT, HRESULT_FROM_WIN32, S_OK};
18//! use winapi::um::dwrite::{IDWriteFontFileStream, IDWriteFontFileStreamVtbl};
19//! use wio::com::ComPtr;
20//!
21//! #[repr(C)]
22//! #[derive(ComImpl)]
23//! #[interfaces(IDWriteFontFileStream)]
24//! pub struct FileStream {
25//! vtbl: VTable<IDWriteFontFileStreamVtbl>,
26//! refcount: Refcount,
27//! write_time: u64,
28//! file_data: Vec<u8>,
29//! }
30//!
31//! impl FileStream {
32//! pub fn new(write_time: u64, data: Vec<u8>) -> ComPtr<IDWriteFontFileStream> {
33//! let ptr = FileStream::create_raw(write_time, data);
34//! let ptr = ptr as *mut IDWriteFontFileStream;
35//! unsafe { ComPtr::from_raw(ptr) }
36//! }
37//! }
38//!
39//! #[com_impl]
40//! unsafe impl IDWriteFontFileStream for FileStream {
41//! unsafe fn get_file_size(&self, size: *mut u64) -> HRESULT {
42//! *size = self.file_data.len() as u64;
43//! S_OK
44//! }
45//!
46//! unsafe fn get_last_write_time(&self, write_time: *mut u64) -> HRESULT {
47//! *write_time = self.write_time;
48//! S_OK
49//! }
50//!
51//! unsafe fn read_file_fragment(
52//! &self,
53//! start: *mut *const c_void,
54//! offset: u64,
55//! size: u64,
56//! ctx: *mut *mut c_void,
57//! ) -> HRESULT {
58//! if offset > std::isize::MAX as u64 || size > std::isize::MAX as u64 {
59//! return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
60//! }
61//!
62//! let offset = offset as usize;
63//! let size = size as usize;
64//!
65//! if offset + size > self.file_data.len() {
66//! return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
67//! }
68//!
69//! *start = self.file_data.as_ptr().offset(offset as isize) as *const c_void;
70//! *ctx = std::ptr::null_mut();
71//!
72//! S_OK
73//! }
74//!
75//! unsafe fn release_file_fragment(&self, _ctx: *mut c_void) {
76//! // Nothing to do
77//! }
78//! }
79//!
80//! fn main() {
81//! let ptr = FileStream::new(100, vec![0xDE, 0xAF, 0x00, 0xF0, 0x01]);
82//!
83//! // Do things with ptr
84//! }
85//! ```
86
87#[macro_use]
88extern crate quote;
89#[macro_use]
90extern crate syn;
91
92extern crate proc_macro;
93extern crate proc_macro2;
94
95use proc_macro::TokenStream;
96use syn::DeriveInput;
97use syn::{AttributeArgs, Item};
98
99mod derive;
100mod com_impl;
101
102#[proc_macro_derive(ComImpl, attributes(interfaces))]
103/// `#[derive(ComImpl)]`
104///
105/// Automatically implements reference counting for your COM object, creating a pointer via
106/// `Box::into_raw` and deallocating with `Box::from_raw`. A private inherent method named
107/// `create_raw` is added to your type that takes all of your struct members except the vtable
108/// and refcount as parameters in declaration order.
109///
110/// ### Additional attributes:
111///
112/// `#[interfaces(ISome, IThing)]`
113///
114/// - Specifies the COM interfaces that this type should respond to in QueryInterface. IUnknown
115/// is included implicitly. If this attribute is not specified it will be assumed that the only
116/// types responded to are IUnknown and the type specified in the VTable.
117pub fn derive_com_impl(input: TokenStream) -> TokenStream {
118 let input = parse_macro_input!(input as DeriveInput);
119
120 derive::expand_derive_com_impl(&input)
121 .unwrap_or_else(compile_error)
122 .into()
123}
124
125#[proc_macro_attribute]
126/// `#[com_impl]`
127///
128/// Generates a VTable for the functions implemented in the `impl` block this attribute is
129/// applied to.
130///
131/// For the general syntax see the example in the crate root.
132///
133/// ### Additional parameters
134///
135/// `#[com_impl(no_parent)]`
136///
137/// Specifies that the vtable being implemented here does not have a `parent` member. These
138/// are very rare, but include IUnknown.
139///
140/// ### Attributes on methods
141///
142/// `#[com_name = "..."]`
143///
144/// Overrides the method name this function corresponds to in the VTable. Method names by
145/// default are mapped from snake_case to PascalCase to determine their winapi names.
146///
147/// <hb/>
148///
149/// `#[panic(abort)]`
150///
151/// Specifies that in the stub function, code should be generated to catch any unwinding from
152/// the user-provided bodies and abort on panic.
153///
154/// <hb/>
155///
156/// `#[panic(result = "EXPRESSION")]`
157///
158/// Specifies that in the stub functions code should be generated to catch any unwinding from
159/// the user-provided bodies and return the specified expression. The expression should have
160/// the same type as the standard function body return. This is most useful with functions that
161/// return an HRESULT.
162pub fn com_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
163 let args = parse_macro_input!(attr as AttributeArgs);
164 let item = parse_macro_input!(item as Item);
165
166 com_impl::expand_com_impl(&args, &item)
167 .unwrap_or_else(compile_error)
168 .into()
169}
170
171fn compile_error(message: String) -> proc_macro2::TokenStream {
172 quote! {
173 compile_error!(#message);
174 }
175}