feat(热键工具): 1. 新增热键查询工具 2. 支持查询热键占用情况 3. 提供自动定位占用者和监控模式 4. 添加常见热键程序列表
This commit is contained in:
1 parent
55a308a927
commit
2f97391743
2 files changed
+508
No files matched your search
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "hotkey"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
winreg = "0.52"
|
||||||
|
windows = { version = "0.56", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_System_Registry", "Win32_System_SystemServices", "Win32_UI_Input_KeyboardAndMouse", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging"] }
|
||||||
@@ -0,0 +1,499 @@
|
|||||||
|
use clap::Parser;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::process::Command;
|
||||||
|
use windows::Win32::Foundation::{BOOL, HWND, LPARAM, TRUE};
|
||||||
|
use windows::Win32::System::Diagnostics::ToolHelp::{CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS};
|
||||||
|
use windows::Win32::UI::Input::KeyboardAndMouse::{MOD_ALT, MOD_CONTROL, MOD_SHIFT, MOD_WIN, RegisterHotKey, UnregisterHotKey};
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::{EnumWindows, GetWindowThreadProcessId, IsWindowVisible};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(name = "hotkey")]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
#[command(
|
||||||
|
about = "⌨️ 热键查询工具 - 查看当前用户下的热键配置",
|
||||||
|
long_about = "⌨️ 热键查询工具\n\n查看当前用户下注册的热键配置,以及查询特定热键被哪个程序占用。\n\n示例:\n hotkey - 列出所有常见热键的占用情况\n hotkey alt+q - 查询 Alt+Q 被哪个程序占用\n hotkey alt+q --find - 自动逐个关闭候选程序,精确定位占用者"
|
||||||
|
)]
|
||||||
|
struct Args {
|
||||||
|
/// 查询特定热键,格式如: alt+q, ctrl+c, win+e
|
||||||
|
#[arg(value_name = "HOTKEY")]
|
||||||
|
hotkey: Option<String>,
|
||||||
|
|
||||||
|
/// 自动逐个关闭候选程序来精确定位占用者
|
||||||
|
#[arg(short, long)]
|
||||||
|
find: bool,
|
||||||
|
|
||||||
|
/// 持续监控热键状态,直到热键释放
|
||||||
|
#[arg(short, long)]
|
||||||
|
watch: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常见注册全局热键的程序(按可能性排序)
|
||||||
|
const KNOWN_HOTKEY_APPS: &[&str] = &[
|
||||||
|
// 聊天软件
|
||||||
|
"Weixin.exe", "WeChatAppEx.exe", "QQ.exe", "TIM.exe", "DingTalk.exe", "AliWorkbench.exe",
|
||||||
|
// 快捷键工具
|
||||||
|
"AutoHotkey.exe", "AutoHotkey64.exe", "AutoHotkeyA32.exe", "AutoHotkeyU32.exe", "AutoHotkeyU64.exe",
|
||||||
|
"PowerToys.exe", "PowerToys.QuickAccess.exe", "PowerToys.KeyboardManagerEngine.exe",
|
||||||
|
"Listary.exe", "Wox.exe", "uelauncher.exe",
|
||||||
|
// 截图/录屏
|
||||||
|
"Snipaste.exe", "ShareX.exe", "obs64.exe", "DingTalkDingCapture.exe", "SunloginClient.exe",
|
||||||
|
// 输入法
|
||||||
|
"SogouCloud.exe", "ctfmon.exe", "ChsIME.exe", "TextInputHost.exe",
|
||||||
|
// 翻译
|
||||||
|
"Doubao.exe", "YoudaoDict.exe", "Lingoes.exe",
|
||||||
|
// 系统工具
|
||||||
|
"Nahimic3.exe", "RTSS.exe", "LogiBolt.exe", "GamingServices.exe",
|
||||||
|
// 远程控制
|
||||||
|
"ToDesk.exe", "rustdesk.exe", "AnyDesk.exe",
|
||||||
|
// 其他
|
||||||
|
"GestureSign.exe", "StrokesPlus.xml", "MultiCommander.exe",
|
||||||
|
];
|
||||||
|
|
||||||
|
fn parse_hotkey(input: &str) -> Option<(u32, u32)> {
|
||||||
|
let parts: Vec<&str> = input.split('+').collect();
|
||||||
|
if parts.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut modifiers = 0u32;
|
||||||
|
let mut vk_code = 0u32;
|
||||||
|
|
||||||
|
for part in parts {
|
||||||
|
let part = part.trim().to_lowercase();
|
||||||
|
match part.as_str() {
|
||||||
|
"alt" => modifiers |= MOD_ALT.0,
|
||||||
|
"ctrl" | "control" => modifiers |= MOD_CONTROL.0,
|
||||||
|
"shift" => modifiers |= MOD_SHIFT.0,
|
||||||
|
"win" | "meta" | "super" => modifiers |= MOD_WIN.0,
|
||||||
|
key => {
|
||||||
|
if key.len() == 1 {
|
||||||
|
let c = key.chars().next().unwrap();
|
||||||
|
vk_code = if c.is_ascii_alphabetic() {
|
||||||
|
c.to_ascii_uppercase() as u32
|
||||||
|
} else if c.is_ascii_digit() {
|
||||||
|
c as u32
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
vk_code = match &*key {
|
||||||
|
"f1" => 0x70, "f2" => 0x71, "f3" => 0x72, "f4" => 0x73,
|
||||||
|
"f5" => 0x74, "f6" => 0x75, "f7" => 0x76, "f8" => 0x77,
|
||||||
|
"f9" => 0x78, "f10" => 0x79, "f11" => 0x7A, "f12" => 0x7B,
|
||||||
|
"space" => 0x20, "enter" => 0x0D, "tab" => 0x09,
|
||||||
|
"backspace" => 0x08, "delete" => 0x2E, "insert" => 0x2D,
|
||||||
|
"home" => 0x24, "end" => 0x23, "pageup" => 0x21, "pagedown" => 0x22,
|
||||||
|
"left" => 0x25, "right" => 0x27, "up" => 0x26, "down" => 0x28,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vk_code == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((modifiers, vk_code))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_hotkey_occupied(modifiers: u32, vk_code: u32) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let result = RegisterHotKey(
|
||||||
|
HWND(0),
|
||||||
|
1,
|
||||||
|
windows::Win32::UI::Input::KeyboardAndMouse::HOT_KEY_MODIFIERS(modifiers),
|
||||||
|
vk_code,
|
||||||
|
);
|
||||||
|
if result.is_err() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let _ = UnregisterHotKey(HWND(0), 1);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hotkey_string(modifiers: u32, vk_code: u32) -> String {
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
if (modifiers & MOD_ALT.0) != 0 { parts.push(String::from("Alt")); }
|
||||||
|
if (modifiers & MOD_CONTROL.0) != 0 { parts.push(String::from("Ctrl")); }
|
||||||
|
if (modifiers & MOD_SHIFT.0) != 0 { parts.push(String::from("Shift")); }
|
||||||
|
if (modifiers & MOD_WIN.0) != 0 { parts.push(String::from("Win")); }
|
||||||
|
|
||||||
|
let key = if vk_code >= 0x41 && vk_code <= 0x5A {
|
||||||
|
(vk_code as u8 as char).to_string()
|
||||||
|
} else if vk_code >= 0x30 && vk_code <= 0x39 {
|
||||||
|
(vk_code as u8 as char).to_string()
|
||||||
|
} else if vk_code >= 0x70 && vk_code <= 0x7B {
|
||||||
|
format!("F{}", vk_code - 0x6F)
|
||||||
|
} else {
|
||||||
|
match vk_code {
|
||||||
|
0x20 => "Space", 0x0D => "Enter", 0x09 => "Tab", 0x08 => "Backspace",
|
||||||
|
0x2E => "Delete", 0x2D => "Insert", 0x24 => "Home", 0x23 => "End",
|
||||||
|
0x21 => "PageUp", 0x22 => "PageDown", 0x25 => "Left", 0x27 => "Right",
|
||||||
|
0x26 => "Up", 0x28 => "Down", _ => "",
|
||||||
|
}.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
parts.push(key);
|
||||||
|
parts.join("+")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct ProcessEntry {
|
||||||
|
pid: u32,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_visible_window_processes() -> Vec<ProcessEntry> {
|
||||||
|
let mut processes = Vec::new();
|
||||||
|
let mut seen_pids: HashSet<u32> = HashSet::new();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let data = (&mut processes, &mut seen_pids);
|
||||||
|
let data_ptr = &data as *const _ as *mut ();
|
||||||
|
let _ = EnumWindows(Some(enum_windows_callback), LPARAM(data_ptr as isize));
|
||||||
|
}
|
||||||
|
|
||||||
|
processes.sort_by(|a: &ProcessEntry, b: &ProcessEntry| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||||
|
processes.dedup_by(|a, b| a.name == b.name);
|
||||||
|
processes
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn enum_windows_callback(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||||
|
unsafe {
|
||||||
|
if !IsWindowVisible(hwnd).as_bool() {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = &mut *(lparam.0 as *mut (&mut Vec<ProcessEntry>, &mut HashSet<u32>));
|
||||||
|
let (processes, seen_pids) = data;
|
||||||
|
|
||||||
|
let mut pid = 0u32;
|
||||||
|
GetWindowThreadProcessId(hwnd, Some(&mut pid));
|
||||||
|
|
||||||
|
if seen_pids.contains(&pid) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取进程名
|
||||||
|
let snapshot = match CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => return TRUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut pe32 = PROCESSENTRY32 {
|
||||||
|
dwSize: std::mem::size_of::<PROCESSENTRY32>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
if Process32First(snapshot, &mut pe32).is_ok() {
|
||||||
|
loop {
|
||||||
|
if pe32.th32ProcessID == pid {
|
||||||
|
let len = pe32.szExeFile.iter().position(|&c| c == 0).unwrap_or(pe32.szExeFile.len());
|
||||||
|
let name = String::from_utf8_lossy(std::slice::from_raw_parts(
|
||||||
|
pe32.szExeFile.as_ptr() as *const u8,
|
||||||
|
len,
|
||||||
|
)).to_string();
|
||||||
|
if !name.is_empty() {
|
||||||
|
processes.push(ProcessEntry { pid, name });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if Process32Next(snapshot, &mut pe32).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seen_pids.insert(pid);
|
||||||
|
}
|
||||||
|
TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_known_hotkey_app(name: &str) -> bool {
|
||||||
|
KNOWN_HOTKEY_APPS.iter().any(|&app| name.eq_ignore_ascii_case(app))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill_process(pid: u32) -> bool {
|
||||||
|
Command::new("taskkill")
|
||||||
|
.args(["/PID", &pid.to_string(), "/F"])
|
||||||
|
.output()
|
||||||
|
.map(|o| o.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_process(name: &str) -> bool {
|
||||||
|
Command::new("cmd")
|
||||||
|
.args(["/C", "start", "", name])
|
||||||
|
.output()
|
||||||
|
.map(|o| o.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_hotkey_owner(modifiers: u32, vk_code: u32) {
|
||||||
|
let hotkey_display = get_hotkey_string(modifiers, vk_code);
|
||||||
|
|
||||||
|
if !check_hotkey_occupied(modifiers, vk_code) {
|
||||||
|
println!("✅ 热键 {} 未被占用", hotkey_display);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("🔍 正在查找占用 {} 的程序...\n", hotkey_display);
|
||||||
|
println!("策略: 逐个关闭候选程序,每次关闭后检查热键是否释放");
|
||||||
|
println!(" 如果释放,则该程序就是占用者,会自动重启它\n");
|
||||||
|
|
||||||
|
// 构建候选列表:已知热键程序优先
|
||||||
|
let all_procs = get_visible_window_processes();
|
||||||
|
let mut candidates: Vec<ProcessEntry> = Vec::new();
|
||||||
|
let mut others: Vec<ProcessEntry> = Vec::new();
|
||||||
|
|
||||||
|
for proc in all_procs {
|
||||||
|
// 跳过自身和关键系统进程
|
||||||
|
if proc.name.eq_ignore_ascii_case("hotkey.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("explorer.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("svchost.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("csrss.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("lsass.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("services.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("smss.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("wininit.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("winlogon.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("dwm.exe")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("System")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("Registry")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("Memory Compression")
|
||||||
|
|| proc.name.eq_ignore_ascii_case("Secure System")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_known_hotkey_app(&proc.name) {
|
||||||
|
candidates.push(proc);
|
||||||
|
} else {
|
||||||
|
others.push(proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已知热键程序排在前面
|
||||||
|
candidates.extend(others);
|
||||||
|
|
||||||
|
if candidates.is_empty() {
|
||||||
|
println!("❌ 没有找到可测试的候选程序");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("📋 候选程序列表(已知热键程序优先):");
|
||||||
|
for (i, proc) in candidates.iter().enumerate() {
|
||||||
|
let tag = if is_known_hotkey_app(&proc.name) { " [已知热键程序]" } else { "" };
|
||||||
|
println!(" {}. {} (PID: {}){}", i + 1, proc.name, proc.pid, tag);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut closed_apps: Vec<(u32, String)> = Vec::new();
|
||||||
|
let mut found = false;
|
||||||
|
|
||||||
|
for (i, proc) in candidates.iter().enumerate() {
|
||||||
|
let tag = if is_known_hotkey_app(&proc.name) { " [已知热键程序]" } else { "" };
|
||||||
|
println!("[{}/{}] 正在关闭 {} (PID: {}){}...", i + 1, candidates.len(), proc.name, proc.pid, tag);
|
||||||
|
|
||||||
|
if !kill_process(proc.pid) {
|
||||||
|
println!(" ⚠️ 无法关闭,跳过");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待进程完全退出
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
|
||||||
|
if !check_hotkey_occupied(modifiers, vk_code) {
|
||||||
|
println!("\n🎯 找到了!占用 {} 的程序是: {} (PID: {})", hotkey_display, proc.name, proc.pid);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
println!(" ❌ 关闭后热键仍被占用,继续...");
|
||||||
|
closed_apps.push((proc.pid, proc.name.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重启已关闭的程序
|
||||||
|
if !closed_apps.is_empty() {
|
||||||
|
println!("\n♻️ 正在重启之前关闭的程序...");
|
||||||
|
for (_, name) in &closed_apps {
|
||||||
|
if start_process(name) {
|
||||||
|
println!(" ✅ 已重启 {}", name);
|
||||||
|
} else {
|
||||||
|
println!(" ⚠️ 无法重启 {}", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
println!("\n❌ 未能找到占用者。可能被系统服务或驱动级程序占用。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch_hotkey(modifiers: u32, vk_code: u32) {
|
||||||
|
let hotkey_display = get_hotkey_string(modifiers, vk_code);
|
||||||
|
|
||||||
|
if !check_hotkey_occupied(modifiers, vk_code) {
|
||||||
|
println!("✅ 热键 {} 未被占用", hotkey_display);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("👁️ 正在监控热键 {} 的状态...", hotkey_display);
|
||||||
|
println!(" 关闭占用的程序后,将自动检测到释放\n");
|
||||||
|
|
||||||
|
let mut last_procs = get_visible_window_processes();
|
||||||
|
loop {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||||
|
|
||||||
|
if !check_hotkey_occupied(modifiers, vk_code) {
|
||||||
|
// 检查哪些进程消失了
|
||||||
|
let current_procs = get_visible_window_processes();
|
||||||
|
let current_names: HashSet<String> = current_procs.iter().map(|p| p.name.clone()).collect();
|
||||||
|
let disappeared: Vec<&ProcessEntry> = last_procs.iter()
|
||||||
|
.filter(|p| !current_names.contains(&p.name))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("\n🎯 热键 {} 已释放!", hotkey_display);
|
||||||
|
if !disappeared.is_empty() {
|
||||||
|
println!("📋 最近关闭的程序(可能的占用者):");
|
||||||
|
for proc in disappeared {
|
||||||
|
println!(" - {} (PID: {})", proc.name, proc.pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进程列表
|
||||||
|
last_procs = get_visible_window_processes();
|
||||||
|
print!(".");
|
||||||
|
std::io::Write::flush(&mut std::io::stdout()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_common_hotkeys() -> Vec<(String, bool)> {
|
||||||
|
let common_hotkeys = [
|
||||||
|
(MOD_ALT.0 | MOD_CONTROL.0, 0x44, "Alt+Ctrl+D"),
|
||||||
|
(MOD_ALT.0, 0x51, "Alt+Q"),
|
||||||
|
(MOD_ALT.0, 0x46, "Alt+F"),
|
||||||
|
(MOD_ALT.0, 0x43, "Alt+C"),
|
||||||
|
(MOD_CONTROL.0, 0x43, "Ctrl+C"),
|
||||||
|
(MOD_CONTROL.0, 0x56, "Ctrl+V"),
|
||||||
|
(MOD_CONTROL.0, 0x58, "Ctrl+X"),
|
||||||
|
(MOD_CONTROL.0, 0x46, "Ctrl+F"),
|
||||||
|
(MOD_CONTROL.0, 0x53, "Ctrl+S"),
|
||||||
|
(MOD_CONTROL.0, 0x4E, "Ctrl+N"),
|
||||||
|
(MOD_CONTROL.0, 0x57, "Ctrl+W"),
|
||||||
|
(MOD_CONTROL.0, 0x54, "Ctrl+T"),
|
||||||
|
(MOD_WIN.0, 0x45, "Win+E"),
|
||||||
|
(MOD_WIN.0, 0x44, "Win+D"),
|
||||||
|
(MOD_WIN.0, 0x52, "Win+R"),
|
||||||
|
(MOD_WIN.0, 0x4C, "Win+L"),
|
||||||
|
(MOD_WIN.0, 0x54, "Win+T"),
|
||||||
|
(MOD_WIN.0, 0x41, "Win+A"),
|
||||||
|
(MOD_WIN.0, 0x53, "Win+S"),
|
||||||
|
(MOD_ALT.0, 0x42, "Alt+B"),
|
||||||
|
(MOD_ALT.0, 0x54, "Alt+T"),
|
||||||
|
(MOD_ALT.0, 0x56, "Alt+V"),
|
||||||
|
(MOD_ALT.0, 0x57, "Alt+W"),
|
||||||
|
(MOD_CONTROL.0 | MOD_SHIFT.0, 0x43, "Ctrl+Shift+C"),
|
||||||
|
(MOD_CONTROL.0 | MOD_SHIFT.0, 0x56, "Ctrl+Shift+V"),
|
||||||
|
(MOD_CONTROL.0, 0x41, "Ctrl+A"),
|
||||||
|
(MOD_CONTROL.0, 0x4B, "Ctrl+K"),
|
||||||
|
(MOD_ALT.0, 0x53, "Alt+S"),
|
||||||
|
(MOD_ALT.0, 0x4E, "Alt+N"),
|
||||||
|
(MOD_ALT.0, 0x50, "Alt+P"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut results: Vec<(String, bool)> = common_hotkeys.iter().map(|&(m, v, name)| {
|
||||||
|
(name.to_string(), check_hotkey_occupied(m, v))
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
results.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
if let Some(hotkey_str) = &args.hotkey {
|
||||||
|
match parse_hotkey(hotkey_str) {
|
||||||
|
Some((modifiers, vk_code)) => {
|
||||||
|
let hotkey_display = get_hotkey_string(modifiers, vk_code);
|
||||||
|
|
||||||
|
if args.find {
|
||||||
|
find_hotkey_owner(modifiers, vk_code);
|
||||||
|
} else if args.watch {
|
||||||
|
watch_hotkey(modifiers, vk_code);
|
||||||
|
} else {
|
||||||
|
let occupied = check_hotkey_occupied(modifiers, vk_code);
|
||||||
|
|
||||||
|
if occupied {
|
||||||
|
println!("❌ 热键 {} 已被占用", hotkey_display);
|
||||||
|
|
||||||
|
let procs = get_visible_window_processes();
|
||||||
|
|
||||||
|
// 优先显示已知热键程序
|
||||||
|
let mut known = Vec::new();
|
||||||
|
let mut other = Vec::new();
|
||||||
|
for proc in &procs {
|
||||||
|
if is_known_hotkey_app(&proc.name) {
|
||||||
|
known.push(proc);
|
||||||
|
} else {
|
||||||
|
other.push(proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !known.is_empty() {
|
||||||
|
println!("\n🔴 高度可疑(已知会注册热键的程序):");
|
||||||
|
for (i, proc) in known.iter().enumerate() {
|
||||||
|
println!(" {}. {} (PID: {})", i + 1, proc.name, proc.pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !other.is_empty() {
|
||||||
|
println!("\n🟡 其他运行中的程序:");
|
||||||
|
for (i, proc) in other.iter().enumerate() {
|
||||||
|
println!(" {}. {} (PID: {})", i + 1, proc.name, proc.pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n💡 使用以下命令精确定位:");
|
||||||
|
println!(" hotkey {} --find 自动逐个关闭程序定位占用者(会自动重启)", hotkey_str);
|
||||||
|
println!(" hotkey {} --watch 持续监控,手动关闭程序后自动检测", hotkey_str);
|
||||||
|
} else {
|
||||||
|
println!("✅ 热键 {} 未被占用,可以使用", hotkey_display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("❌ 无效的热键格式: {}", hotkey_str);
|
||||||
|
eprintln!("💡 正确格式示例: alt+q, ctrl+c, win+e, alt+ctrl+delete");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("⌨️ 热键查询工具\n");
|
||||||
|
println!("正在检测当前系统中的热键占用情况...\n");
|
||||||
|
|
||||||
|
let hotkeys = check_common_hotkeys();
|
||||||
|
|
||||||
|
println!("{:<30} {:<12}", "热键", "状态");
|
||||||
|
println!("{}", "-".repeat(45));
|
||||||
|
|
||||||
|
for (key, occupied) in hotkeys {
|
||||||
|
let status = if occupied { "🔴 已占用" } else { "🟢 可用" };
|
||||||
|
println!("{:<30} {:<12}", key, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n💡 提示:");
|
||||||
|
println!(" hotkey <热键> 查询特定热键占用情况");
|
||||||
|
println!(" hotkey <热键> --find 自动定位占用者(逐个关闭后检测,会自动重启)");
|
||||||
|
println!(" hotkey <热键> --watch 监控模式(手动关闭程序后自动检测释放)");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in new issue
Block a user