@@ -75,15 +75,20 @@ mod windows_impl {
7575 use super :: VolumeControlImpl ;
7676 use windows:: core:: Interface ;
7777 use windows:: Win32 :: Media :: Audio :: {
78- eRender, ERole , IAudioSessionControl2 , IAudioSessionEnumerator , IAudioSessionManager2 ,
79- IMMDeviceEnumerator , ISimpleAudioVolume , MMDeviceEnumerator ,
78+ eRender, ERole , IAudioSessionControl2 , IAudioSessionManager2 , IMMDeviceEnumerator ,
79+ ISimpleAudioVolume , MMDeviceEnumerator ,
8080 } ;
8181 use windows:: Win32 :: System :: Com :: {
8282 CoCreateInstance , CoInitializeEx , CoUninitialize , CLSCTX_ALL , COINIT_MULTITHREADED ,
8383 } ;
8484
85+ // Wrapper to make ISimpleAudioVolume Send
86+ // SAFETY: COM objects are thread-safe when used with COINIT_MULTITHREADED
87+ struct SendableVolumeInterface ( ISimpleAudioVolume ) ;
88+ unsafe impl Send for SendableVolumeInterface { }
89+
8590 pub struct WindowsVolumeControl {
86- volume_interface : Option < ISimpleAudioVolume > ,
91+ volume_interface : Option < SendableVolumeInterface > ,
8792 com_initialized : bool ,
8893 }
8994
@@ -113,10 +118,10 @@ mod windows_impl {
113118
114119 // S_FALSE means already initialized, which is okay
115120 let com_initialized = com_result. is_ok ( )
116- || com_result
117- == Err ( windows :: core :: Error :: from_hresult (
118- windows:: Win32 :: Foundation :: S_FALSE ,
119- ) ) ;
121+ || matches ! (
122+ com_result ,
123+ Err ( e ) if e . code ( ) == windows:: Win32 :: Foundation :: S_FALSE
124+ ) ;
120125
121126 if !com_initialized {
122127 return Err ( "Failed to initialize COM" . to_string ( ) ) ;
@@ -156,7 +161,7 @@ mod windows_impl {
156161 if let Ok ( volume) = session_control. cast :: < ISimpleAudioVolume > ( ) {
157162 eprintln ! ( "[VolumeControl] Found audio session for current process (PID: {})" , current_pid) ;
158163 return Ok ( Self {
159- volume_interface : Some ( volume) ,
164+ volume_interface : Some ( SendableVolumeInterface ( volume) ) ,
160165 com_initialized,
161166 } ) ;
162167 }
@@ -206,8 +211,8 @@ mod windows_impl {
206211 if let Ok ( session_pid) = unsafe { session_control2. GetProcessId ( ) } {
207212 if session_pid == current_pid {
208213 if let Ok ( volume) = session_control. cast :: < ISimpleAudioVolume > ( ) {
209- self . volume_interface = Some ( volume) ;
210- return Ok ( self . volume_interface . as_ref ( ) . unwrap ( ) ) ;
214+ self . volume_interface = Some ( SendableVolumeInterface ( volume) ) ;
215+ return Ok ( & self . volume_interface . as_ref ( ) . unwrap ( ) . 0 ) ;
211216 }
212217 }
213218 }
@@ -290,7 +295,7 @@ mod windows_impl {
290295
291296 fn get_volume ( & self ) -> Result < u8 , String > {
292297 if let Some ( ref volume_interface) = self . volume_interface {
293- let volume_scalar = unsafe { volume_interface. GetMasterVolume ( ) }
298+ let volume_scalar = unsafe { volume_interface. 0 . GetMasterVolume ( ) }
294299 . map_err ( |e| format ! ( "Failed to get volume: {}" , e) ) ?;
295300
296301 Ok ( ( volume_scalar * 100.0 ) as u8 )
@@ -301,7 +306,7 @@ mod windows_impl {
301306
302307 fn get_mute ( & self ) -> Result < bool , String > {
303308 if let Some ( ref volume_interface) = self . volume_interface {
304- let muted = unsafe { volume_interface. GetMute ( ) }
309+ let muted = unsafe { volume_interface. 0 . GetMute ( ) }
305310 . map_err ( |e| format ! ( "Failed to get mute state: {}" , e) ) ?;
306311
307312 Ok ( muted. as_bool ( ) )
0 commit comments