Skip to main content

ferroui_microcom/
com_object.rs

1use std::ffi::c_void;
2use std::marker::PhantomData;
3use std::sync::atomic::{fence, AtomicPtr, AtomicU32, Ordering};
4
5use crate::{ComPtr, Guid, IUnknownVtbl, Interface, RawHResult, E_NOINTERFACE, E_POINTER, S_OK};
6
7/// Links an interface `Self` to a Rust type `T` that implements its
8/// callbacks: supplies the vtable native code will call through.
9///
10/// Generated code provides
11/// `unsafe impl<T: IFooImpl> ImplementedBy<T> for IFoo`.
12///
13/// # Safety
14/// `VTBL` must be a vtable for `Self` whose thunks expect `this` to point at
15/// a [`ComObject<T>`].
16pub unsafe trait ImplementedBy<T>: Interface {
17    const VTBL: &'static Self::Vtbl;
18}
19
20/// One interface of a Rust-implemented COM object as native code sees it (a
21/// "COM callable wrapper" of the MicroCom runtime): the first word is the
22/// vtable pointer, so a `*mut ComObject<T>` is the interface pointer; the
23/// second leads to the object the wrapper belongs to.
24///
25/// An object has one wrapper per interface it was made with, as the shadow
26/// of an object has in the MicroCom runtime. They share the reference count
27/// and the value, and `QueryInterface` on any of them answers with the
28/// wrapper of the interface asked for.
29#[repr(C)]
30pub struct ComObject<T> {
31    vtbl: *const c_void,
32    shadow: *const Shadow<T>,
33}
34
35/// An interface a Rust value is exposed through: its vtable and the test
36/// for the identifiers it answers to (its own and those of its bases).
37pub struct InterfaceEntry<T> {
38    vtbl: *const c_void,
39    matches_iid: fn(&Guid) -> bool,
40    _value: PhantomData<fn(T)>,
41}
42
43impl<T> InterfaceEntry<T> {
44    /// The entry of interface `I`, which `T` implements.
45    pub fn of<I: ImplementedBy<T>>() -> InterfaceEntry<T> {
46        InterfaceEntry { vtbl: I::VTBL as *const I::Vtbl as *const c_void, matches_iid: I::matches_iid, _value: PhantomData }
47    }
48}
49
50/// The object behind its wrappers (the shadow of the MicroCom runtime): the
51/// reference count, the wrappers in the order the interfaces were given,
52/// the inner object of a composed object ([`set_inner`]), and the Rust value.
53struct Shadow<T> {
54    refs: AtomicU32,
55    wrappers: Box<[Wrapper<T>]>,
56    inner: AtomicPtr<c_void>,
57    value: T,
58}
59
60struct Wrapper<T> {
61    object: ComObject<T>,
62    matches_iid: fn(&Guid) -> bool,
63}
64
65impl<T> ComObject<T> {
66    /// Recovers the Rust value from the `this` pointer a thunk receives:
67    /// the pointer of any interface of the object.
68    ///
69    /// # Safety
70    /// `this` must point at a live `ComObject<T>`.
71    #[inline]
72    pub unsafe fn value<'a>(this: *mut c_void) -> &'a T {
73        &(*(*(this as *const ComObject<T>)).shadow).value
74    }
75
76    /// Current reference count (diagnostics/tests only).
77    ///
78    /// # Safety
79    /// `this` must point at a live `ComObject<T>`.
80    pub unsafe fn ref_count(this: *mut c_void) -> u32 {
81        (*(*(this as *const ComObject<T>)).shadow).refs.load(Ordering::Relaxed)
82    }
83}
84
85/// Allocates the object with its wrappers and returns the wrapper of the
86/// first interface, with the one reference of the object.
87fn new_object<T>(value: T, interfaces: impl Iterator<Item = InterfaceEntry<T>>) -> *mut ComObject<T> {
88    let wrappers: Box<[Wrapper<T>]> = interfaces
89        .map(|entry| Wrapper {
90            object: ComObject { vtbl: entry.vtbl, shadow: std::ptr::null() },
91            matches_iid: entry.matches_iid,
92        })
93        .collect();
94    let shadow = Box::into_raw(Box::new(Shadow {
95        refs: AtomicU32::new(1),
96        wrappers,
97        inner: AtomicPtr::new(std::ptr::null_mut()),
98        value,
99    }));
100    // SAFETY: the allocation above, which nothing else refers to yet; the
101    // wrappers live in an allocation of their own that does not move for as
102    // long as the shadow lives, so pointers to them stay valid.
103    unsafe {
104        for wrapper in (*shadow).wrappers.iter_mut() {
105            wrapper.object.shadow = shadow;
106        }
107        &raw mut (*shadow).wrappers[0].object
108    }
109}
110
111/// Moves `value` to the heap behind a COM vtable for interface `I` and
112/// returns the (single) owning reference.
113///
114/// The object is destroyed — and `value` dropped — when the last reference is
115/// released. Reference counting is atomic because native code may release
116/// from another thread (in which case `value` is dropped on that thread);
117/// the interface methods themselves are only ever invoked on the UI thread.
118pub fn make_com<I, T>(value: T) -> ComPtr<I>
119where
120    I: ImplementedBy<T>,
121    T: 'static,
122{
123    make_com_with(value, [])
124}
125
126/// Moves `value` to the heap as a COM object that implements interface `I`
127/// and every interface of `more`, and returns the (single) owning reference
128/// as `I`.
129///
130/// `QueryInterface` on any interface of the object answers for all of them
131/// (and for their bases): the first interface in the order `I`, `more` that
132/// answers to the identifier asked for. `IUnknown` is therefore always the
133/// pointer of `I`, whichever interface is asked, which is the identity of
134/// the object. The count of references is one for the object, as in the
135/// MicroCom runtime, whose shadow of an object holds one wrapper per
136/// interface. The object is destroyed as [`make_com`] describes.
137pub fn make_com_with<I, T, const N: usize>(value: T, more: [InterfaceEntry<T>; N]) -> ComPtr<I>
138where
139    I: ImplementedBy<T>,
140    T: 'static,
141{
142    let first = new_object(value, std::iter::once(InterfaceEntry::of::<I>()).chain(more));
143    // SAFETY: freshly created object with one reference; the first wrapper
144    // carries the vtable of `I`, and the layout contract of
145    // `Interface`/`ImplementedBy` makes it a valid `I`.
146    unsafe { ComPtr::from_raw(first as *mut I).unwrap_unchecked() }
147}
148
149/// Makes the object of `this` the outer object of an aggregate: the object
150/// whose implementation of `QueryInterface` asks `inner` for every interface
151/// it was not made with, and which releases `inner` when it is destroyed.
152///
153/// This is how a class of the Windows Runtime is derived from (composition:
154/// the factory of a composable class creates the base object as the inner
155/// object of the outer object it is given, and hands out the inner object's
156/// non-delegating `IUnknown`, whose interfaces count their references on
157/// the outer object). The MicroCom runtime has no counterpart: its objects
158/// are created by one side only. An object that is not composed answers
159/// for its own interfaces alone, as before.
160///
161/// Until this is called, the object answers for its own interfaces only,
162/// which is what the factory sees while it creates the inner object.
163///
164/// # Safety
165/// `this` must be an interface pointer of a live object made by
166/// [`make_com`] or [`make_com_with`] whose value is a `T`, and `inner` an
167/// owned reference to the non-delegating `IUnknown` of an object that
168/// aggregates it (or null). The reference is the object's from now on. An
169/// object has one inner object: an earlier one is released.
170pub unsafe fn set_inner<T>(this: *mut c_void, inner: *mut c_void) {
171    let shadow = (*(this as *const ComObject<T>)).shadow;
172    let earlier = (*shadow).inner.swap(inner, Ordering::AcqRel);
173    if !earlier.is_null() {
174        (*(earlier as *const crate::IUnknown)).release();
175    }
176}
177
178/// The three `IUnknown` slots for a [`ComObject<T>`] exposing interface `I`.
179pub struct UnknownThunks<I, T>(PhantomData<(I, T)>);
180
181impl<I: Interface, T> UnknownThunks<I, T> {
182    pub const VTBL: IUnknownVtbl = IUnknownVtbl {
183        query_interface: Self::query_interface,
184        add_ref: Self::add_ref,
185        release: Self::release,
186    };
187
188    unsafe extern "system" fn query_interface(
189        this: *mut c_void,
190        riid: *const Guid,
191        ppv: *mut *mut c_void,
192    ) -> RawHResult {
193        if ppv.is_null() || riid.is_null() {
194            return E_POINTER;
195        }
196        let shadow = (*(this as *const ComObject<T>)).shadow;
197        for wrapper in (*shadow).wrappers.iter() {
198            if (wrapper.matches_iid)(&*riid) {
199                Self::add_ref(this);
200                *ppv = &raw const wrapper.object as *mut c_void;
201                return S_OK;
202            }
203        }
204        // An interface of the inner object of a composed object: its
205        // non-delegating `QueryInterface` answers with an interface whose
206        // references are counted on this object (`set_inner`).
207        let inner = (*shadow).inner.load(Ordering::Acquire);
208        if !inner.is_null() {
209            return (*(inner as *const crate::IUnknown)).query_interface_raw(&*riid, ppv);
210        }
211        *ppv = std::ptr::null_mut();
212        E_NOINTERFACE
213    }
214
215    unsafe extern "system" fn add_ref(this: *mut c_void) -> u32 {
216        let shadow = &*(*(this as *const ComObject<T>)).shadow;
217        shadow.refs.fetch_add(1, Ordering::Relaxed) + 1
218    }
219
220    unsafe extern "system" fn release(this: *mut c_void) -> u32 {
221        let shadow = (*(this as *const ComObject<T>)).shadow as *mut Shadow<T>;
222        let left = (*shadow).refs.fetch_sub(1, Ordering::Release) - 1;
223        if left == 0 {
224            fence(Ordering::Acquire);
225            // The inner object of a composed object goes first, and may call
226            // the outer object while it goes (its interfaces count on this
227            // object): a count of one keeps those calls from destroying the
228            // object a second time.
229            let inner = (*shadow).inner.swap(std::ptr::null_mut(), Ordering::AcqRel);
230            if !inner.is_null() {
231                (*shadow).refs.store(1, Ordering::Relaxed);
232                (*(inner as *const crate::IUnknown)).release();
233            }
234            drop(Box::from_raw(shadow));
235        }
236        left
237    }
238}
239
240impl IUnknownVtbl {
241    /// Vtable prefix for a [`ComObject<T>`] whose most-derived interface is `I`.
242    pub const fn new<I: Interface, T>() -> Self {
243        UnknownThunks::<I, T>::VTBL
244    }
245}
246
247unsafe impl<T> ImplementedBy<T> for crate::IUnknown {
248    const VTBL: &'static IUnknownVtbl = &IUnknownVtbl::new::<crate::IUnknown, T>();
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254    use crate::{IUnknown, E_NOINTERFACE};
255    use std::rc::Rc;
256    use std::cell::Cell;
257
258    struct DropFlag(Rc<Cell<bool>>);
259    impl Drop for DropFlag {
260        fn drop(&mut self) {
261            self.0.set(true);
262        }
263    }
264
265    #[test]
266    fn refcount_and_query_interface() {
267        let dropped = Rc::new(Cell::new(false));
268        let p: ComPtr<IUnknown> = make_com(DropFlag(dropped.clone()));
269        let raw = p.as_ptr() as *mut c_void;
270        unsafe {
271            assert_eq!(ComObject::<DropFlag>::ref_count(raw), 1);
272            let q = p.clone();
273            assert_eq!(ComObject::<DropFlag>::ref_count(raw), 2);
274            let r = q.cast::<IUnknown>().unwrap();
275            assert_eq!(ComObject::<DropFlag>::ref_count(raw), 3);
276            drop(r);
277            drop(q);
278            assert_eq!(ComObject::<DropFlag>::ref_count(raw), 1);
279
280            let mut out = std::ptr::null_mut();
281            let bogus = Guid::from_u128(0x1234);
282            assert_eq!(p.query_interface_raw(&bogus, &mut out), E_NOINTERFACE);
283            assert!(out.is_null());
284        }
285        assert!(!dropped.get());
286        drop(p);
287        assert!(dropped.get());
288    }
289
290    // Two interfaces written by hand, as the generator writes them: a
291    // vtable that begins with the one of `IUnknown`, thunks that recover
292    // the value from `this`, and the link to the implementing type.
293    macro_rules! test_interface {
294        ($name:ident, $vtbl:ident, $imp:ident, $method:ident, $iid:expr) => {
295            #[repr(C)]
296            struct $name {
297                vtbl: *const $vtbl,
298            }
299
300            #[repr(C)]
301            struct $vtbl {
302                base: IUnknownVtbl,
303                $method: unsafe extern "system" fn(this: *mut c_void) -> u32,
304            }
305
306            trait $imp {
307                fn $method(&self) -> u32;
308            }
309
310            unsafe impl Interface for $name {
311                const IID: Guid = Guid::from_u128($iid);
312                const NAME: &'static str = stringify!($name);
313                type Vtbl = $vtbl;
314
315                fn matches_iid(iid: &Guid) -> bool {
316                    *iid == Self::IID || IUnknown::matches_iid(iid)
317                }
318            }
319
320            impl $name {
321                fn $method(&self) -> u32 {
322                    unsafe { ((*self.vtbl).$method)(self as *const _ as *mut c_void) }
323                }
324            }
325
326            unsafe impl<T: $imp> ImplementedBy<T> for $name {
327                const VTBL: &'static $vtbl = &$vtbl {
328                    base: IUnknownVtbl::new::<$name, T>(),
329                    $method: {
330                        unsafe extern "system" fn thunk<T: $imp>(this: *mut c_void) -> u32 {
331                            <T as $imp>::$method(ComObject::<T>::value(this))
332                        }
333                        thunk::<T>
334                    },
335                };
336            }
337        };
338    }
339
340    test_interface!(IFirst, IFirstVtbl, IFirstImpl, first, 0x11111111_0000_0000_0000_000000000001);
341    test_interface!(ISecond, ISecondVtbl, ISecondImpl, second, 0x22222222_0000_0000_0000_000000000002);
342    test_interface!(IThird, IThirdVtbl, IThirdImpl, third, 0x33333333_0000_0000_0000_000000000003);
343
344    struct Both(u32, #[allow(dead_code)] DropFlag);
345
346    impl IFirstImpl for Both {
347        fn first(&self) -> u32 {
348            self.0 + 1
349        }
350    }
351
352    impl ISecondImpl for Both {
353        fn second(&self) -> u32 {
354            self.0 + 2
355        }
356    }
357
358    impl IThirdImpl for Both {
359        fn third(&self) -> u32 {
360            self.0 + 3
361        }
362    }
363
364    #[test]
365    fn an_object_with_several_interfaces_answers_for_each_of_them() {
366        let dropped = Rc::new(Cell::new(false));
367        let first: ComPtr<IFirst> =
368            make_com_with(Both(40, DropFlag(dropped.clone())), [InterfaceEntry::of::<ISecond>(), InterfaceEntry::of::<IThird>()]);
369        let raw = first.as_ptr() as *mut c_void;
370        assert_eq!(41, first.first());
371
372        // Every interface is reached from every other, and each call lands
373        // in the one value.
374        let second = first.cast::<ISecond>().unwrap();
375        assert_eq!(42, second.second());
376        let third = second.cast::<IThird>().unwrap();
377        assert_eq!(43, third.third());
378        let back = third.cast::<IFirst>().unwrap();
379        assert_eq!(41, back.first());
380        assert_eq!(back.as_ptr(), first.as_ptr());
381        assert_ne!(second.as_ptr() as *mut c_void, raw);
382        assert_ne!(third.as_ptr() as *mut c_void, second.as_ptr() as *mut c_void);
383
384        // One count for the object, whichever interface is held.
385        unsafe {
386            assert_eq!(4, ComObject::<Both>::ref_count(raw));
387            assert_eq!(4, ComObject::<Both>::ref_count(third.as_ptr() as *mut c_void));
388        }
389
390        // The identity of the object: `IUnknown` is the same pointer from
391        // every interface.
392        let unknowns = [
393            first.cast::<IUnknown>().unwrap(),
394            second.cast::<IUnknown>().unwrap(),
395            third.cast::<IUnknown>().unwrap(),
396        ];
397        assert!(unknowns.iter().all(|unknown| unknown.as_ptr() as *mut c_void == raw));
398        drop(unknowns);
399
400        // An interface the object was not made with.
401        let mut out = std::ptr::null_mut();
402        unsafe {
403            assert_eq!(second.as_unknown().query_interface_raw(&Guid::from_u128(0x1234), &mut out), E_NOINTERFACE);
404        }
405        assert!(out.is_null());
406
407        // The value lives while any interface is held, and is dropped with
408        // the last, whichever that is.
409        drop(first);
410        drop(back);
411        drop(third);
412        assert!(!dropped.get());
413        assert_eq!(42, second.second());
414        drop(second);
415        assert!(dropped.get());
416    }
417
418    #[test]
419    fn an_object_with_one_interface_does_not_answer_for_another() {
420        let dropped = Rc::new(Cell::new(false));
421        let first: ComPtr<IFirst> = make_com(Both(1, DropFlag(dropped.clone())));
422        assert_eq!(2, first.first());
423        assert_eq!(Err(crate::HResult(E_NOINTERFACE)), first.cast::<ISecond>().map(|_| ()));
424        let unknown = first.cast::<IUnknown>().unwrap();
425        assert_eq!(unknown.as_ptr() as *mut c_void, first.as_ptr() as *mut c_void);
426        drop(first);
427        assert!(!dropped.get());
428        drop(unknown);
429        assert!(dropped.get());
430    }
431
432    struct Inner(u32, #[allow(dead_code)] DropFlag);
433
434    impl ISecondImpl for Inner {
435        fn second(&self) -> u32 {
436            self.0
437        }
438    }
439
440    #[test]
441    fn a_composed_object_asks_its_inner_object_and_releases_it() {
442        let outer_dropped = Rc::new(Cell::new(false));
443        let inner_dropped = Rc::new(Cell::new(false));
444        let outer: ComPtr<IFirst> = make_com(Both(1, DropFlag(outer_dropped.clone())));
445        // Not composed yet: an interface the object was not made with.
446        assert_eq!(Err(crate::HResult(E_NOINTERFACE)), outer.cast::<ISecond>().map(|_| ()));
447
448        let inner: ComPtr<ISecond> = make_com(Inner(7, DropFlag(inner_dropped.clone())));
449        unsafe { set_inner::<Both>(outer.as_ptr() as *mut c_void, inner.into_raw() as *mut c_void) };
450
451        // Its own interfaces first, then the inner object's.
452        assert_eq!(2, outer.first());
453        let second = outer.cast::<ISecond>().unwrap();
454        assert_eq!(7, second.second());
455        assert_eq!(Err(crate::HResult(E_NOINTERFACE)), outer.cast::<IThird>().map(|_| ()));
456        drop(second);
457
458        // The inner object goes with the outer one.
459        drop(outer);
460        assert!(outer_dropped.get());
461        assert!(inner_dropped.get());
462    }
463
464    #[test]
465    fn guid_layout_and_parse() {
466        assert_eq!(std::mem::size_of::<Guid>(), 16);
467        let g = Guid::parse("809c652e-7396-11d2-9771-00a0c9b4d50c").unwrap();
468        assert_eq!(g, Guid::from_u128(0x809c652e_7396_11d2_9771_00a0c9b4d50c));
469        assert_eq!(g.to_string(), "809c652e-7396-11d2-9771-00a0c9b4d50c");
470        assert_eq!(g.as_com_h_materialized().data4, [0x97, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71]);
471    }
472}