Upgrade windows-rs, get the basics working

This commit is contained in:
Your Name
2023-02-18 21:59:02 -05:00
parent 1484eada3e
commit 532d404815
3 changed files with 224 additions and 93 deletions

76
Cargo.lock generated
View File

@@ -60,60 +60,90 @@ checksum = "b5babd2d3fcd28bcf9712ef93e437684156f5c46173a9718829aa57aa4aa37a8"
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.37.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
dependencies = [ dependencies = [
"windows-implement", "windows-implement",
"windows_aarch64_msvc", "windows-interface",
"windows_i686_gnu", "windows-targets",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
] ]
[[package]] [[package]]
name = "windows-implement" name = "windows-implement"
version = "0.37.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14" checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
dependencies = [ dependencies = [
"proc-macro2",
"quote",
"syn", "syn",
"windows-tokens",
] ]
[[package]] [[package]]
name = "windows-tokens" name = "windows-interface"
version = "0.37.0" version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169" checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.37.0" version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.37.0" version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.37.0" version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.37.0" version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.37.0" version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"

View File

@@ -10,9 +10,8 @@ widestring = "1.0.0"
once_cell = "1.12.0" once_cell = "1.12.0"
[dependencies.windows] [dependencies.windows]
version = "0.37.0" version = "0.44.0"
features = [ features = [
"alloc",
"implement", "implement",
"Win32_Media_Audio", "Win32_Media_Audio",
"Win32_UI_Shell_PropertiesSystem", "Win32_UI_Shell_PropertiesSystem",

View File

@@ -2,19 +2,24 @@ mod pid_to_exe;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{ use std::{
collections::HashSet,
error::Error, error::Error,
ffi::c_void, ffi::{OsStr, OsString},
mem, path::Path,
ptr::{self, null_mut}, sync::{Arc, Mutex} ptr::null_mut,
sync::{Arc, Mutex},
}; };
use widestring::U16CStr; use widestring::U16CStr;
use windows::{ use windows::{
core::{Interface, PCWSTR},
Win32::{ Win32::{
Foundation::{HINSTANCE, HWND}, Foundation::{HINSTANCE, HWND},
Media::Audio::{ Media::Audio::{
eRender, IAudioSessionManager2, IMMDeviceEnumerator, MMDeviceEnumerator, eRender, IAudioSessionControl, IAudioSessionControl2, IAudioSessionManager2,
DEVICE_STATE_ACTIVE, IAudioSessionNotification, IAudioSessionControl, IAudioSessionNotification_Impl, IAudioSessionControl2, IMMNotificationClient, IMMNotificationClient_Impl, IAudioSessionNotification, IAudioSessionNotification_Impl, IMMDeviceEnumerator,
IMMNotificationClient, IMMNotificationClient_Impl, ISimpleAudioVolume,
MMDeviceEnumerator, DEVICE_STATE_ACTIVE,
}, },
System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED}, System::Com::{CoCreateInstance, CoInitializeEx, CLSCTX_ALL, COINIT_MULTITHREADED},
UI::{ UI::{
@@ -22,10 +27,10 @@ use windows::{
WindowsAndMessaging::{ WindowsAndMessaging::{
DispatchMessageW, GetMessageW, GetWindowThreadProcessId, TranslateMessage, DispatchMessageW, GetMessageW, GetWindowThreadProcessId, TranslateMessage,
EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT, EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT,
WINEVENT_SKIPOWNPROCESS, WINEVENT_SKIPOWNPROCESS, GetForegroundWindow,
},
}, },
}, },
}, core::{Interface, PCWSTR},
}; };
use crate::pid_to_exe::pid_to_exe_path; use crate::pid_to_exe::pid_to_exe_path;
@@ -33,7 +38,7 @@ use crate::pid_to_exe::pid_to_exe_path;
unsafe extern "system" fn win_event_proc( unsafe extern "system" fn win_event_proc(
_hook: HWINEVENTHOOK, _hook: HWINEVENTHOOK,
event: u32, event: u32,
hwnd: HWND, _hwnd: HWND,
_id_object: i32, _id_object: i32,
_id_child: i32, _id_child: i32,
_dw_event_thread: u32, _dw_event_thread: u32,
@@ -41,8 +46,11 @@ unsafe extern "system" fn win_event_proc(
) { ) {
if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND { if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND {
let mut pid: u32 = 0; let mut pid: u32 = 0;
GetWindowThreadProcessId(hwnd, &mut pid); // Instead of using the hwnd passed to us in the wineventproc, call GetForegroundWindow again because Hollow Knight does something weird
println!("{:?}", pid_to_exe_path(pid)); // and immediately fires again with the hwnd for explorer?
GetWindowThreadProcessId(GetForegroundWindow(), Some(&mut pid));
let path = pid_to_exe_path(pid).unwrap_or("".to_string());
MUTER.lock().unwrap().notify_window_changed(&path);
} }
} }
@@ -84,25 +92,34 @@ Okay, the main way this will function:
- Register Session Notification on Session Manager - Register Session Notification on Session Manager
- Get session enumerator from 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 - Detect sessions from applications we care about by getting session PID and mapping to process names using existing code
- This should really work based on PID and unmute only the open process's PID when focused? Though maybe not due to the weird nature of electron apps and shit?
- Keep it the way it is for now I guess
*/ */
#[windows::core::implement(IAudioSessionNotification)] #[windows::core::implement(IAudioSessionNotification)]
struct SessionNotification { struct SessionNotification {}
sessions: Arc<Mutex<Vec<IAudioSessionControl>>>
}
impl IAudioSessionNotification_Impl for SessionNotification { impl IAudioSessionNotification_Impl for SessionNotification {
fn OnSessionCreated(self: &SessionNotification,newsession: &core::option::Option<IAudioSessionControl>) -> windows::core::Result<()> { fn OnSessionCreated(
println!("Got OSC call"); self: &SessionNotification,
let ses = newsession.as_ref().unwrap().clone(); newsession: &core::option::Option<IAudioSessionControl>,
let s2: IAudioSessionControl2 = ses.cast().unwrap(); ) -> windows::core::Result<()> {
println!("Holy shit, new session created! {}", unsafe { pid_to_exe_path(s2.GetProcessId().unwrap()).unwrap() }); let ses: IAudioSessionControl2 = newsession.as_ref().unwrap().cast().unwrap();
self.sessions.lock().unwrap().push(ses); println!("New session created: {}", unsafe {
ses.GetProcessId()
.and_then(|x| Ok(pid_to_exe_path(x).unwrap().to_string()))
.unwrap_or("UNKNOWN".to_string())
});
MUTER.lock()
.unwrap()
.add_session_if_interesting(ses)
.unwrap();
Ok(()) Ok(())
} }
} }
impl Drop for SessionNotification { impl Drop for SessionNotification {
fn drop(&mut self) { fn drop(&mut self) {
println!("SN drop"); println!("SN drop");
} }
@@ -118,70 +135,141 @@ impl Drop for DeviceNotificationClient {
} }
impl IMMNotificationClient_Impl for DeviceNotificationClient { impl IMMNotificationClient_Impl for DeviceNotificationClient {
fn OnDeviceStateChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_dwnewstate:u32) -> windows::core::Result<()> { fn OnDeviceStateChanged(
&self,
_pwstrdeviceid: &windows::core::PCWSTR,
_dwnewstate: u32,
) -> windows::core::Result<()> {
println!("OnDeviceStateChanged"); println!("OnDeviceStateChanged");
Ok(()) Ok(())
} }
fn OnDeviceAdded(&self,_pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { fn OnDeviceAdded(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
// EUGH!
println!("OnDeviceAdded"); println!("OnDeviceAdded");
/* MUTER.lock()
KING.lock().unwrap().add_device_by_id(pwstrdeviceid).map_err(|error| { .unwrap()
.add_device_by_id(pwstrdeviceid)
.map_err(|error| {
println!("Error adding device: {:?}", error); println!("Error adding device: {:?}", error);
}).unwrap_or(()); })
*/ .unwrap_or(());
Ok(()) Ok(())
} }
fn OnDeviceRemoved(&self, _pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { fn OnDeviceRemoved(&self, _pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
println!("OnDeviceRemoved"); println!("OnDeviceRemoved");
// TODO: Remove device and all its sessions
Ok(()) Ok(())
} }
fn OnDefaultDeviceChanged(&self,_flow:windows::Win32::Media::Audio::EDataFlow,_role:windows::Win32::Media::Audio::ERole,_pwstrdefaultdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> { fn OnDefaultDeviceChanged(
&self,
_flow: windows::Win32::Media::Audio::EDataFlow,
_role: windows::Win32::Media::Audio::ERole,
_pwstrdefaultdeviceid: &windows::core::PCWSTR,
) -> windows::core::Result<()> {
println!("OnDefaultDeviceChanged"); println!("OnDefaultDeviceChanged");
Ok(()) Ok(())
} }
fn OnPropertyValueChanged(&self,_pwstrdeviceid: &windows::core::PCWSTR,_key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY) -> windows::core::Result<()> { fn OnPropertyValueChanged(
&self,
_pwstrdeviceid: &windows::core::PCWSTR,
_key: &windows::Win32::UI::Shell::PropertiesSystem::PROPERTYKEY,
) -> windows::core::Result<()> {
println!("OnPropertyValueChanged"); println!("OnPropertyValueChanged");
Ok(()) Ok(())
} }
} }
struct SessionKing { struct SessionMuter {
sessions: Arc<Mutex<Vec<IAudioSessionControl>>>, sessions: Arc<Mutex<Vec<IAudioSessionControl2>>>,
device_enumerator: IMMDeviceEnumerator, device_enumerator: IMMDeviceEnumerator,
dnc : IMMNotificationClient dnc: IMMNotificationClient,
session_notification: IAudioSessionNotification,
good_files: HashSet<String>,
mute_flag: bool,
session_managers: Vec<IAudioSessionManager2>
} }
impl SessionKing { impl SessionMuter {
fn new() -> SessionKing { fn new() -> SessionMuter {
SessionKing { let s = Arc::new(Mutex::new(Vec::new()));
sessions: Arc::new(Mutex::new(Vec::new())), SessionMuter {
device_enumerator: unsafe { CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap() }, session_managers: Vec::new(),
dnc: IMMNotificationClient::from(DeviceNotificationClient {}) sessions: s.clone(),
device_enumerator: unsafe {
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap()
},
dnc: IMMNotificationClient::from(DeviceNotificationClient {}),
session_notification: IAudioSessionNotification::from(SessionNotification {}),
good_files: HashSet::from(["hollow_knight.exe"].map(|s| s.to_string())),
mute_flag: true,
} }
} }
fn add_session_if_interesting(
fn boot_devices(self: &mut SessionKing) -> Result<String, Box<dyn Error>> { self: &mut SessionMuter,
session: IAudioSessionControl2,
) -> Result<(), Box<dyn Error>> {
let file_name = self.session_to_filename(&session);
if file_name.is_err() {
return Ok(());
}
let fn_str = file_name
.unwrap()
.to_os_string()
.to_string_lossy()
.to_string();
println!("Got session: {:?}", fn_str);
if self.good_files.contains(&fn_str) {
unsafe { unsafe {
(Interface::vtable(&self.device_enumerator).RegisterEndpointNotificationCallback)(Interface::as_raw(&self.device_enumerator), self.dnc.as_raw()).ok()?; let volume: ISimpleAudioVolume = session.cast()?;
let device_collection = volume.SetMute(self.mute_flag, null_mut())?;
self.device_enumerator.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?; }
self.sessions.lock().unwrap().push(session);
}
return Ok(());
}
unsafe fn set_mute_all(self: &mut SessionMuter, mute: bool) {
let sessions = self.sessions.lock().unwrap();
let results = sessions
.iter()
.map(|session_control2| session_control2.cast::<ISimpleAudioVolume>())
.map(|vol_result| vol_result.map(|volume| volume.SetMute(mute, null_mut())));
for err in results.filter_map(|x| x.err()) {
println!("Error muting a session: {:?}", err);
}
}
unsafe fn notify_window_changed(self: &mut SessionMuter, path: &str) {
let binding = Path::new(path).file_name().unwrap_or(OsStr::new(""));
let file_name = binding.to_os_string().to_string_lossy().to_string();
self.mute_flag = !self.good_files.contains(&file_name);
self.set_mute_all(self.mute_flag);
println!("Mute set to {} due to selection of {}", self.mute_flag, file_name);
}
fn boot_devices(self: &mut SessionMuter) -> Result<String, Box<dyn Error>> {
unsafe {
self.device_enumerator
.RegisterEndpointNotificationCallback(&self.dnc)?;
let device_collection = self
.device_enumerator
.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?;
let dc = 0..device_collection.GetCount()?; let dc = 0..device_collection.GetCount()?;
let devices = dc.map(|x| device_collection.Item(x)); let devices = dc.map(|x| device_collection.Item(x));
for x in devices { for x in devices {
let mmdevice = x?; let mmdevice = x?;
self.add_device(mmdevice)?; self.add_device(mmdevice)?;
} }
println!("Boot done");
return Ok("Done".to_string()); return Ok("Done".to_string());
} }
} }
fn add_device_by_id(self: &mut SessionKing, id: &PCWSTR) -> Result<(), Box<dyn Error>> { fn add_device_by_id(self: &mut SessionMuter, id: &PCWSTR) -> Result<(), Box<dyn Error>> {
unsafe { unsafe {
let device_enumerator: IMMDeviceEnumerator = let device_enumerator: IMMDeviceEnumerator =
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?; CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)?;
@@ -191,40 +279,54 @@ impl SessionKing {
} }
} }
unsafe fn add_device(self: &mut SessionKing, mmdevice: windows::Win32::Media::Audio::IMMDevice) -> Result<(), Box<dyn Error>> { fn session_to_filename(
let mut sm_ptr: *mut c_void = null_mut(); self: &mut SessionMuter,
mmdevice.Activate( session: &IAudioSessionControl2,
&<IAudioSessionManager2 as ::windows::core::Interface>::IID, ) -> Result<OsString, Box<dyn Error>> {
CLSCTX_ALL, unsafe {
null_mut(), let pid = session.GetProcessId()?;
ptr::addr_of_mut!(sm_ptr), let path = pid_to_exe_path(pid)?;
)?; let file_name = Path::new(&path)
let sm: IAudioSessionManager2 = mem::transmute(sm_ptr); .file_name()
.ok_or("Failed to extract filename from path")?;
return Ok(file_name.to_os_string());
}
}
unsafe fn add_device(
self: &mut SessionMuter,
mmdevice: windows::Win32::Media::Audio::IMMDevice,
) -> Result<(), Box<dyn Error>> {
let sm: IAudioSessionManager2 = mmdevice.Activate(CLSCTX_ALL, None)?;
sm.RegisterSessionNotification(&self.session_notification)?;
let sesenum = sm.GetSessionEnumerator()?; let sesenum = sm.GetSessionEnumerator()?;
let sn_count = sesenum.GetCount()?; let sn_count = sesenum.GetCount()?;
let mut cdses = (0..sn_count).map(|idx| sesenum.GetSession(idx)).collect::<Result<Vec<IAudioSessionControl>,_>>()?; let device_sessions = (0..sn_count)
self.sessions.lock().unwrap().append(&mut cdses); .map(|idx| sesenum.GetSession(idx))
sm.RegisterSessionNotification(IAudioSessionNotification::from( SessionNotification { .map(|x| match x {
sessions: self.sessions.clone() Err(e) => Err(e),
}))?; Ok(v) => v.cast(),
})
.collect::<Result<Vec<IAudioSessionControl2>, _>>()?;
for session in device_sessions {
self.add_session_if_interesting(session)?;
}
let str = U16CStr::from_ptr_str(mmdevice.GetId()?.0).to_string_lossy(); let str = U16CStr::from_ptr_str(mmdevice.GetId()?.0).to_string_lossy();
println!("Device Added: {} {}", str, sn_count); println!("Device Added: {} {}", str, sn_count);
self.session_managers.push(sm);
Ok(()) Ok(())
} }
} }
unsafe impl Send for SessionKing {} unsafe impl Send for SessionMuter {}
unsafe impl Sync for SessionKing {} unsafe impl Sync for SessionMuter {}
static KING : Lazy<Arc<Mutex<SessionKing>>> = Lazy::new(|| { static MUTER: Lazy<Arc<Mutex<SessionMuter>>> = Lazy::new(|| Arc::new(Mutex::new(SessionMuter::new())));
Arc::new(Mutex::new(SessionKing::new()))
});
fn main() { fn main() {
unsafe { unsafe {
CoInitializeEx(null_mut(), COINIT_MULTITHREADED).unwrap(); CoInitializeEx(None, COINIT_MULTITHREADED).unwrap();
} }
KING.lock().unwrap().boot_devices().unwrap(); MUTER.lock().unwrap().boot_devices().unwrap();
win_event_hook_loop(); win_event_hook_loop();
} }