Hacky COM stuff
This commit is contained in:
40
Cargo.lock
generated
40
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
]
|
||||
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