1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#![allow(non_camel_case_types, non_snake_case)]

use crate::co;
use crate::decl::*;
use crate::ole::{privs::*, vts::*};

com_interface! { IUnknown: "00000000-0000-0000-c000-000000000046";
	/// [`IUnknown`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown)
	/// COM interface. It's the base to all COM interfaces.
	///
	/// The `clone` method calls
	/// [`AddRef`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-addref)
	/// internally.
	///
	/// Automatically calls
	/// [`Release`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release)
	/// when the object goes out of scope.
}

/// This trait is enabled with the `ole` feature, and provides methods for
/// [`IUnknown`](crate::IUnknown). It is the base trait for all COM traits.
///
/// Prefer importing this trait through the prelude:
///
/// ```no_run
/// use winsafe::prelude::*;
/// ```
///
/// Note that the `IUnknown` virtual table has two other methods: `AddRef` and
/// `Release`. While these methods are relevant in C++, here they are abstracted
/// away as it follows:
///
/// * `AddRef` – called along the `clone` method from the
/// [`Clone`](std::clone::Clone) trait;
///
/// * `Release` – called automatically by the [`Drop`](std::ops::Drop) trait, so
/// you don't need to worry about it.
pub trait ole_IUnknown: Clone {
	/// The COM interface ID.
	const IID: co::IID;

	/// Creates an object from a COM virtual table pointer.
	///
	/// This method can be used as an escape hatch to interoperate with other
	/// libraries.
	///
	/// # Panics
	///
	/// Panics if trying to create a custom COM implementation object.
	///
	/// # Safety
	///
	/// Be sure the pointer points to a properly allocated COM virtual table.
	#[must_use]
	unsafe fn from_ptr(p: *mut std::ffi::c_void) -> Self;

	/// Returns the pointer to the underlying COM virtual table.
	///
	/// This method can be used as an escape hatch to interoperate with other
	/// libraries.
	#[must_use]
	fn ptr(&self) -> *mut std::ffi::c_void;

	/// Returns a mutable reference do the underlying COM virtual table pointer.
	///
	/// This method can be used as an escape hatch to interoperate with other
	/// libraries.
	///
	/// # Panics
	///
	/// Panics if trying to modify a custom COM implementation object.
	///
	/// # Safety
	///
	/// Be sure the pointer being set points to a properly allocated COM virtual
	/// table.
	#[must_use]
	unsafe fn as_mut(&mut self) -> &mut *mut std::ffi::c_void;

	/// Creates an object from a null COM virtual table pointer.
	///
	/// # Safety
	///
	/// The pointer must be initialized before any call. Do not call methods on
	/// a null COM pointer.
	#[must_use]
	unsafe fn null() -> Self {
		Self::from_ptr(std::ptr::null_mut())
	}

	/// Returns the pointer to the underlying COM virtual table and sets the
	/// internal pointer to null, so that
	/// [`Release`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-release)
	/// won't be called.
	///
	/// Be sure to release the pointer, otherwise, as the name of this method
	/// implies, you will cause a resource leak.
	#[must_use]
	fn leak(&mut self) -> *mut std::ffi::c_void {
		let p = self.ptr();
		unsafe { *self.as_mut() = std::ptr::null_mut(); }
		p
	}

	/// [`IUnknown::QueryInterface`](https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void))
	/// method.
	#[must_use]
	fn QueryInterface<T>(&self) -> HrResult<T>
		where T: ole_IUnknown,
	{
		let mut queried = unsafe { T::null() };
		ok_to_hrresult(
			unsafe {
				(vt::<IUnknownVT>(self).QueryInterface)(
					self.ptr(),
					&T::IID as *const _ as _,
					queried.as_mut(),
				)
			},
		).map(|_| queried)
	}
}