]>
Commit | Line | Data |
---|---|---|
353b0b11 FG |
1 | use super::*; |
2 | ||
3 | /// All COM interfaces (and thus WinRT classes and interfaces) implement | |
4 | /// [IUnknown](https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown) | |
5 | /// under the hood to provide reference-counted lifetime management as well as the ability | |
6 | /// to query for additional interfaces that the object may implement. | |
7 | #[repr(transparent)] | |
8 | pub struct IUnknown(std::ptr::NonNull<std::ffi::c_void>); | |
9 | ||
10 | #[doc(hidden)] | |
11 | #[repr(C)] | |
12 | pub struct IUnknown_Vtbl { | |
13 | pub QueryInterface: unsafe extern "system" fn(this: *mut std::ffi::c_void, iid: &GUID, interface: *mut *const std::ffi::c_void) -> HRESULT, | |
14 | pub AddRef: unsafe extern "system" fn(this: *mut std::ffi::c_void) -> u32, | |
15 | pub Release: unsafe extern "system" fn(this: *mut std::ffi::c_void) -> u32, | |
16 | } | |
17 | ||
18 | unsafe impl Interface for IUnknown { | |
19 | type Vtable = IUnknown_Vtbl; | |
20 | } | |
21 | ||
22 | unsafe impl ComInterface for IUnknown { | |
23 | const IID: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046); | |
24 | } | |
25 | ||
26 | impl Clone for IUnknown { | |
27 | fn clone(&self) -> Self { | |
28 | unsafe { | |
29 | (self.vtable().AddRef)(std::mem::transmute_copy(self)); | |
30 | } | |
31 | ||
32 | Self(self.0) | |
33 | } | |
34 | } | |
35 | ||
36 | impl Drop for IUnknown { | |
37 | fn drop(&mut self) { | |
38 | unsafe { | |
39 | (self.vtable().Release)(std::mem::transmute_copy(self)); | |
40 | } | |
41 | } | |
42 | } | |
43 | ||
44 | impl PartialEq for IUnknown { | |
45 | fn eq(&self, other: &Self) -> bool { | |
46 | // Since COM objects may implement multiple interfaces, COM identity can only | |
47 | // be determined by querying for `IUnknown` explicitly and then comparing the | |
48 | // pointer values. This works since `QueryInterface` is required to return | |
49 | // the same pointer value for queries for `IUnknown`. | |
50 | self.cast::<IUnknown>().unwrap().0 == other.cast::<IUnknown>().unwrap().0 | |
51 | } | |
52 | } | |
53 | ||
54 | impl Eq for IUnknown {} | |
55 | ||
56 | impl std::fmt::Debug for IUnknown { | |
57 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
58 | f.debug_tuple("IUnknown").field(&self.0).finish() | |
59 | } | |
60 | } | |
61 | ||
62 | #[doc(hidden)] | |
63 | pub trait IUnknownImpl { | |
64 | type Impl; | |
65 | /// Get a reference to the backing implementation. | |
66 | fn get_impl(&self) -> &Self::Impl; | |
67 | ||
68 | /// The classic `QueryInterface` method from COM. | |
69 | /// | |
70 | /// # Safety | |
71 | /// | |
72 | /// This function is safe to call as long as the interface pointer is non-null and valid for writes | |
73 | /// of an interface pointer. | |
74 | unsafe fn QueryInterface(&self, iid: &GUID, interface: *mut *const std::ffi::c_void) -> HRESULT; | |
75 | /// Increments the reference count of the interface | |
76 | fn AddRef(&self) -> u32; | |
77 | /// Decrements the reference count causing the interface's memory to be freed when the count is 0 | |
78 | /// | |
79 | /// # Safety | |
80 | /// | |
81 | /// This function should only be called when the interfacer pointer is no longer used as calling `Release` | |
82 | /// on a non-aliased interface pointer and then using that interface pointer may result in use after free. | |
83 | unsafe fn Release(&self) -> u32; | |
84 | } | |
85 | ||
86 | #[cfg(feature = "implement")] | |
87 | impl IUnknown_Vtbl { | |
88 | pub const fn new<T: IUnknownImpl, const OFFSET: isize>() -> Self { | |
89 | unsafe extern "system" fn QueryInterface<T: IUnknownImpl, const OFFSET: isize>(this: *mut std::ffi::c_void, iid: &GUID, interface: *mut *const std::ffi::c_void) -> HRESULT { | |
90 | let this = (this as *mut *mut std::ffi::c_void).offset(OFFSET) as *mut T; | |
91 | (*this).QueryInterface(iid, interface) | |
92 | } | |
93 | unsafe extern "system" fn AddRef<T: IUnknownImpl, const OFFSET: isize>(this: *mut std::ffi::c_void) -> u32 { | |
94 | let this = (this as *mut *mut std::ffi::c_void).offset(OFFSET) as *mut T; | |
95 | (*this).AddRef() | |
96 | } | |
97 | unsafe extern "system" fn Release<T: IUnknownImpl, const OFFSET: isize>(this: *mut std::ffi::c_void) -> u32 { | |
98 | let this = (this as *mut *mut std::ffi::c_void).offset(OFFSET) as *mut T; | |
99 | (*this).Release() | |
100 | } | |
101 | Self { QueryInterface: QueryInterface::<T, OFFSET>, AddRef: AddRef::<T, OFFSET>, Release: Release::<T, OFFSET> } | |
102 | } | |
103 | } |