com_scrape_types/
ptr.rs

1use std::marker::PhantomData;
2use std::mem;
3use std::ptr::NonNull;
4
5use super::{Inherits, Interface};
6
7/// Trait for types that represent a smart pointer to a COM object.
8///
9/// Implemented by [`ComPtr`] and [`ComRef`].
10///
11/// The purpose of this trait is to enable blanket implementations of interface traits for all
12/// smart pointers with a compatible target for that interface. For each interface `IInterface`,
13/// `com-scrape` will generate a blanket implementation of `IInterfaceTrait` as follows:
14///
15/// ```ignore
16/// impl<P> IInterfaceTrait for P
17/// where
18///     P: SmartPtr,
19///     P::Target: Inherits<IInterface>,
20/// {
21///     /* ... */
22/// }
23/// ```
24///
25/// This makes it possible to call the methods of `IInterface` directly on a `ComPtr<IInterface>`,
26/// or on any `ComPtr<IOtherInterface>` where `IOtherInterface` is derived from from `IInterface`.
27pub trait SmartPtr {
28    /// The interface type pointed to by this smart pointer.
29    type Target;
30
31    /// Gets the raw pointer held by this smart pointer.
32    fn ptr(&self) -> *mut Self::Target;
33}
34
35/// A non-owning smart pointer to a COM object.
36///
37/// A `ComRef<'a, I>` represents a borrowed reference to a COM object implementing interface `I`.
38/// Like [`ComPtr`], `ComRef` can be used to call interface methods on the referenced object.
39/// Unlike [`ComPtr`], `ComRef` does not manage the object's reference count, i.e. it will *not*
40/// call the release method of the object it points to when going out of scope. See the
41/// [crate-level documentation](crate#reference-counting) for more information.
42///
43/// A `ComRef` can be created safely from a [`ComPtr`] via [`ComPtr::as_com_ref`], or from a
44/// [`ComWrapper`][crate::ComWrapper] via [`ComWrapper::as_com_ref`][crate::ComWrapper::as_com_ref].
45/// It can also be created unsafely via [`ComRef::from_raw`].
46pub struct ComRef<'a, I: Interface> {
47    ptr: NonNull<I>,
48    _marker: PhantomData<&'a I>,
49}
50
51impl<'a, I: Interface> SmartPtr for ComRef<'a, I> {
52    type Target = I;
53
54    #[inline]
55    fn ptr(&self) -> *mut I {
56        self.ptr.as_ptr()
57    }
58}
59
60impl<'a, I: Interface> Copy for ComRef<'a, I> {}
61
62impl<'a, I: Interface> Clone for ComRef<'a, I> {
63    #[inline]
64    fn clone(&self) -> ComRef<'a, I> {
65        ComRef {
66            ptr: self.ptr,
67            _marker: PhantomData,
68        }
69    }
70}
71
72unsafe impl<'a, I: Interface> Send for ComRef<'a, I> where I: Sync + Send {}
73
74unsafe impl<'a, I: Interface> Sync for ComRef<'a, I> where I: Sync + Send {}
75
76impl<'a, I: Interface> ComRef<'a, I> {
77    /// Gets the wrapped interface pointer.
78    ///
79    /// Does not perform any reference counting operations.
80    #[inline]
81    pub fn as_ptr(&self) -> *mut I {
82        self.ptr.as_ptr() as *mut I
83    }
84
85    /// Creates a `ComRef` from a raw interface pointer if the pointer is non-null.
86    ///
87    /// Does not perform any reference counting operations.
88    ///
89    /// `from_raw` will check if `ptr` is null (and return `None` if so), but this method is still
90    /// unsafe, as the caller must still ensure that `ptr` is a valid interface pointer (see below)
91    /// if it is non-null.
92    ///
93    /// # Safety
94    ///
95    /// If `ptr` is non-null, it must be a valid pointer to a value of type `I`, and if `*ptr` is
96    /// reinterpreted as `*const I::Vtbl` (see the [safety documentation](Interface#safety) for
97    /// [`Interface`]), it must in turn be a valid pointer to `I::Vtbl`.
98    #[inline]
99    pub unsafe fn from_raw(ptr: *mut I) -> Option<ComRef<'a, I>> {
100        NonNull::new(ptr).map(|ptr| ComRef {
101            ptr,
102            _marker: PhantomData,
103        })
104    }
105
106    /// Creates a `ComRef` from a raw interface pointer.
107    ///
108    /// Does not perform any reference counting operations.
109    ///
110    /// # Safety
111    ///
112    /// `ptr` must be a valid pointer to a value of type `I`, and if `*ptr` is reinterpreted as
113    /// `*const I::Vtbl` (see the [safety documentation](Interface#safety) for [`Interface`]), it
114    /// must in turn be a valid pointer to `I::Vtbl`.
115    #[inline]
116    pub unsafe fn from_raw_unchecked(ptr: *mut I) -> ComRef<'a, I> {
117        ComRef {
118            ptr: NonNull::new_unchecked(ptr),
119            _marker: PhantomData,
120        }
121    }
122
123    /// Upgrades the `ComRef` to a [`ComPtr`].
124    ///
125    /// Increments the reference count of the object that the `ComRef` points to.
126    #[inline]
127    pub fn to_com_ptr(&self) -> ComPtr<I> {
128        unsafe {
129            let ptr = self.ptr.as_ptr();
130
131            I::add_ref(ptr);
132
133            ComPtr::from_raw_unchecked(ptr)
134        }
135    }
136
137    /// Casts the `ComRef` from a derived interface to a base interface.
138    ///
139    /// Does not perform any reference counting operations.
140    #[inline]
141    pub fn upcast<J: Interface>(self) -> ComRef<'a, J>
142    where
143        I: Inherits<J>,
144    {
145        unsafe { ComRef::from_raw_unchecked(self.as_ptr() as *mut J) }
146    }
147
148    /// Attempts to cast from one interface to another, returning a [`ComPtr`] if successful.
149    ///
150    /// If the cast is successful, increments the reference count of the object that the `ComRef`
151    /// points to.
152    #[inline]
153    pub fn cast<J: Interface>(&self) -> Option<ComPtr<J>> {
154        unsafe {
155            if let Some(ptr) = I::query_interface(self.ptr.as_ptr(), &J::IID) {
156                ComPtr::from_raw(ptr as *mut J)
157            } else {
158                None
159            }
160        }
161    }
162}
163
164/// An owning smart pointer to a COM object.
165///
166/// A `ComPtr<I>` represents an owning reference to a COM object implementing interface `I`. Like
167/// [`ComRef`], `ComPtr` can be used to call interface methods on the referenced object. Unlike
168/// [`ComRef`], `ComPtr` manages the object's reference count, i.e. it *will* call the release
169/// method of the object it points to when going out of scope. See the
170/// [crate-level documentation](crate#reference-counting) for more information.
171///
172/// A `ComPtr` can be created safely from a [`ComRef`] via [`ComRef::to_com_ptr`], or from a
173/// [`ComWrapper`][crate::ComWrapper] via [`ComWrapper::to_com_ptr`][crate::ComWrapper::to_com_ptr].
174/// It can also be created unsafely via [`ComPtr::from_raw`].
175pub struct ComPtr<I: Interface> {
176    ptr: NonNull<I>,
177}
178
179impl<I: Interface> SmartPtr for ComPtr<I> {
180    type Target = I;
181
182    #[inline]
183    fn ptr(&self) -> *mut I {
184        self.ptr.as_ptr()
185    }
186}
187
188impl<I: Interface> Clone for ComPtr<I> {
189    #[inline]
190    fn clone(&self) -> ComPtr<I> {
191        unsafe {
192            I::add_ref(self.ptr.as_ptr());
193        }
194
195        ComPtr { ptr: self.ptr }
196    }
197}
198
199unsafe impl<I: Interface> Send for ComPtr<I> where I: Sync + Send {}
200
201unsafe impl<I: Interface> Sync for ComPtr<I> where I: Sync + Send {}
202
203impl<I: Interface> Drop for ComPtr<I> {
204    #[inline]
205    fn drop(&mut self) {
206        unsafe {
207            I::release(self.ptr.as_ptr());
208        }
209    }
210}
211
212impl<I: Interface> ComPtr<I> {
213    /// Gets the wrapped interface pointer.
214    ///
215    /// Does not perform any reference counting operations.
216    #[inline]
217    pub fn as_ptr(&self) -> *mut I {
218        self.ptr.as_ptr() as *mut I
219    }
220
221    /// Creates a `ComPtr` from a raw interface pointer if the pointer is non-null.
222    ///
223    /// When the resulting `ComPtr` is dropped, the reference count of the object it points to will
224    /// be decremented. Thus, using this method can be thought of as "taking ownership" of a
225    /// pointer to a COM object.
226    ///
227    /// `from_raw` will check if `ptr` is null (and return `None` if so), but this method is still
228    /// unsafe, as the caller must still ensure that `ptr` is a valid interface pointer (see below)
229    /// if it is non-null.
230    ///
231    /// # Safety
232    ///
233    /// If `ptr` is non-null, it must be a valid pointer to a value of type `I`, and if `*ptr` is
234    /// reinterpreted as `*const I::Vtbl` (see the [safety documentation](Interface#safety) for
235    /// [`Interface`]), it must in turn be a valid pointer to `I::Vtbl`.
236    #[inline]
237    pub unsafe fn from_raw(ptr: *mut I) -> Option<ComPtr<I>> {
238        NonNull::new(ptr).map(|ptr| ComPtr { ptr })
239    }
240
241    /// Creates a `ComPtr` from a raw interface pointer.
242    ///
243    /// When the resulting `ComPtr` is dropped, the reference count of the object it points to will
244    /// be decremented. Thus, using this method can be thought of as "taking ownership" of a
245    /// pointer to a COM object.
246    ///
247    /// # Safety
248    ///
249    /// `ptr` must be a valid pointer to a value of type `I`, and if `*ptr` is reinterpreted as
250    /// `*const I::Vtbl` (see the [safety documentation](Interface#safety) for [`Interface`]), it
251    /// must in turn be a valid pointer to `I::Vtbl`.
252    #[inline]
253    pub unsafe fn from_raw_unchecked(ptr: *mut I) -> ComPtr<I> {
254        ComPtr {
255            ptr: NonNull::new_unchecked(ptr),
256        }
257    }
258
259    /// Consumes the `ComPtr`, returning the wrapped interface pointer.
260    ///
261    /// Since this method consumes the `ComPtr`, it prevents the `ComPtr` from decrementing the
262    /// reference count of the object it points to. Thus, using this method can be thought of as
263    /// "relinquishing ownership" of a pointer to a COM object.
264    #[inline]
265    pub fn into_raw(self) -> *mut I {
266        let ptr = self.ptr.as_ptr();
267        mem::forget(self);
268        ptr
269    }
270
271    /// Returns a [`ComRef`] pointing to the same object as this `ComPtr`. Can be thought of as a
272    /// borrow.
273    ///
274    /// Does not perform any reference counting operations.
275    #[inline]
276    pub fn as_com_ref<'a>(&'a self) -> ComRef<'a, I> {
277        unsafe { ComRef::from_raw_unchecked(self.ptr.as_ptr()) }
278    }
279
280    /// Does not perform any reference counting operations.
281    #[inline]
282    pub fn upcast<J: Interface>(self) -> ComPtr<J>
283    where
284        I: Inherits<J>,
285    {
286        unsafe { ComPtr::from_raw_unchecked(self.into_raw() as *mut J) }
287    }
288
289    /// Attempts to cast from one interface to another, returning another [`ComPtr`] if successful.
290    ///
291    /// If the cast is successful, increments the reference count of the object that the `ComPtr`
292    /// points to.
293    #[inline]
294    pub fn cast<J: Interface>(&self) -> Option<ComPtr<J>> {
295        unsafe {
296            if let Some(ptr) = I::query_interface(self.ptr.as_ptr(), &J::IID) {
297                ComPtr::from_raw(ptr as *mut J)
298            } else {
299                None
300            }
301        }
302    }
303}