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