Skip to main content

ferroui_microcom/
com_ptr.rs

1use std::ffi::c_void;
2use std::fmt;
3use std::marker::PhantomData;
4use std::ops::Deref;
5use std::ptr::NonNull;
6
7use crate::{HResult, Interface, E_NOINTERFACE, S_OK};
8
9/// Owning, non-null COM interface pointer: holds exactly one reference.
10///
11/// Not `Send`/`Sync`: the native objects are UI-thread affine.
12#[repr(transparent)]
13pub struct ComPtr<T: Interface> {
14    ptr: NonNull<T>,
15    _marker: PhantomData<*mut T>,
16}
17
18impl<T: Interface> ComPtr<T> {
19    /// Takes ownership of one existing reference (no `AddRef`). Returns
20    /// `None` for null.
21    ///
22    /// # Safety
23    /// `raw` must be null or a valid pointer to a COM object implementing `T`
24    /// whose reference the caller owns.
25    #[inline]
26    pub unsafe fn from_raw(raw: *mut T) -> Option<Self> {
27        NonNull::new(raw).map(|ptr| ComPtr { ptr, _marker: PhantomData })
28    }
29
30    /// `AddRef`s a borrowed pointer and wraps it. Returns `None` for null.
31    ///
32    /// # Safety
33    /// `raw` must be null or a valid pointer to a COM object implementing `T`.
34    #[inline]
35    pub unsafe fn from_raw_add_ref(raw: *mut T) -> Option<Self> {
36        let p = Self::from_raw(raw)?;
37        p.as_unknown().add_ref();
38        Some(p)
39    }
40
41    /// `AddRef`s a borrowed interface reference.
42    #[inline]
43    pub fn from_ref(r: &T) -> Self {
44        // SAFETY: `&T` always refers to a live COM object.
45        unsafe { Self::from_raw_add_ref(r.as_raw()).unwrap_unchecked() }
46    }
47
48    /// Borrowed raw pointer; the reference count is unchanged.
49    #[inline]
50    pub fn as_ptr(&self) -> *mut T {
51        self.ptr.as_ptr()
52    }
53
54    /// Gives up ownership without releasing.
55    #[inline]
56    pub fn into_raw(self) -> *mut T {
57        let p = self.ptr.as_ptr();
58        std::mem::forget(self);
59        p
60    }
61
62    /// `QueryInterface` for `U`.
63    ///
64    /// The native library compares against the IIDs materialized by `com.h`
65    /// (see [`crate::Guid::as_com_h_materialized`]), Rust-implemented objects
66    /// accept both forms; so the materialized form is tried first and the IDL
67    /// form second.
68    pub fn cast<U: Interface>(&self) -> Result<ComPtr<U>, HResult> {
69        let mut last = E_NOINTERFACE;
70        for iid in [U::IID.as_com_h_materialized(), U::IID] {
71            let mut out: *mut c_void = std::ptr::null_mut();
72            // SAFETY: valid object, valid out pointer.
73            let hr = unsafe { self.as_unknown().query_interface_raw(&iid, &mut out) };
74            if hr == S_OK {
75                // SAFETY: QueryInterface returned an owned reference to `U`.
76                return unsafe { ComPtr::from_raw(out as *mut U) }.ok_or(HResult::POINTER);
77            }
78            last = hr;
79        }
80        Err(HResult(last))
81    }
82}
83
84impl<T: Interface> Deref for ComPtr<T> {
85    type Target = T;
86    #[inline]
87    fn deref(&self) -> &T {
88        // SAFETY: we hold a reference, so the object is alive.
89        unsafe { self.ptr.as_ref() }
90    }
91}
92
93impl<T: Interface> Clone for ComPtr<T> {
94    #[inline]
95    fn clone(&self) -> Self {
96        // SAFETY: the object is alive.
97        unsafe { self.as_unknown().add_ref() };
98        ComPtr { ptr: self.ptr, _marker: PhantomData }
99    }
100}
101
102impl<T: Interface> Drop for ComPtr<T> {
103    #[inline]
104    fn drop(&mut self) {
105        // SAFETY: we own one reference.
106        unsafe { self.as_unknown().release() };
107    }
108}
109
110impl<T: Interface> PartialEq for ComPtr<T> {
111    fn eq(&self, other: &Self) -> bool {
112        self.ptr == other.ptr
113    }
114}
115
116impl<T: Interface> fmt::Debug for ComPtr<T> {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "ComPtr<{}>({:p})", T::NAME, self.ptr.as_ptr())
119    }
120}