diff --git a/Cargo.lock b/Cargo.lock index fcc2831..2f7e57a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,40 +6,16 @@ version = 3 name = "auto_mute_tool" version = "0.1.0" dependencies = [ - "com", + "once_cell", + "widestring", "windows", ] [[package]] -name = "com" -version = "0.6.0" +name = "once_cell" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" -dependencies = [ - "com_macros", -] - -[[package]] -name = "com_macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" -dependencies = [ - "com_macros_support", - "proc-macro2", - "syn", -] - -[[package]] -name = "com_macros_support" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "proc-macro2" @@ -76,6 +52,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "widestring" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5babd2d3fcd28bcf9712ef93e437684156f5c46173a9718829aa57aa4aa37a8" + [[package]] name = "windows" version = "0.37.0" diff --git a/Cargo.toml b/Cargo.toml index f4cdc07..58199d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -com = {version="0.6.0", features=["production"]} +widestring = "1.0.0" +once_cell = "1.12.0" [dependencies.windows] version = "0.37.0" @@ -22,4 +23,7 @@ features = [ "Win32_Security", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging", + "Win32_System_Com", + "Win32_System_Com_StructuredStorage" + ] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index cc2aabf..1f8955b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,31 @@ mod pid_to_exe; -use windows::Win32::{ - Foundation::{HINSTANCE, HWND}, - Media::Audio::{IMMNotificationClient, IMMNotificationClient_Impl}, - UI::{ - Accessibility::{SetWinEventHook, HWINEVENTHOOK}, - WindowsAndMessaging::{ - DispatchMessageW, GetMessageW, GetWindowThreadProcessId, TranslateMessage, - EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT, - WINEVENT_SKIPOWNPROCESS, +use once_cell::sync::Lazy; +use std::{ + error::Error, + ffi::c_void, + mem, + ptr::{self, null_mut}, sync::{Arc, Mutex} +}; + +use widestring::U16CStr; +use windows::{ + Win32::{ + Foundation::{HINSTANCE, HWND}, + Media::Audio::{ + eRender, IAudioSessionManager2, IMMDeviceEnumerator, MMDeviceEnumerator, + DEVICE_STATE_ACTIVE, IAudioSessionNotification, IAudioSessionControl, IAudioSessionNotification_Impl, IAudioSessionControl2, IMMNotificationClient, IMMNotificationClient_Impl, }, - }, + System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED}, + UI::{ + Accessibility::{SetWinEventHook, HWINEVENTHOOK}, + WindowsAndMessaging::{ + DispatchMessageW, GetMessageW, GetWindowThreadProcessId, TranslateMessage, + EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT, + WINEVENT_SKIPOWNPROCESS, + }, + }, + }, core::{Interface, PCWSTR}, }; use crate::pid_to_exe::pid_to_exe_path; @@ -52,7 +67,7 @@ fn win_event_hook_loop() { WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS, ); - let mut msg: MSG = Default::default(); + let mut msg: MSG = MSG::default(); while GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() { TranslateMessage(&msg); DispatchMessageW(&msg); @@ -60,44 +75,156 @@ fn win_event_hook_loop() { } } -#[windows::core::implement(IMMNotificationClient)] -struct DeviceNotification; +/* +Okay, the main way this will function: +- MMDeviceEnumerator - get all current devices +- RegisterEndpointNotificationCallback - get updates on device add/remove +- For each new device found + - Activate Session Manager on Device + - Register Session Notification on Session Manager + - Get session enumerator from session manager + - Detect sessions from applications we care about by getting session PID and mapping to process names using existing code +*/ -impl IMMNotificationClient_Impl for DeviceNotification { - fn OnDeviceStateChanged( - &self, - pwstrdeviceid: &windows::core::PCWSTR, - dwnewstate: u32, - ) -> windows::core::Result<()> { - Ok(()) - } - - fn OnDeviceAdded(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { - Ok(()) - } - - fn OnDeviceRemoved(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { - Ok(()) - } - - fn OnDefaultDeviceChanged( - &self, - flow: windows::Win32::Media::Audio::EDataFlow, - role: windows::Win32::Media::Audio::ERole, - pwstrdefaultdeviceid: &windows::core::PCWSTR, - ) -> windows::core::Result<()> { - Ok(()) - } - - fn OnPropertyValueChanged( - &self, - pwstrdeviceid: &windows::core::PCWSTR, - key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY, - ) -> windows::core::Result<()> { +#[windows::core::implement(IAudioSessionNotification)] +struct SessionNotification { + sessions: Arc>> +} +impl IAudioSessionNotification_Impl for SessionNotification { + fn OnSessionCreated(self: &SessionNotification,newsession: &core::option::Option) -> windows::core::Result<()> { + println!("Got OSC call"); + let ses = newsession.as_ref().unwrap().clone(); + let s2: IAudioSessionControl2 = ses.cast().unwrap(); + println!("Holy shit, new session created! {}", unsafe { pid_to_exe_path(s2.GetProcessId().unwrap()).unwrap() }); + self.sessions.lock().unwrap().push(ses); Ok(()) } } +impl Drop for SessionNotification { + + fn drop(&mut self) { + println!("SN drop"); + } +} + +#[windows::core::implement(IMMNotificationClient)] +struct DeviceNotificationClient {} + +impl Drop for DeviceNotificationClient { + fn drop(&mut self) { + println!("DNC drop"); + } +} + +impl IMMNotificationClient_Impl for DeviceNotificationClient { + fn OnDeviceStateChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_dwnewstate:u32) -> windows::core::Result<()> { + println!("OnDeviceStateChanged"); + Ok(()) + } + + fn OnDeviceAdded(&self,_pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { + // EUGH! + println!("OnDeviceAdded"); + /* + KING.lock().unwrap().add_device_by_id(pwstrdeviceid).map_err(|error| { + println!("Error adding device: {:?}", error); + }).unwrap_or(()); + */ + Ok(()) + } + + fn OnDeviceRemoved(&self,_pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { + println!("OnDeviceRemoved"); + Ok(()) + } + + fn OnDefaultDeviceChanged(&self,_flow:windows::Win32::Media::Audio::EDataFlow,_role:windows::Win32::Media::Audio::ERole,_pwstrdefaultdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { + println!("OnDefaultDeviceChanged"); + Ok(()) + } + + fn OnPropertyValueChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY) -> windows::core::Result<()> { + println!("OnPropertyValueChanged"); + Ok(()) + } +} + +struct SessionKing { + sessions: Arc>>, + device_enumerator : IMMDeviceEnumerator, + dnc : IMMNotificationClient +} + +impl SessionKing { + fn new() -> SessionKing { + SessionKing { + sessions: Arc::new(Mutex::new(Vec::new())), + device_enumerator: unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap() }, + dnc: IMMNotificationClient::from(DeviceNotificationClient {}) + } + } + + + fn boot_devices(self: &mut SessionKing) -> Result> { + unsafe { + (Interface::vtable(&self.device_enumerator).RegisterEndpointNotificationCallback)(Interface::as_raw(&self.device_enumerator), self.dnc.as_raw()).ok()?; + let device_collection = + self.device_enumerator.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?; + let dc = 0..device_collection.GetCount()?; + let devices = dc.map(|x| device_collection.Item(x)); + for x in devices { + let mmdevice = x?; + self.add_device(mmdevice)?; + } + return Ok("Done".to_string()); + } + } + + fn add_device_by_id(self: &mut SessionKing, id: &PCWSTR) -> Result<(), Box> { + unsafe { + let device_enumerator: IMMDeviceEnumerator = + CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?; + let device = device_enumerator.GetDevice(id)?; + self.add_device(device)?; + Ok(()) + } + } + + unsafe fn add_device(self: &mut SessionKing, mmdevice: windows::Win32::Media::Audio::IMMDevice) -> Result<(), Box> { + let mut sm_ptr: *mut c_void = null_mut(); + mmdevice.Activate( + &::IID, + CLSCTX_ALL, + null_mut(), + ptr::addr_of_mut!(sm_ptr), + )?; + let sm: IAudioSessionManager2 = mem::transmute(sm_ptr); + let sesenum = sm.GetSessionEnumerator()?; + let sn_count = sesenum.GetCount()?; + let mut cdses = (0..sn_count).map(|idx| sesenum.GetSession(idx)).collect::,_>>()?; + self.sessions.lock().unwrap().append(&mut cdses); + sm.RegisterSessionNotification(IAudioSessionNotification::from( SessionNotification { + sessions: self.sessions.clone() + }))?; + let str = U16CStr::from_ptr_str(mmdevice.GetId()?.0).to_string_lossy(); + println!("Device Added: {} {}", str, sn_count); + Ok(()) + } + +} + +unsafe impl Send for SessionKing {} +unsafe impl Sync for SessionKing {} + +static KING : Lazy>> = Lazy::new(|| { + Arc::new(Mutex::new(SessionKing::new())) +}); + fn main() { + unsafe { + CoInitializeEx(null_mut(), COINIT_MULTITHREADED).unwrap(); + } + KING.lock().unwrap().boot_devices().unwrap(); win_event_hook_loop(); }