Compare commits
22 Commits
1484eada3e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a14bc78b3b | ||
|
|
e81900fa79 | ||
|
|
16f5f49c99 | ||
|
|
989c5c05a5 | ||
|
|
5f69fc664a | ||
|
|
82153d31c9 | ||
|
|
a647fc5186 | ||
|
|
847631d94e | ||
|
|
5d9c8b4d0c | ||
|
|
60b7b34708 | ||
|
|
45f8d9bd26 | ||
|
|
671814fc81 | ||
|
|
f7fde32897 | ||
|
|
3bb1ca9b1c | ||
|
|
f2ac3d5fa6 | ||
|
|
a5d0729bba | ||
|
|
9eb15a4a5f | ||
|
|
f2d5263244 | ||
|
|
270a256444 | ||
|
|
23e9f624f4 | ||
|
|
739dd6613b | ||
|
|
532d404815 |
274
Cargo.lock
generated
274
Cargo.lock
generated
@@ -3,43 +3,111 @@
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "auto_mute_tool"
|
||||
version = "0.1.0"
|
||||
name = "auto_mute_cli"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"widestring",
|
||||
"auto_mute_lib",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
name = "auto_mute_gui"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"auto_mute_lib",
|
||||
"windows",
|
||||
"windows-core",
|
||||
"winresource",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auto_mute_lib"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"windows",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -47,73 +115,193 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.0"
|
||||
name = "toml"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.0"
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5babd2d3fcd28bcf9712ef93e437684156f5c46173a9718829aa57aa4aa37a8"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.37.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
|
||||
checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_msvc",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.37.0"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a1062e555f7d9d66fd1130ed4f7c6ec41a47529ee0850cd0e926d95b26bb14"
|
||||
checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"windows-tokens",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-tokens"
|
||||
version = "0.37.0"
|
||||
name = "windows-interface"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169"
|
||||
checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d08106ce80268c4067c0571ca55a9b4e9516518eaa1a1fe9b37ca403ae1d1a34"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b888f919960b42ea4e11c2f408fadb55f78a9f236d5eef084103c8ce52893491"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.37.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.37.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.37.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.37.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.37.0"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winresource"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7276691b353ad4547af8c3268488d1311f4be791ffdc0c65b8cfa8f41eed693b"
|
||||
dependencies = [
|
||||
"toml",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
32
Cargo.toml
32
Cargo.toml
@@ -1,29 +1,3 @@
|
||||
[package]
|
||||
name = "auto_mute_tool"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
widestring = "1.0.0"
|
||||
once_cell = "1.12.0"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.37.0"
|
||||
features = [
|
||||
"alloc",
|
||||
"implement",
|
||||
"Win32_Media_Audio",
|
||||
"Win32_UI_Shell_PropertiesSystem",
|
||||
"Data_Xml_Dom",
|
||||
"Win32_UI",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_Com_StructuredStorage"
|
||||
|
||||
]
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
resolver = "2"
|
||||
BIN
auto_mute_gui.exe
Normal file
BIN
auto_mute_gui.exe
Normal file
Binary file not shown.
26
crates/auto_mute_cli/Cargo.toml
Normal file
26
crates/auto_mute_cli/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "auto_mute_cli"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.59.0"
|
||||
features = [
|
||||
"Win32_Media_Audio",
|
||||
"Win32_UI_Shell_PropertiesSystem",
|
||||
"Data_Xml_Dom",
|
||||
"Win32_UI",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_Devices_FunctionDiscovery",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_WindowsProgramming",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
auto_mute_lib = { path = "../auto_mute_lib" }
|
||||
48
crates/auto_mute_cli/src/main.rs
Normal file
48
crates/auto_mute_cli/src/main.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
env,
|
||||
error::Error,
|
||||
fs::{self, File},
|
||||
io::{BufRead, BufReader}
|
||||
};
|
||||
|
||||
use auto_mute_lib::muter::MuterThread;
|
||||
use windows::{
|
||||
Win32::{
|
||||
Foundation::HANDLE,
|
||||
Storage::FileSystem::{
|
||||
FindCloseChangeNotification, FindFirstChangeNotificationW, FindNextChangeNotification,
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
},
|
||||
System::Threading::{WaitForSingleObject, INFINITE},
|
||||
}, core::w,
|
||||
};
|
||||
|
||||
pub fn await_file_change(file_name: &str) -> Result<(), Box<dyn Error>> {
|
||||
unsafe {
|
||||
let md = fs::metadata(file_name)?.modified()?;
|
||||
let handle = FindFirstChangeNotificationW(w!("."), false, FILE_NOTIFY_CHANGE_LAST_WRITE)?;
|
||||
while md == fs::metadata(file_name)?.modified()? {
|
||||
WaitForSingleObject(HANDLE(handle.0), INFINITE);
|
||||
FindNextChangeNotification(handle)?;
|
||||
}
|
||||
println!("File change detected, restarting");
|
||||
FindCloseChangeNotification(handle)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn load_mute_txt(file_name: &str) -> HashSet<String> {
|
||||
let file = File::open(file_name).unwrap();
|
||||
HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap()))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
MuterThread::com_init();
|
||||
let mute_file: String = env::args().nth(1).unwrap_or("mute.txt".to_string());
|
||||
loop {
|
||||
let mut _mt = MuterThread::new(load_mute_txt(&mute_file), None);
|
||||
await_file_change(&mute_file).unwrap();
|
||||
}
|
||||
}
|
||||
25
crates/auto_mute_gui/Cargo.toml
Normal file
25
crates/auto_mute_gui/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "auto_mute_gui"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
winresource = "0.1.17"
|
||||
|
||||
[dependencies]
|
||||
auto_mute_lib = { path = "../auto_mute_lib" }
|
||||
windows-core = "0.59.0"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.59.0"
|
||||
features = [
|
||||
"Win32_UI",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_Foundation",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
11
crates/auto_mute_gui/build.rs
Normal file
11
crates/auto_mute_gui/build.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
extern crate winresource;
|
||||
|
||||
fn main() {
|
||||
if cfg!(target_os = "windows") {
|
||||
winresource::WindowsResource::new()
|
||||
.set_icon(r"C:\Users\ultra\ownCloud\ShitMuter\ShitMuter\icon1.ico")
|
||||
.set_icon_with_id(r"C:\Users\ultra\ownCloud\ShitMuter\ShitMuter\icon1.ico", "ICO_UNMUTE")
|
||||
.set_icon_with_id(r"C:\Users\ultra\ownCloud\ShitMuter\ShitMuter\icon2.ico", "ICO_MUTE")
|
||||
.compile().unwrap();
|
||||
}
|
||||
}
|
||||
181
crates/auto_mute_gui/src/main.rs
Normal file
181
crates/auto_mute_gui/src/main.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
env,
|
||||
error::Error,
|
||||
ffi::c_void,
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
sync::mpsc,
|
||||
thread,
|
||||
};
|
||||
|
||||
use auto_mute_lib::muter::{MuteChangeNotification, MuterThread};
|
||||
use windows::Win32::{
|
||||
Foundation::HWND,
|
||||
System::LibraryLoader::GetModuleHandleW,
|
||||
UI::{
|
||||
Shell::{Shell_NotifyIconW, NIF_ICON, NIF_MESSAGE, NIM_ADD, NIM_MODIFY, NOTIFYICONDATAW},
|
||||
WindowsAndMessaging::{LoadIconW, WM_USER},
|
||||
},
|
||||
};
|
||||
use windows::{
|
||||
core::*, Win32::{Foundation::*, Graphics::Gdi::ValidateRect, System::LibraryLoader::GetModuleHandleA, UI::WindowsAndMessaging::*},
|
||||
};
|
||||
|
||||
use windows_core::w;
|
||||
|
||||
const WM_TRAY_MENU: u32 = WM_USER;
|
||||
|
||||
pub struct MagicTray {
|
||||
pub notifdata: NOTIFYICONDATAW,
|
||||
pub hwnd: HWND,
|
||||
}
|
||||
|
||||
impl MagicTray {
|
||||
fn new() -> MagicTray {
|
||||
unsafe {
|
||||
MagicTray {
|
||||
notifdata: NOTIFYICONDATAW {
|
||||
cbSize: size_of::<NOTIFYICONDATAW>().try_into().unwrap(),
|
||||
hWnd: HWND::default(),
|
||||
uID: 1,
|
||||
uFlags: NIF_ICON | NIF_MESSAGE,
|
||||
uCallbackMessage: WM_TRAY_MENU,
|
||||
hIcon: LoadIconW(
|
||||
Some(GetModuleHandleW(None).unwrap().into()),
|
||||
w!("ICO_UNMUTE"),
|
||||
)
|
||||
.unwrap(),
|
||||
..Default::default()
|
||||
},
|
||||
hwnd: MagicTray::main_window().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn main_window() -> std::result::Result<HWND, Box<dyn Error>> {
|
||||
unsafe {
|
||||
let instance = GetModuleHandleA(None)?;
|
||||
let window_class = s!("window");
|
||||
|
||||
let wc = WNDCLASSA {
|
||||
hCursor: LoadCursorW(None, IDC_ARROW)?,
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: window_class,
|
||||
|
||||
style: CS_HREDRAW | CS_VREDRAW,
|
||||
lpfnWndProc: Some(wndproc),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let atom = RegisterClassA(&wc);
|
||||
debug_assert!(atom != 0);
|
||||
|
||||
Ok(CreateWindowExA(
|
||||
WINDOW_EX_STYLE::default(),
|
||||
window_class,
|
||||
s!("Tray Host"),
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_loop(&mut self) {
|
||||
unsafe {
|
||||
SetWindowLongPtrW(self.hwnd, GWLP_USERDATA, (&mut* self as *mut _) as isize);
|
||||
self.notifdata.hWnd = self.hwnd;
|
||||
Shell_NotifyIconW(NIM_ADD, &self.notifdata).unwrap();
|
||||
|
||||
|
||||
let mut message = MSG::default();
|
||||
while GetMessageA(&mut message, None, 0, 0).into() {
|
||||
DispatchMessageA(&message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mute(&mut self, lparam: LPARAM) {
|
||||
unsafe {
|
||||
self.notifdata.hIcon = LoadIconW(
|
||||
Some(GetModuleHandleW(None).unwrap().into()),
|
||||
if lparam.0 == 0 {w!("ICO_UNMUTE")} else {w!("ICO_MUTE")},
|
||||
).unwrap();
|
||||
Shell_NotifyIconW(NIM_MODIFY, &self.notifdata).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MagicTray {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_mute_txt(file_name: &str) -> HashSet<String> {
|
||||
let file = File::open(file_name).unwrap();
|
||||
HashSet::from_iter(BufReader::new(file).lines().map(|line| line.unwrap()))
|
||||
}
|
||||
|
||||
const WM_APP_CUSTOM: u32 = WM_APP + 1;
|
||||
fn main() {
|
||||
|
||||
let mut tray = MagicTray::default();
|
||||
|
||||
let (tx, rx) = mpsc::channel::<MuteChangeNotification>();
|
||||
|
||||
MuterThread::com_init();
|
||||
let mute_file: String = env::args().nth(1).unwrap_or("mute.txt".to_string());
|
||||
let _mt = MuterThread::new(load_mute_txt(&mute_file), Some(tx));
|
||||
|
||||
let hwnd = tray.hwnd.0 as usize;
|
||||
|
||||
thread::spawn(move || loop {
|
||||
if let Ok(mpsc_msg) = rx.recv() {
|
||||
match mpsc_msg {
|
||||
MuteChangeNotification::MuteChanged(val) => unsafe {
|
||||
PostMessageA(
|
||||
Some(HWND(hwnd as *mut c_void)),
|
||||
WM_APP_CUSTOM,
|
||||
WPARAM(0),
|
||||
LPARAM(if val { 1 } else { 0 }),
|
||||
)
|
||||
.unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tray.run_loop()
|
||||
}
|
||||
|
||||
extern "system" fn wndproc(hwnd: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
|
||||
unsafe {
|
||||
let mt = (GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut MagicTray).as_mut();
|
||||
match message {
|
||||
WM_PAINT => {
|
||||
println!("WM_PAINT");
|
||||
_ = ValidateRect(Some(hwnd), None);
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_DESTROY => {
|
||||
println!("WM_DESTROY");
|
||||
PostQuitMessage(0);
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_APP_CUSTOM => {
|
||||
mt.unwrap().set_mute(lparam);
|
||||
LRESULT(0)
|
||||
}
|
||||
_ => DefWindowProcA(hwnd, message, wparam, lparam),
|
||||
}
|
||||
}
|
||||
}
|
||||
27
crates/auto_mute_lib/Cargo.toml
Normal file
27
crates/auto_mute_lib/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "auto_mute_lib"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
windows-core = "0.59.0"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.59.0"
|
||||
features = [
|
||||
"Win32_Media_Audio",
|
||||
"Win32_UI_Shell_PropertiesSystem",
|
||||
"Data_Xml_Dom",
|
||||
"Win32_UI",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_Foundation",
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_Devices_FunctionDiscovery",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Win32_System_Variant"
|
||||
]
|
||||
47
crates/auto_mute_lib/src/device_notification_client.rs
Normal file
47
crates/auto_mute_lib/src/device_notification_client.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use windows::Win32::Media::Audio::{
|
||||
IMMNotificationClient, IMMNotificationClient_Impl, DEVICE_STATE,
|
||||
};
|
||||
pub trait DeviceNotificationObserver {
|
||||
fn add_device(&self, device_id: &windows::core::PCWSTR);
|
||||
}
|
||||
|
||||
#[windows::core::implement(IMMNotificationClient)]
|
||||
pub(crate) struct DeviceNotificationClient {
|
||||
pub observer: Box<dyn DeviceNotificationObserver>,
|
||||
}
|
||||
|
||||
impl IMMNotificationClient_Impl for DeviceNotificationClient_Impl {
|
||||
fn OnDeviceStateChanged(
|
||||
&self,
|
||||
_pwstrdeviceid: &windows::core::PCWSTR,
|
||||
_dwnewstate: DEVICE_STATE,
|
||||
) -> windows::core::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn OnDeviceAdded(&self, pwstrdeviceid: &windows::core::PCWSTR) -> windows::core::Result<()> {
|
||||
self.observer.add_device(pwstrdeviceid);
|
||||
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::Foundation::PROPERTYKEY,
|
||||
) -> windows_core::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
6
crates/auto_mute_lib/src/lib.rs
Normal file
6
crates/auto_mute_lib/src/lib.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
mod device_notification_client;
|
||||
mod pid_to_exe;
|
||||
mod session_notification;
|
||||
mod sm_session_notifier;
|
||||
mod window_change;
|
||||
pub mod muter;
|
||||
182
crates/auto_mute_lib/src/muter.rs
Normal file
182
crates/auto_mute_lib/src/muter.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
error::Error,
|
||||
ffi::OsString,
|
||||
path::Path,
|
||||
ptr::null_mut,
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use crate::sm_session_notifier::SMSessionNotifierThread;
|
||||
use crate::window_change::WindowChangeMonitor;
|
||||
use windows::{
|
||||
core::Interface,
|
||||
Win32::{
|
||||
Media::Audio::{IAudioSessionControl2, ISimpleAudioVolume},
|
||||
System::Com::{CoInitializeEx, COINIT_MULTITHREADED},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::pid_to_exe::pid_to_exe_path;
|
||||
enum MuterMessage {
|
||||
WindowChange(String),
|
||||
AddSession(IAudioSessionControl2),
|
||||
Exit(),
|
||||
}
|
||||
|
||||
pub enum MuteChangeNotification {
|
||||
MuteChanged(bool),
|
||||
}
|
||||
|
||||
unsafe impl Send for MuterMessage {}
|
||||
|
||||
struct SessionMuter {
|
||||
sessions: Vec<IAudioSessionControl2>,
|
||||
mute_executables: HashSet<String>,
|
||||
mute_flag: bool,
|
||||
_session_notifier: SMSessionNotifierThread,
|
||||
_win_change_mon: WindowChangeMonitor,
|
||||
rx: Receiver<MuterMessage>,
|
||||
notify_tx: Option<Sender<MuteChangeNotification>>,
|
||||
}
|
||||
|
||||
impl SessionMuter {
|
||||
fn new(
|
||||
mute_executables: HashSet<String>,
|
||||
rx: Receiver<MuterMessage>,
|
||||
tx: Sender<MuterMessage>,
|
||||
notify_tx: Option<Sender<MuteChangeNotification>>,
|
||||
) -> SessionMuter {
|
||||
SessionMuter {
|
||||
sessions: Vec::new(),
|
||||
mute_executables,
|
||||
mute_flag: true,
|
||||
_session_notifier: {
|
||||
let tx = tx.clone();
|
||||
SMSessionNotifierThread::new(Box::new(move |session| {
|
||||
tx.send(MuterMessage::AddSession(session)).unwrap();
|
||||
}))
|
||||
},
|
||||
_win_change_mon: {
|
||||
WindowChangeMonitor::start(Box::new(move |s| {
|
||||
tx.send(MuterMessage::WindowChange(s.to_owned())).unwrap();
|
||||
}))
|
||||
},
|
||||
rx,
|
||||
notify_tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
loop {
|
||||
let msg = self.rx.recv().unwrap();
|
||||
match msg {
|
||||
MuterMessage::WindowChange(win) => self.notify_window_changed(&win),
|
||||
MuterMessage::AddSession(session) => self.add_session(session).unwrap(),
|
||||
MuterMessage::Exit() => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_session(
|
||||
self: &mut SessionMuter,
|
||||
session: IAudioSessionControl2,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if let Ok(file_name) = self.session_to_filename(&session) {
|
||||
let fn_str = file_name.to_string_lossy().to_string();
|
||||
if self.mute_executables.contains(&fn_str) {
|
||||
println!("Adding session from: {:?}", fn_str);
|
||||
unsafe {
|
||||
let volume: ISimpleAudioVolume = session.cast()?;
|
||||
volume.SetMute(self.mute_flag, null_mut())?;
|
||||
}
|
||||
self.sessions.push(session);
|
||||
} else {
|
||||
println!("Skipping session from: {:?}", fn_str);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mute_all(self: &mut SessionMuter, mute: bool) {
|
||||
unsafe {
|
||||
self.notify_tx
|
||||
.as_ref()
|
||||
.and_then(|x| x.send(MuteChangeNotification::MuteChanged(mute)).ok());
|
||||
let results = self
|
||||
.sessions
|
||||
.iter()
|
||||
.map(|session_control2| session_control2.cast::<ISimpleAudioVolume>())
|
||||
.map(|vol_result| vol_result?.SetMute(mute, null_mut()));
|
||||
for err in results.filter_map(|x| x.err()) {
|
||||
println!("Error muting a session: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_window_changed(self: &mut SessionMuter, path: &str) {
|
||||
let binding = Path::new(path)
|
||||
.file_name()
|
||||
.expect("failed to extract filename from path");
|
||||
let file_name = binding.to_os_string().to_string_lossy().to_string();
|
||||
let mute_flag = !self.mute_executables.contains(&file_name);
|
||||
if mute_flag != self.mute_flag {
|
||||
self.mute_flag = mute_flag;
|
||||
self.set_mute_all(self.mute_flag);
|
||||
println!(
|
||||
"Mute set to {} due to foreground window: {}",
|
||||
self.mute_flag, file_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn session_to_filename(
|
||||
self: &mut SessionMuter,
|
||||
session: &IAudioSessionControl2,
|
||||
) -> Result<OsString, Box<dyn Error>> {
|
||||
unsafe {
|
||||
let pid = session.GetProcessId()?;
|
||||
let path = pid_to_exe_path(pid)?;
|
||||
let file_name = Path::new(&path)
|
||||
.file_name()
|
||||
.ok_or("Failed to extract filename from path")?;
|
||||
Ok(file_name.to_os_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SessionMuter {}
|
||||
|
||||
pub struct MuterThread {
|
||||
handle: Option<JoinHandle<()>>,
|
||||
sender: Sender<MuterMessage>,
|
||||
}
|
||||
|
||||
impl MuterThread {
|
||||
pub fn com_init() {
|
||||
unsafe { CoInitializeEx(None, COINIT_MULTITHREADED).unwrap() };
|
||||
}
|
||||
pub fn new(
|
||||
s: HashSet<String>,
|
||||
notify_tx: Option<Sender<MuteChangeNotification>>,
|
||||
) -> MuterThread {
|
||||
let (sender, receiver) = mpsc::channel::<MuterMessage>();
|
||||
MuterThread {
|
||||
sender: sender.clone(),
|
||||
handle: Some(thread::spawn(move || {
|
||||
let mut muter = SessionMuter::new(s, receiver, sender, notify_tx);
|
||||
muter.run();
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MuterThread {
|
||||
fn drop(&mut self) {
|
||||
if let Some(handle) = self.handle.take() {
|
||||
self.sender.send(MuterMessage::Exit()).unwrap();
|
||||
handle.join().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
29
crates/auto_mute_lib/src/pid_to_exe.rs
Normal file
29
crates/auto_mute_lib/src/pid_to_exe.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use std::error::Error;
|
||||
|
||||
use windows::Win32::{
|
||||
Foundation::{CloseHandle, MAX_PATH},
|
||||
System::Threading::{
|
||||
OpenProcess, QueryFullProcessImageNameW, PROCESS_QUERY_LIMITED_INFORMATION
|
||||
},
|
||||
};
|
||||
|
||||
pub fn pid_to_exe_path(pid: u32) -> Result<String, Box<dyn Error>> {
|
||||
let mut exe_name: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
|
||||
let mut size: u32 = exe_name.capacity().try_into().unwrap();
|
||||
unsafe {
|
||||
let process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid)?;
|
||||
QueryFullProcessImageNameW(
|
||||
process,
|
||||
Default::default(),
|
||||
windows::core::PWSTR(exe_name.as_mut_ptr()),
|
||||
&mut size,
|
||||
)
|
||||
.ok();
|
||||
|
||||
CloseHandle(process)?;
|
||||
|
||||
exe_name.set_len(size.try_into().unwrap());
|
||||
}
|
||||
let process_name = String::from_utf16_lossy(&exe_name);
|
||||
Ok(process_name)
|
||||
}
|
||||
23
crates/auto_mute_lib/src/session_notification.rs
Normal file
23
crates/auto_mute_lib/src/session_notification.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use windows::{
|
||||
core::{implement, Interface},
|
||||
Win32::Media::Audio::{
|
||||
IAudioSessionControl, IAudioSessionControl2, IAudioSessionNotification, IAudioSessionNotification_Impl,
|
||||
},
|
||||
};
|
||||
|
||||
#[implement(IAudioSessionNotification)]
|
||||
pub(crate) struct SessionNotification {
|
||||
pub(crate) observer: Box<dyn SessionObserver>,
|
||||
}
|
||||
|
||||
pub trait SessionObserver {
|
||||
fn add_session(&self, session: IAudioSessionControl2);
|
||||
}
|
||||
|
||||
impl IAudioSessionNotification_Impl for SessionNotification_Impl {
|
||||
fn OnSessionCreated(&self,newsession:windows_core::Ref<'_, IAudioSessionControl>) -> windows::core::Result<()> {
|
||||
let ses: IAudioSessionControl2 = newsession.as_ref().unwrap().cast().unwrap();
|
||||
self.observer.add_session(ses);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
205
crates/auto_mute_lib/src/sm_session_notifier.rs
Normal file
205
crates/auto_mute_lib/src/sm_session_notifier.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
device_notification_client::{DeviceNotificationClient, DeviceNotificationObserver},
|
||||
session_notification::{SessionNotification, SessionObserver},
|
||||
};
|
||||
use windows::{
|
||||
core::{Interface, PCWSTR},
|
||||
Win32::{
|
||||
Devices::FunctionDiscovery::PKEY_Device_FriendlyName,
|
||||
Media::Audio::{
|
||||
eRender, IAudioSessionControl2, IAudioSessionManager2, IAudioSessionNotification,
|
||||
IMMDeviceEnumerator, IMMNotificationClient, MMDeviceEnumerator, DEVICE_STATE_ACTIVE, IMMDevice,
|
||||
},
|
||||
System::Com::{
|
||||
CoCreateInstance, StructuredStorage::PropVariantClear, CLSCTX_ALL, STGM_READ,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub enum SMMessage {
|
||||
Session(IAudioSessionControl2),
|
||||
Device(PCWSTR),
|
||||
Exit(),
|
||||
}
|
||||
|
||||
unsafe impl Send for SMMessage {}
|
||||
|
||||
struct SessionToMessage {
|
||||
sender: Sender<SMMessage>,
|
||||
}
|
||||
|
||||
impl SessionObserver for SessionToMessage {
|
||||
fn add_session(&self, session: IAudioSessionControl2) {
|
||||
self.sender
|
||||
.send(SMMessage::Session(session))
|
||||
.unwrap_or_else(|_| println!("Failed to add new session"));
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceToMessage {
|
||||
sender: Sender<SMMessage>,
|
||||
}
|
||||
|
||||
impl DeviceNotificationObserver for DeviceToMessage {
|
||||
fn add_device(&self, device_id: &windows::core::PCWSTR) {
|
||||
self.sender
|
||||
.send(SMMessage::Device(*device_id))
|
||||
.unwrap_or_else(|_| println!("Failed to add new device"));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SMSessionNotifier {
|
||||
device_enumerator: IMMDeviceEnumerator,
|
||||
device_notification_client: IMMNotificationClient,
|
||||
session_notification: IAudioSessionNotification,
|
||||
session_managers: Vec<IAudioSessionManager2>,
|
||||
notification_function: Box<dyn Fn(IAudioSessionControl2)>,
|
||||
receiver: Receiver<SMMessage>,
|
||||
}
|
||||
|
||||
impl SMSessionNotifier {
|
||||
pub(crate) fn new(
|
||||
callback: Box<dyn Fn(IAudioSessionControl2)>,
|
||||
sender: mpsc::Sender<SMMessage>,
|
||||
receiver: mpsc::Receiver<SMMessage>,
|
||||
) -> SMSessionNotifier {
|
||||
SMSessionNotifier {
|
||||
session_managers: Vec::new(),
|
||||
device_enumerator: unsafe {
|
||||
CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL).unwrap()
|
||||
},
|
||||
device_notification_client: IMMNotificationClient::from(DeviceNotificationClient {
|
||||
observer: Box::new(DeviceToMessage {
|
||||
sender: sender.clone(),
|
||||
}),
|
||||
}),
|
||||
session_notification: IAudioSessionNotification::from(SessionNotification {
|
||||
observer: Box::new(SessionToMessage { sender }),
|
||||
}),
|
||||
notification_function: callback,
|
||||
receiver,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn boot_devices(self: &mut SMSessionNotifier) -> Result<(), Box<dyn Error>> {
|
||||
unsafe {
|
||||
self.device_enumerator
|
||||
.RegisterEndpointNotificationCallback(&self.device_notification_client)?;
|
||||
let device_collection = self
|
||||
.device_enumerator
|
||||
.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE)?;
|
||||
for device in (0..device_collection.GetCount()?).map(|x| device_collection.Item(x)) {
|
||||
let mmdevice = device?;
|
||||
self.add_device(mmdevice)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn add_device_by_id(self: &mut SMSessionNotifier, 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 SMSessionNotifier,
|
||||
device: IMMDevice,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let session_manager: IAudioSessionManager2 = device.Activate(CLSCTX_ALL, None)?;
|
||||
session_manager.RegisterSessionNotification(&self.session_notification)?;
|
||||
let session_enumerator = session_manager.GetSessionEnumerator()?;
|
||||
let session_count = session_enumerator.GetCount()?;
|
||||
let device_sessions = (0..session_count)
|
||||
.map(|idx| {
|
||||
session_enumerator
|
||||
.GetSession(idx)
|
||||
.and_then(|session| session.cast())
|
||||
})
|
||||
.collect::<Result<Vec<IAudioSessionControl2>, _>>()?;
|
||||
for session in device_sessions {
|
||||
(self.notification_function)(session);
|
||||
}
|
||||
let prop_store = device.OpenPropertyStore(STGM_READ)?;
|
||||
let mut prop_var = prop_store.GetValue(&PKEY_Device_FriendlyName)?;
|
||||
println!(
|
||||
"Device Added: {} Existing Sessions: {}",
|
||||
prop_var.to_string(),
|
||||
session_count
|
||||
);
|
||||
PropVariantClear(&mut prop_var)?;
|
||||
|
||||
self.session_managers.push(session_manager);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
self.boot_devices()?;
|
||||
|
||||
loop {
|
||||
let msg = self.receiver.recv()?;
|
||||
match msg {
|
||||
SMMessage::Session(session) => {
|
||||
(self.notification_function)(session);
|
||||
}
|
||||
SMMessage::Device(id) => {
|
||||
self.add_device_by_id(&id)?;
|
||||
}
|
||||
SMMessage::Exit() => break,
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SMSessionNotifier {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.session_managers
|
||||
.drain(..)
|
||||
.map(|x| x.UnregisterSessionNotification(&self.session_notification))
|
||||
.collect::<Result<(), _>>()
|
||||
.unwrap();
|
||||
self.device_enumerator
|
||||
.UnregisterEndpointNotificationCallback(&self.device_notification_client)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SMSessionNotifierThread {
|
||||
handle: Option<JoinHandle<()>>,
|
||||
sender: Sender<SMMessage>,
|
||||
}
|
||||
|
||||
impl SMSessionNotifierThread {
|
||||
pub fn new(s: Box<dyn Fn(IAudioSessionControl2) + Send>) -> SMSessionNotifierThread {
|
||||
let (sender, receiver) = mpsc::channel::<SMMessage>();
|
||||
SMSessionNotifierThread {
|
||||
sender: sender.clone(),
|
||||
handle: Some(thread::spawn(move || {
|
||||
let mut session_notifier = SMSessionNotifier::new(s, sender, receiver);
|
||||
session_notifier.run().unwrap();
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SMSessionNotifierThread {
|
||||
fn drop(&mut self) {
|
||||
if let Some(handle) = self.handle.take() {
|
||||
self.sender.send(SMMessage::Exit()).unwrap();
|
||||
handle.join().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
127
crates/auto_mute_lib/src/window_change.rs
Normal file
127
crates/auto_mute_lib/src/window_change.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use std::{
|
||||
sync::{atomic::AtomicU32, Arc, Mutex},
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
use windows::Win32::{
|
||||
Foundation::{HWND, LPARAM, WPARAM},
|
||||
System::Threading::GetCurrentThreadId,
|
||||
UI::{
|
||||
Accessibility::{SetWinEventHook, UnhookWinEvent, HWINEVENTHOOK},
|
||||
WindowsAndMessaging::{
|
||||
DispatchMessageW, GetForegroundWindow, GetMessageW, GetWindowThreadProcessId,
|
||||
PostThreadMessageW, TranslateMessage, EVENT_SYSTEM_FOREGROUND,
|
||||
EVENT_SYSTEM_MINIMIZEEND, MSG, WINEVENT_OUTOFCONTEXT, WINEVENT_SKIPOWNPROCESS, WM_QUIT,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::pid_to_exe::pid_to_exe_path;
|
||||
|
||||
type WinCallback = Box<dyn Fn(&str) + Send>;
|
||||
static WIN_CHANGE_CALLBACK: Mutex<Option<WinCallback>> = Mutex::new(None);
|
||||
|
||||
unsafe extern "system" fn win_event_proc(
|
||||
_hook: HWINEVENTHOOK,
|
||||
event: u32,
|
||||
_hwnd: HWND,
|
||||
_id_object: i32,
|
||||
_id_child: i32,
|
||||
_dw_event_thread: u32,
|
||||
_dwms_event_time: u32,
|
||||
) {
|
||||
if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND {
|
||||
let mut pid: u32 = 0;
|
||||
// Instead of using the hwnd passed to us in the wineventproc, call GetForegroundWindow again because Hollow Knight does something weird
|
||||
// and immediately fires again with the hwnd for explorer?
|
||||
let hwnd = GetForegroundWindow();
|
||||
GetWindowThreadProcessId(hwnd, Some(&mut pid));
|
||||
pid_to_exe_path(pid)
|
||||
.map(|path| WIN_CHANGE_CALLBACK.lock().unwrap().as_ref().unwrap()(&path))
|
||||
.unwrap_or_else(|err| {
|
||||
println!(
|
||||
"Error finding process with pid {} for hwnd: {:?}: {:?}",
|
||||
pid, hwnd, err
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn await_win_change_events(callback: WinCallback) {
|
||||
*WIN_CHANGE_CALLBACK.lock().unwrap() = Some(callback);
|
||||
unsafe {
|
||||
let fg_event = SetWinEventHook(
|
||||
EVENT_SYSTEM_FOREGROUND,
|
||||
EVENT_SYSTEM_FOREGROUND,
|
||||
None,
|
||||
Some(win_event_proc),
|
||||
0,
|
||||
0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||
);
|
||||
let min_event = SetWinEventHook(
|
||||
EVENT_SYSTEM_MINIMIZEEND,
|
||||
EVENT_SYSTEM_MINIMIZEEND,
|
||||
None,
|
||||
Some(win_event_proc),
|
||||
0,
|
||||
0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||
);
|
||||
|
||||
let mut msg: MSG = MSG::default();
|
||||
while GetMessageW(&mut msg, None, 0, 0).0 > 0 {
|
||||
TranslateMessage(&msg).unwrap();
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
UnhookWinEvent(fg_event).unwrap();
|
||||
UnhookWinEvent(min_event).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowChangeMonitor {
|
||||
join_handle: Option<JoinHandle<()>>,
|
||||
win_thread_id: Arc<AtomicU32>,
|
||||
}
|
||||
|
||||
impl Drop for WindowChangeMonitor {
|
||||
fn drop(&mut self) {
|
||||
if let Some(join_handle) = self.join_handle.take() {
|
||||
let tid: u32 = self
|
||||
.win_thread_id
|
||||
.load(std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
if tid != 0 {
|
||||
unsafe {
|
||||
PostThreadMessageW(tid, WM_QUIT, WPARAM(0), LPARAM(0))
|
||||
.ok()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
join_handle
|
||||
.join()
|
||||
.expect("Unable to terminate window change thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowChangeMonitor {
|
||||
pub(crate) fn start(f: WinCallback) -> WindowChangeMonitor {
|
||||
let win_thread_id = Arc::new(AtomicU32::new(0));
|
||||
let join_handle = {
|
||||
let win_thread_id = win_thread_id.clone();
|
||||
thread::spawn(move || {
|
||||
win_thread_id.store(
|
||||
unsafe { GetCurrentThreadId() },
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
await_win_change_events(f);
|
||||
})
|
||||
};
|
||||
WindowChangeMonitor {
|
||||
join_handle: Some(join_handle),
|
||||
win_thread_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
23
mute.txt
Normal file
23
mute.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
Baba is You.exe
|
||||
Celeste.exe
|
||||
Cemu.exe
|
||||
DarkSoulsII.exe
|
||||
GRIS.exe
|
||||
Hades.exe
|
||||
LWIW.exe
|
||||
Super Mario 64 Plus.exe
|
||||
Trials of Mana-Win64-Shipping.exe
|
||||
VampireSurvivors.exe
|
||||
cod.exe
|
||||
hollow_knight.exe
|
||||
sm64.us.f3dex2e.exe
|
||||
teardown.exe
|
||||
underrail.exe
|
||||
valheim.exe
|
||||
Balatro.exe
|
||||
Tactical Breach Wizards.exe
|
||||
eldenring.exe
|
||||
ACValhalla.exe
|
||||
DQMonsters3.exe
|
||||
Patrick's Parabox.exe
|
||||
Signal.exe
|
||||
BIN
res/muted.ico
Normal file
BIN
res/muted.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
BIN
res/unmuted.ico
Normal file
BIN
res/unmuted.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
230
src/main.rs
230
src/main.rs
@@ -1,230 +0,0 @@
|
||||
mod pid_to_exe;
|
||||
|
||||
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;
|
||||
|
||||
unsafe extern "system" fn win_event_proc(
|
||||
_hook: HWINEVENTHOOK,
|
||||
event: u32,
|
||||
hwnd: HWND,
|
||||
_id_object: i32,
|
||||
_id_child: i32,
|
||||
_dw_event_thread: u32,
|
||||
_dwms_event_time: u32,
|
||||
) {
|
||||
if event == EVENT_SYSTEM_FOREGROUND || event == EVENT_SYSTEM_MINIMIZEEND {
|
||||
let mut pid: u32 = 0;
|
||||
GetWindowThreadProcessId(hwnd, &mut pid);
|
||||
println!("{:?}", pid_to_exe_path(pid));
|
||||
}
|
||||
}
|
||||
|
||||
fn win_event_hook_loop() {
|
||||
unsafe {
|
||||
SetWinEventHook(
|
||||
EVENT_SYSTEM_FOREGROUND,
|
||||
EVENT_SYSTEM_FOREGROUND,
|
||||
HINSTANCE::default(),
|
||||
Some(win_event_proc),
|
||||
0,
|
||||
0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||
);
|
||||
SetWinEventHook(
|
||||
EVENT_SYSTEM_MINIMIZEEND,
|
||||
EVENT_SYSTEM_MINIMIZEEND,
|
||||
HINSTANCE::default(),
|
||||
Some(win_event_proc),
|
||||
0,
|
||||
0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS,
|
||||
);
|
||||
|
||||
let mut msg: MSG = MSG::default();
|
||||
while GetMessageW(&mut msg, HWND::default(), 0, 0).as_bool() {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#[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();
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
use std::error::Error;
|
||||
|
||||
use windows::Win32::{
|
||||
Foundation::MAX_PATH,
|
||||
System::Threading::{
|
||||
OpenProcess, QueryFullProcessImageNameW, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
|
||||
},
|
||||
};
|
||||
|
||||
pub unsafe fn pid_to_exe_path(pid: u32) -> Result<String, Box<dyn Error>> {
|
||||
let mut exe_name: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
|
||||
let mut size: u32 = exe_name.capacity().try_into().unwrap();
|
||||
let process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
|
||||
if !QueryFullProcessImageNameW(
|
||||
process?,
|
||||
Default::default(),
|
||||
windows::core::PWSTR(exe_name.as_mut_ptr()),
|
||||
&mut size,
|
||||
).as_bool()
|
||||
{
|
||||
return Err(Box::new(windows::core::Error::from_win32()));
|
||||
}
|
||||
exe_name.set_len(size.try_into().unwrap());
|
||||
let process_name = String::from_utf16_lossy(&exe_name);
|
||||
return Ok(process_name);
|
||||
}
|
||||
Reference in New Issue
Block a user