From aada05a1fc6c6fa8f265663ac9f024b82dbd83f3 Mon Sep 17 00:00:00 2001 From: ASLant <2041438004@qq.com> Date: Wed, 12 Nov 2025 09:56:58 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0Windows=E6=A1=8C?= =?UTF-8?q?=E9=9D=A2=E5=9B=BE=E6=A0=87=E7=BC=93=E5=AD=98=E6=B8=85=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/main.rs | 115 +++++++++++++++++++++++++++++------------- ui/index.html | 13 +++++ ui/js/main.js | 25 ++++++++- 3 files changed, 117 insertions(+), 36 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 73ab334..7f310f2 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,6 +7,12 @@ use std::path::{Path, PathBuf}; use std::fs; use encoding_rs::GBK; +#[cfg(target_os = "windows")] +use std::os::windows::process::CommandExt; + +#[cfg(target_os = "windows")] +const CREATE_NO_WINDOW: u32 = 0x08000000; + #[derive(Debug, Serialize, Deserialize)] struct UpxOptions { mode: String, @@ -15,6 +21,7 @@ struct UpxOptions { compression_level: String, backup: bool, ultra_brute: bool, + force: bool, } #[derive(Debug, Serialize, Deserialize)] @@ -24,53 +31,42 @@ struct ScanFolderOptions { } // 获取UPX可执行文件路径 -fn get_upx_path() -> PathBuf { - // 尝试1: 使用打包后的upx (在exe同目录下) +fn get_upx_path() -> Option { + // 打包后的位置 if let Ok(exe_path) = std::env::current_exe() { if let Some(exe_dir) = exe_path.parent() { - let bundled_upx = exe_dir.join("upx.exe"); - if bundled_upx.exists() { - return bundled_upx; + let upx = exe_dir.join("_up_/upx/upx.exe"); + if upx.exists() { + return Some(upx); } } } - // 尝试2: 使用开发环境的upx (src-tauri/../upx/upx.exe) - let dev_upx = PathBuf::from("../../upx/upx.exe"); + // 开发环境 + let dev_upx = PathBuf::from("../upx/upx.exe"); if dev_upx.exists() { - return dev_upx; + return Some(dev_upx); } - // 尝试3: 另一个开发环境路径 - let dev_upx2 = PathBuf::from("../upx/upx.exe"); - if dev_upx2.exists() { - return dev_upx2; - } - - // 尝试4: 使用系统PATH中的upx (降级方案) - PathBuf::from("upx") + None } #[tauri::command] async fn process_upx(options: UpxOptions) -> Result { // 获取UPX可执行文件路径 - let upx_path = get_upx_path(); + let upx_path = get_upx_path().ok_or("未找到 UPX 工具!请确保安装完整")?; // 检查 UPX 是否可用 let mut upx_check = Command::new(&upx_path); // 在 Windows 上隐藏控制台窗口 #[cfg(target_os = "windows")] - { - use std::os::windows::process::CommandExt; - const CREATE_NO_WINDOW: u32 = 0x08000000; - upx_check.creation_flags(CREATE_NO_WINDOW); - } + upx_check.creation_flags(CREATE_NO_WINDOW); let upx_check = upx_check.arg("--version").output(); if upx_check.is_err() { - return Err("未找到 UPX 工具!".to_string()); + return Err("UPX 工具无法执行!".to_string()); } // 检查输入文件是否存在 @@ -101,11 +97,7 @@ async fn process_upx(options: UpxOptions) -> Result { // 在 Windows 上隐藏控制台窗口 #[cfg(target_os = "windows")] - { - use std::os::windows::process::CommandExt; - const CREATE_NO_WINDOW: u32 = 0x08000000; - cmd.creation_flags(CREATE_NO_WINDOW); - } + cmd.creation_flags(CREATE_NO_WINDOW); // 判断是否覆盖原文件 let is_overwrite = options.input_file == options.output_file; @@ -122,6 +114,11 @@ async fn process_upx(options: UpxOptions) -> Result { cmd.arg(format!("-{}", options.compression_level)); } + // 添加强制压缩参数 + if options.force { + cmd.arg("--force"); + } + if is_overwrite { // 直接覆盖原文件,不使用 -o 参数 cmd.arg(&options.input_file); @@ -137,6 +134,11 @@ async fn process_upx(options: UpxOptions) -> Result { // 脱壳模式 cmd.arg("-d"); + // 添加强制压缩参数(脱壳时也可能需要) + if options.force { + cmd.arg("--force"); + } + if is_overwrite { // 直接覆盖原文件 cmd.arg(&options.input_file); @@ -267,17 +269,13 @@ fn scan_folder(options: ScanFolderOptions) -> Result, String> { // 获取UPX版本 #[tauri::command] fn get_upx_version() -> Result { - let upx_path = get_upx_path(); + let upx_path = get_upx_path().ok_or("未找到 UPX 工具!")?; let mut cmd = Command::new(&upx_path); // 在 Windows 上隐藏控制台窗口 #[cfg(target_os = "windows")] - { - use std::os::windows::process::CommandExt; - const CREATE_NO_WINDOW: u32 = 0x08000000; - cmd.creation_flags(CREATE_NO_WINDOW); - } + cmd.creation_flags(CREATE_NO_WINDOW); match cmd.arg("--version").output() { Ok(output) => { @@ -293,11 +291,58 @@ fn get_upx_version() -> Result { } } +// 刷新图标缓存 +#[tauri::command] +#[allow(dead_code)] +async fn refresh_icon_cache() -> Result<(), String> { + // 后台执行刷新 + tokio::task::spawn_blocking(move || { + #[cfg(target_os = "windows")] + { + // 1. 关闭 Explorer + let _ = Command::new("taskkill") + .args(&["/f", "/im", "explorer.exe"]) + .creation_flags(CREATE_NO_WINDOW) + .output(); + + std::thread::sleep(std::time::Duration::from_millis(500)); + + // 2. 删除图标缓存 + if let Ok(userprofile) = std::env::var("USERPROFILE") { + let cache_db = format!("{}\\AppData\\Local\\IconCache.db", userprofile); + let _ = fs::remove_file(&cache_db); + + // 删除缩略图缓存 + let explorer_path = format!("{}\\AppData\\Local\\Microsoft\\Windows\\Explorer", userprofile); + if let Ok(entries) = fs::read_dir(&explorer_path) { + for entry in entries.flatten() { + let path = entry.path(); + if let Some(name) = path.file_name().and_then(|n| n.to_str()) { + if name.starts_with("thumbcache_") { + let _ = fs::remove_file(&path); + } + } + } + } + } + + std::thread::sleep(std::time::Duration::from_millis(500)); + + // 3. 重启 Explorer + let _ = Command::new("explorer.exe") + .creation_flags(CREATE_NO_WINDOW) + .spawn(); + } + }); + + Ok(()) +} + fn main() { tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) - .invoke_handler(tauri::generate_handler![process_upx, scan_folder, get_upx_version]) + .invoke_handler(tauri::generate_handler![process_upx, scan_folder, get_upx_version, refresh_icon_cache]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/ui/index.html b/ui/index.html index 184a4f0..9960932 100644 --- a/ui/index.html +++ b/ui/index.html @@ -16,6 +16,11 @@
+
+ + diff --git a/ui/js/main.js b/ui/js/main.js index 52d782e..ad2d20a 100644 --- a/ui/js/main.js +++ b/ui/js/main.js @@ -13,10 +13,12 @@ const overwriteCheckbox = document.getElementById('overwrite'); const backupCheckbox = document.getElementById('backup'); const ultraBruteCheckbox = document.getElementById('ultra-brute'); const includeSubfoldersCheckbox = document.getElementById('include-subfolders'); +const forceCompressCheckbox = document.getElementById('force-compress'); const logOutput = document.getElementById('log-output'); const settingsModal = document.getElementById('settings-modal'); const settingsBtn = document.getElementById('settings-btn'); const closeSettingsBtn = document.getElementById('close-settings'); +const refreshIconBtn = document.getElementById('refresh-icon-btn'); // 窗口控制按钮 const minimizeBtn = document.getElementById('titlebar-minimize'); @@ -84,6 +86,11 @@ function initWindowControls() { // 操作按钮初始化 function initOperationButtons() { + // 刷新图标缓存按钮 + refreshIconBtn.addEventListener('click', async () => { + await handleRefreshIcon(); + }); + // 设置按钮 - 显示全局设置 settingsBtn.addEventListener('click', () => { showSettingsModal(); @@ -496,12 +503,17 @@ async function processUpx(mode, inputFile, outputFile) { output_file: outputFile, compression_level: getCompressionLevel(), backup: backupCheckbox.checked, - ultra_brute: ultraBruteCheckbox.checked + ultra_brute: ultraBruteCheckbox.checked, + force: forceCompressCheckbox.checked }; if (ultraBruteCheckbox.checked) { addLog('已启用极限压缩模式', 'info'); } + + if (forceCompressCheckbox.checked) { + addLog('已启用强制压缩模式', 'warning'); + } addLog(`开始${mode === 'compress' ? '加壳压缩' : '脱壳解压'}...`, 'info'); @@ -515,6 +527,17 @@ async function processUpx(mode, inputFile, outputFile) { } } +// 刷新图标缓存 +async function handleRefreshIcon() { + try { + addLog('正在刷新图标缓存...', 'info'); + await invoke('refresh_icon_cache'); + addLog('图标缓存刷新完成', 'success'); + } catch (error) { + addLog(`刷新失败: ${error}`, 'error'); + } +} + // 添加日志 function addLog(message, type = 'info', highlight = false) { const logLine = document.createElement('div');