Hacky COM stuff

This commit is contained in:
Your Name
2022-08-01 00:11:37 -04:00
parent 6c50e23b47
commit 1484eada3e
3 changed files with 187 additions and 74 deletions

40
Cargo.lock generated
View File

@@ -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"

View File

@@ -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"
]

View File

@@ -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();
}