//! 快捷位置:Windows 常用系统位置/工具。 //! //! 提供两类能力: //! - `get_special_locations`:返回预定义列表(hosts、回收站、此电脑、用户目录、系统管理工具), //! 路径在 Rust 侧解析(环境变量 / dirs crate),前端只负责展示与搜索。 //! - 打开逻辑:目录走 explorer.exe(比 ShellExecute "open" 目录更可靠), //! 文件走 ShellExecuteW(open + openas fallback),shell 位置与系统工具走 ShellExecuteW。 use std::path::PathBuf; use serde::Serialize; use specta::Type; /// 快捷位置条目 #[derive(Debug, Clone, Serialize, Type)] #[serde(rename_all = "camelCase")] pub struct SpecialLocation { pub id: String, pub title: String, pub subtitle: String, pub keywords: Vec, /// file: 真实文件/文件夹路径;shell: explorer 打开的 shell 路径;cmd: 可执行命令 pub kind: String, pub target: String, pub args: Vec, } fn sl( id: &str, title: &str, subtitle: &str, keywords: &[&str], kind: &str, target: &str, args: &[&str], ) -> SpecialLocation { SpecialLocation { id: id.to_string(), title: title.to_string(), subtitle: subtitle.to_string(), keywords: keywords.iter().map(|s| s.to_string()).collect(), kind: kind.to_string(), target: target.to_string(), args: args.iter().map(|s| s.to_string()).collect(), } } /// Windows 系统目录(%SystemRoot%) fn system_root() -> String { std::env::var("SystemRoot") .or_else(|_| std::env::var("windir")) .unwrap_or_else(|_| "C:\\Windows".to_string()) } fn dir_str(d: Option) -> Option { d.map(|p| p.to_string_lossy().to_string()) } /// 返回全部快捷位置(路径已在 Rust 侧解析,避免前端硬编码) pub fn get_special_locations() -> Vec { let mut list = Vec::new(); // ===== shell 位置(explorer 打开,CLSID 格式) ===== list.push(sl( "sp-recycle-bin", "回收站", "Windows 回收站", &["recycle", "bin", "trash", "回收站", "垃圾桶", "垃圾箱"], "shell", "::{645FF040-5081-101B-9F08-00AA002F954E}", &[], )); list.push(sl( "sp-this-pc", "此电脑", "我的电脑", &["此电脑", "我的电脑", "computer", "pc", "thispc"], "shell", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", &[], )); list.push(sl( "sp-network", "网络", "网络位置", &["网络", "network", "网上邻居"], "shell", "::{208D2C60-3AEA-1069-A2D7-08002B30309D}", &[], )); // ===== 常用文件 ===== let hosts = format!("{}\\System32\\drivers\\etc\\hosts", system_root()); list.push(sl( "sp-hosts", "hosts 文件", &hosts, &["hosts", "host", "主机文件", "域名映射"], "file", &hosts, &[], )); // ===== 用户目录(真实路径) ===== let user_dirs: &[(&str, &str, &[&str], Option)] = &[ ("sp-documents", "我的文档", &["文档", "我的文档", "documents", "docs"], dirs::document_dir()), ("sp-desktop", "桌面", &["桌面", "desktop"], dirs::desktop_dir()), ("sp-downloads", "下载", &["下载", "download", "downloads"], dirs::download_dir()), ("sp-pictures", "图片", &["图片", "照片", "pictures", "photos"], dirs::picture_dir()), ("sp-music", "音乐", &["音乐", "music"], dirs::audio_dir()), ("sp-videos", "视频", &["视频", "videos"], dirs::video_dir()), ]; for (id, title, keywords, path) in user_dirs { if let Some(p) = dir_str(path.clone()) { list.push(sl(id, title, &p, keywords, "file", &p, &[])); } } // ===== 系统管理工具(ShellExecuteW 直接启动 .msc/.cpl/.exe) ===== let tools: &[(&str, &str, &[&str], &str)] = &[ ("sp-devmgmt", "设备管理器", &["设备管理器", "设备", "devmgmt", "device"], "devmgmt.msc"), ("sp-diskmgmt", "磁盘管理", &["磁盘管理", "磁盘", "diskmgmt", "disk"], "diskmgmt.msc"), ("sp-compmgmt", "计算机管理", &["计算机管理", "compmgmt"], "compmgmt.msc"), ("sp-services", "服务", &["服务", "services", "service"], "services.msc"), ("sp-perfmon", "性能监视器", &["性能监视器", "性能", "perfmon"], "perfmon.msc"), ("sp-msinfo", "系统信息", &["系统信息", "msinfo", "systeminfo"], "msinfo32.exe"), ("sp-ncpa", "网络连接", &["网络连接", "ncpa", "网卡"], "ncpa.cpl"), ("sp-appwiz", "程序和功能", &["程序和功能", "卸载", "appwiz", "uninstall"], "appwiz.cpl"), ("sp-powercfg", "电源选项", &["电源选项", "电源", "powercfg", "power"], "powercfg.cpl"), ("sp-osk", "屏幕键盘", &["屏幕键盘", "键盘", "osk", "virtual keyboard"], "osk.exe"), ("sp-magnify", "放大镜", &["放大镜", "magnify", "magnifier"], "magnify.exe"), ]; for (id, title, keywords, exe) in tools { list.push(sl(id, title, exe, keywords, "cmd", exe, &[])); } list.push(sl( "sp-envvar", "环境变量", "rundll32 sysdm.cpl,EditEnvironmentVariables", &["环境变量", "环境", "env", "environment"], "cmd", "rundll32.exe", &["sysdm.cpl,EditEnvironmentVariables"], )); list } // ===== 打开逻辑 ===== /// ShellExecuteW 调用(verb 可指定,如 "open" / "openas") #[cfg(windows)] fn shell_execute_verb(exe: &str, params: &str, verb: &str) -> Result<(), String> { use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; use windows_sys::Win32::Foundation::HWND; use windows_sys::Win32::UI::Shell::ShellExecuteW; use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL; let wide_exe: Vec = OsStr::new(exe) .encode_wide() .chain(std::iter::once(0)) .collect(); let wide_params: Vec = OsStr::new(params) .encode_wide() .chain(std::iter::once(0)) .collect(); let wide_verb: Vec = OsStr::new(verb) .encode_wide() .chain(std::iter::once(0)) .collect(); unsafe { let hinst = ShellExecuteW( 0 as HWND, wide_verb.as_ptr(), wide_exe.as_ptr(), wide_params.as_ptr(), std::ptr::null(), SW_SHOWNORMAL, ); // ShellExecuteW 返回值 <= 32 表示错误 if hinst <= 32 { return Err(format!("打开失败 (code: {})", hinst as i32)); } } Ok(()) } #[cfg(not(windows))] fn shell_execute_verb(_exe: &str, _params: &str, _verb: &str) -> Result<(), String> { Err("当前平台不支持".into()) } /// 打开文件/文件夹路径: /// - 目录:explorer.exe 直接打开(比 ShellExecute "open" 目录更可靠,修复索引目录点击不生效问题) /// - 文件:ShellExecuteW open,无关联程序时 fallback 到「打开方式」对话框 pub fn open_path(path: &str) -> Result<(), String> { let p = std::path::Path::new(path); #[cfg(windows)] { if p.is_dir() { use crate::process_manager::setup_creation_flags; let mut cmd = std::process::Command::new("explorer.exe"); cmd.arg(path); setup_creation_flags(&mut cmd); return cmd .spawn() .map(|_| ()) .map_err(|e| format!("打开文件夹失败: {}", e)); } let params = String::new(); if shell_execute_verb(path, ¶ms, "open").is_err() { return shell_execute_verb(path, ¶ms, "openas"); } Ok(()) } #[cfg(not(windows))] { let _ = p; Err("当前平台不支持".into()) } } /// 打开 shell 位置(explorer shell: 或 ::{CLSID}) pub fn open_shell(target: &str) -> Result<(), String> { shell_execute_verb("explorer.exe", target, "open") } /// 打开系统工具(.msc / .cpl / .exe,参数空格连接) pub fn open_cmd(exe: &str, args: &[String]) -> Result<(), String> { let params = args.join(" "); shell_execute_verb(exe, ¶ms, "open") }