Hacky COM stuff
This commit is contained in:
215
src/main.rs
215
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<Mutex<Vec<IAudioSessionControl>>>
|
||||
}
|
||||
impl IAudioSessionNotification_Impl for SessionNotification {
|
||||
fn OnSessionCreated(self: &SessionNotification,newsession: &core::option::Option<IAudioSessionControl>) -> 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<Mutex<Vec<IAudioSessionControl>>>,
|
||||
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<String, Box<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
let mut sm_ptr: *mut c_void = null_mut();
|
||||
mmdevice.Activate(
|
||||
&<IAudioSessionManager2 as ::windows::core::Interface>::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::<Result<Vec<IAudioSessionControl>,_>>()?;
|
||||
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<Arc<Mutex<SessionKing>>> = 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user