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
7pub unsafe trait ImplementedBy<T>: Interface {
17 const VTBL: &'static Self::Vtbl;
18}
19
20#[repr(C)]
30pub struct ComObject<T> {
31 vtbl: *const c_void,
32 shadow: *const Shadow<T>,
33}
34
35pub 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 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
50struct 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 #[inline]
72 pub unsafe fn value<'a>(this: *mut c_void) -> &'a T {
73 &(*(*(this as *const ComObject<T>)).shadow).value
74 }
75
76 pub unsafe fn ref_count(this: *mut c_void) -> u32 {
81 (*(*(this as *const ComObject<T>)).shadow).refs.load(Ordering::Relaxed)
82 }
83}
84
85fn 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 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
111pub 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
126pub 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 unsafe { ComPtr::from_raw(first as *mut I).unwrap_unchecked() }
147}
148
149pub 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
178pub 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 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 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 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 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 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 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 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 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 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 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 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 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}