1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::sync::atomic::AtomicU32;
6 use std::sync::atomic::Ordering::SeqCst;
7
8 use base::info;
9 use base::Event;
10 use libc::c_void;
11 use winapi::shared::guiddef::IsEqualGUID;
12 use winapi::shared::guiddef::REFIID;
13 use winapi::shared::minwindef::ULONG;
14 use winapi::shared::winerror::E_INVALIDARG;
15 use winapi::shared::winerror::E_NOINTERFACE;
16 use winapi::shared::winerror::NOERROR;
17 use winapi::shared::winerror::S_OK;
18 use winapi::um::mmdeviceapi::*;
19 use winapi::um::objidlbase::IAgileObject;
20 use winapi::um::unknwnbase::IUnknown;
21 use winapi::um::unknwnbase::IUnknownVtbl;
22 use winapi::um::winnt::HRESULT;
23 use winapi::Interface;
24 use wio::com::ComPtr;
25
26 /// This struct is used to create the completion handler `IActivateAudioInterfaceCompletionHandler`
27 /// that is passed into `ActivateAudioInterfaceAsync`. In other words, the first field in the struct
28 /// must be `IActivateAudioInterfaceCompletionHandlerVtbl`.
29 ///
30 /// This struct matches the `IActivateAudioInterfaceCompletionHandler` struct with the addition of
31 /// the `ref_count` below `lp_vtbl` which is used to keep a reference count to the completion
32 /// handler.
33 #[repr(C)]
34 pub struct WinAudioActivateAudioInterfaceCompletionHandler {
35 pub lp_vtbl: &'static IActivateAudioInterfaceCompletionHandlerVtbl,
36 ref_count: AtomicU32,
37 activate_audio_interface_complete_event: Event,
38 }
39
40 impl WinAudioActivateAudioInterfaceCompletionHandler {
41 /// The ComPtr is a `WinAudioActivateAudioInterfaceCompletionHandler` casted as an
42 /// `IActivateAudioInterfaceCompletionHandler`.
create_com_ptr( activate_audio_interface_complete_event: Event, ) -> ComPtr<IActivateAudioInterfaceCompletionHandler>43 pub fn create_com_ptr(
44 activate_audio_interface_complete_event: Event,
45 ) -> ComPtr<IActivateAudioInterfaceCompletionHandler> {
46 let win_completion_handler = Box::new(WinAudioActivateAudioInterfaceCompletionHandler {
47 lp_vtbl: IWIN_AUDIO_COMPLETION_HANDLER_VTBL,
48 ref_count: AtomicU32::new(1),
49 activate_audio_interface_complete_event,
50 });
51
52 // This is safe if the value passed into `from_raw` is structured in a way where it can
53 // match `IActivateAudioInterfaceCompletionHandler`.
54 // Since `win_completion_handler.cast_to_com_ptr()` does, this is safe.
55 // Safe because we are passing in a valid COM object that implements `IUnknown` into
56 // `from_raw`.
57 unsafe {
58 ComPtr::from_raw(Box::into_raw(win_completion_handler)
59 as *mut IActivateAudioInterfaceCompletionHandler)
60 }
61 }
62
63 /// Unsafe if `thing` cannot because casted to
64 /// `WinAudioActivateAudioInterfaceCompletionHandler`. This is safe because `thing` is
65 /// originally a `WinAudioActivateAudioInterfaceCompletionHandler.
increment_counter(&self) -> ULONG66 unsafe fn increment_counter(&self) -> ULONG {
67 self.ref_count.fetch_add(1, SeqCst) + 1
68 }
69
decrement_counter(&mut self) -> ULONG70 fn decrement_counter(&mut self) -> ULONG {
71 let old_val = self.ref_count.fetch_sub(1, SeqCst);
72 if old_val == 0 {
73 panic!("Attempted to decrement WinAudioActivateInterfaceCompletionHandler ref count when it is already 0.");
74 }
75 old_val - 1
76 }
77
activate_completed(&self)78 fn activate_completed(&self) {
79 info!("Activate Completed handler called from ActiviateAudioInterfaceAsync.");
80 self.activate_audio_interface_complete_event
81 .signal()
82 .expect("Failed to notify audioclientevent");
83 }
84 }
85
86 impl Drop for WinAudioActivateAudioInterfaceCompletionHandler {
drop(&mut self)87 fn drop(&mut self) {
88 info!("IActivateAudioInterfaceCompletionHandler is dropped.");
89 }
90 }
91
92 /// This is the callback when `ActivateAudioInterfaceAsync` is completed. When this is callback is
93 /// triggered, the IAudioClient will be available.
94 /// More info: https://docs.microsoft.com/en-us/windows/win32/api/mmdeviceapi/nf-mmdeviceapi-iactivateaudiointerfacecompletionhandler-activatecompleted
95 ///
96 /// Safe because we are certain that `completion_handler` can be casted to
97 /// `WinAudioActivateAudioInterfaceHandler`, since that is its original type during construction.
activate_completed( completion_handler: *mut IActivateAudioInterfaceCompletionHandler, _activate_operation: *mut IActivateAudioInterfaceAsyncOperation, ) -> HRESULT98 unsafe extern "system" fn activate_completed(
99 completion_handler: *mut IActivateAudioInterfaceCompletionHandler,
100 _activate_operation: *mut IActivateAudioInterfaceAsyncOperation,
101 ) -> HRESULT {
102 let win_audio_activate_interface =
103 completion_handler as *mut WinAudioActivateAudioInterfaceCompletionHandler;
104 (*win_audio_activate_interface).activate_completed();
105
106 S_OK
107 }
108
109 const IWIN_AUDIO_COMPLETION_HANDLER_VTBL: &IActivateAudioInterfaceCompletionHandlerVtbl =
110 // Implementation based on
111 // https://docs.microsoft.com/en-us/office/client-developer/outlook/mapi/implementing-iunknown-in-c-plus-plus
112 &IActivateAudioInterfaceCompletionHandlerVtbl {
113 parent: IUnknownVtbl {
114 QueryInterface: {
115 /// Safe because if `this` is not implemented (fails the RIID check) this function
116 /// will just return. If it valid, it should be able to safely increment the ref
117 /// counter and set the pointer `ppv_object`.
query_interface( this: *mut IUnknown, riid: REFIID, ppv_object: *mut *mut c_void, ) -> HRESULT118 unsafe extern "system" fn query_interface(
119 this: *mut IUnknown,
120 riid: REFIID,
121 ppv_object: *mut *mut c_void,
122 ) -> HRESULT {
123 if ppv_object.is_null() {
124 return E_INVALIDARG;
125 }
126
127 *ppv_object = std::ptr::null_mut();
128
129 // Check for valid RIID's
130 if IsEqualGUID(&*riid, &IUnknown::uuidof())
131 || IsEqualGUID(
132 &*riid,
133 &IActivateAudioInterfaceCompletionHandler::uuidof(),
134 )
135 || IsEqualGUID(&*riid, &IAgileObject::uuidof())
136 {
137 *ppv_object = this as *mut c_void;
138 (*this).AddRef();
139 return NOERROR;
140 }
141 return E_NOINTERFACE;
142 }
143 query_interface
144 },
145 AddRef: {
146 /// Unsafe if `this` cannot because casted to
147 /// `WinAudioActivateAudioInterfaceCompletionHandler`.
148 ///
149 /// This is safe because `this` is
150 /// originally a `WinAudioActivateAudioInterfaceCompletionHandler.
add_ref(this: *mut IUnknown) -> ULONG151 unsafe extern "system" fn add_ref(this: *mut IUnknown) -> ULONG {
152 info!("Adding ref in IActivateAudioInterfaceCompletionHandler.");
153 let win_audio_completion_handler =
154 this as *mut WinAudioActivateAudioInterfaceCompletionHandler;
155 (*win_audio_completion_handler).increment_counter()
156 }
157 add_ref
158 },
159 Release: {
160 /// Unsafe if `this` cannot because casted to
161 /// `WinAudioActivateAudioInterfaceCompletionHandler`. Also would be unsafe
162 /// if `release` is called more than `add_ref`.
163 ///
164 /// This is safe because `this` is
165 /// originally a `WinAudioActivateAudioInterfaceCompletionHandler and isn't called
166 /// more than `add_ref`.
release(this: *mut IUnknown) -> ULONG167 unsafe extern "system" fn release(this: *mut IUnknown) -> ULONG {
168 info!("Releasing ref in IActivateAudioInterfaceCompletionHandler.");
169 // Decrementing will free the `this` pointer if it's ref_count becomes 0.
170 let win_audio_completion_handler =
171 this as *mut WinAudioActivateAudioInterfaceCompletionHandler;
172 let ref_count = (*win_audio_completion_handler).decrement_counter();
173 if ref_count == 0 {
174 // Delete the pointer
175 drop(Box::from_raw(
176 this as *mut WinAudioActivateAudioInterfaceCompletionHandler,
177 ));
178 }
179 ref_count
180 }
181 release
182 },
183 },
184 ActivateCompleted: activate_completed,
185 };
186
187 /// `ActivateAudioInterfaceAsync` requires that `IActivateAudioCompletionHandler` to implement
188 /// `IAgileObject`, which means it is free threaded and can be called from any apartment. These
189 /// traits should allow it to do that.
190 unsafe impl Send for WinAudioActivateAudioInterfaceCompletionHandler {}
191 unsafe impl Sync for WinAudioActivateAudioInterfaceCompletionHandler {}
192
193 #[cfg(test)]
194 mod test {
195 use base::EventExt;
196
197 use super::*;
198
199 #[test]
test_query_interface_valid()200 fn test_query_interface_valid() {
201 let completion_handler = WinAudioActivateAudioInterfaceCompletionHandler::create_com_ptr(
202 Event::new_auto_reset().unwrap(),
203 );
204 let invalid_ref_iid = IUnknown::uuidof();
205 let mut null_value = std::ptr::null_mut();
206 let ppv_object: *mut *mut c_void = &mut null_value;
207
208 // Calling `QueryInterface`
209 let res = unsafe {
210 ((*(*completion_handler).lpVtbl).parent.QueryInterface)(
211 completion_handler.as_raw() as *mut IUnknown,
212 &invalid_ref_iid,
213 ppv_object,
214 )
215 };
216 assert_eq!(res, NOERROR);
217
218 // Release the reference from `QueryInteface` by calling `Release`
219 release(&completion_handler);
220
221 let invalid_ref_iid = IActivateAudioInterfaceCompletionHandler::uuidof();
222 let res = unsafe {
223 ((*(*completion_handler).lpVtbl).parent.QueryInterface)(
224 completion_handler.as_raw() as *mut IUnknown,
225 &invalid_ref_iid,
226 ppv_object,
227 )
228 };
229 assert_eq!(res, NOERROR);
230
231 release(&completion_handler);
232
233 let invalid_ref_iid = IAgileObject::uuidof();
234 let res = unsafe {
235 ((*(*completion_handler).lpVtbl).parent.QueryInterface)(
236 completion_handler.as_raw() as *mut IUnknown,
237 &invalid_ref_iid,
238 ppv_object,
239 )
240 };
241 release(&completion_handler);
242 assert_eq!(res, NOERROR);
243 }
244
245 #[test]
test_query_interface_invalid()246 fn test_query_interface_invalid() {
247 let completion_handler = WinAudioActivateAudioInterfaceCompletionHandler::create_com_ptr(
248 Event::new_auto_reset().unwrap(),
249 );
250 let invalid_ref_iid = IMMDeviceCollection::uuidof();
251 let mut null_value = std::ptr::null_mut();
252 let ppv_object: *mut *mut c_void = &mut null_value;
253
254 // Call `QueryInterface`
255 let res = unsafe {
256 ((*(*completion_handler).lpVtbl).parent.QueryInterface)(
257 completion_handler.as_raw() as *mut IUnknown,
258 &invalid_ref_iid,
259 ppv_object,
260 )
261 };
262 assert_eq!(res, E_NOINTERFACE)
263 }
264
265 #[test]
test_add_ref()266 fn test_add_ref() {
267 // ref_count = 1
268 let completion_handler = WinAudioActivateAudioInterfaceCompletionHandler::create_com_ptr(
269 Event::new_auto_reset().unwrap(),
270 );
271 // ref_count = 2
272 let ref_count = add_ref(&completion_handler);
273 assert_eq!(ref_count, 2);
274 // ref_count = 1
275 release(&completion_handler);
276 // ref_count = 0 since ComPtr drops
277 }
278
279 #[test]
test_release()280 fn test_release() {
281 // ref_count = 1
282 let completion_handler = WinAudioActivateAudioInterfaceCompletionHandler::create_com_ptr(
283 Event::new_auto_reset().unwrap(),
284 );
285 // ref_count = 2
286 let ref_count = add_ref(&completion_handler);
287 assert_eq!(ref_count, 2);
288 // ref_count = 1
289 let ref_count = release(&completion_handler);
290 assert_eq!(ref_count, 1);
291 // ref_count = 0 since ComPtr drops
292 }
293
release(completion_handler: &ComPtr<IActivateAudioInterfaceCompletionHandler>) -> ULONG294 fn release(completion_handler: &ComPtr<IActivateAudioInterfaceCompletionHandler>) -> ULONG {
295 unsafe {
296 ((*(*completion_handler).lpVtbl).parent.Release)(
297 completion_handler.as_raw() as *mut IUnknown
298 )
299 }
300 }
301
add_ref(completion_handler: &ComPtr<IActivateAudioInterfaceCompletionHandler>) -> ULONG302 fn add_ref(completion_handler: &ComPtr<IActivateAudioInterfaceCompletionHandler>) -> ULONG {
303 unsafe {
304 ((*(*completion_handler).lpVtbl).parent.AddRef)(
305 completion_handler.as_raw() as *mut IUnknown
306 )
307 }
308 }
309 }
310