From 5655aaf4506fd449f19ed61a223f68116cf2fb8a Mon Sep 17 00:00:00 2001 From: sigoden Date: Tue, 9 Jun 2026 05:41:25 +0800 Subject: [PATCH] refactor: improve app icon retrieval with small2 fallback --- src/utils/app_icon.rs | 54 +++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/utils/app_icon.rs b/src/utils/app_icon.rs index eb709e4..5f2e25a 100644 --- a/src/utils/app_icon.rs +++ b/src/utils/app_icon.rs @@ -24,8 +24,8 @@ use windows::{ Shell::{SHGetFileInfoW, SHGetImageList, SHFILEINFOW, SHGFI_SYSICONINDEX}, WindowsAndMessaging::{ CopyIcon, CreateIconFromResourceEx, DestroyIcon, GetIconInfo, LoadIconW, - LoadImageW, SendMessageTimeoutW, GCLP_HICONSM, GCL_HICON, HICON, ICONINFO, - ICON_BIG, IDI_APPLICATION, IMAGE_ICON, LR_DEFAULTCOLOR, LR_DEFAULTSIZE, + LoadImageW, SendMessageTimeoutW, GCLP_HICON, HICON, ICONINFO, ICON_BIG, + ICON_SMALL2, IDI_APPLICATION, IMAGE_ICON, LR_DEFAULTCOLOR, LR_DEFAULTSIZE, LR_LOADFROMFILE, SMTO_ABORTIFHUNG, WM_GETICON, }, }, @@ -205,20 +205,27 @@ pub fn get_window_icon(hwnd: HWND) -> Option { return unsafe { CopyIcon(HICON(result as _)) }.ok(); } #[cfg(target_arch = "x86")] - let ret = unsafe { windows::Win32::UI::WindowsAndMessaging::GetClassLongW(hwnd, GCL_HICON) }; - #[cfg(not(target_arch = "x86"))] - let ret = unsafe { windows::Win32::UI::WindowsAndMessaging::GetClassLongPtrW(hwnd, GCL_HICON) }; - if ret != 0 { - return unsafe { CopyIcon(HICON(ret as _)) }.ok(); - } - #[cfg(target_arch = "x86")] - let ret = unsafe { windows::Win32::UI::WindowsAndMessaging::GetClassLongW(hwnd, GCLP_HICONSM) }; + let ret = unsafe { windows::Win32::UI::WindowsAndMessaging::GetClassLongW(hwnd, GCLP_HICON) }; #[cfg(not(target_arch = "x86"))] let ret = - unsafe { windows::Win32::UI::WindowsAndMessaging::GetClassLongPtrW(hwnd, GCLP_HICONSM) }; + unsafe { windows::Win32::UI::WindowsAndMessaging::GetClassLongPtrW(hwnd, GCLP_HICON) }; if ret != 0 { return unsafe { CopyIcon(HICON(ret as _)) }.ok(); } + let ret = unsafe { + SendMessageTimeoutW( + hwnd, + WM_GETICON, + WPARAM(ICON_SMALL2 as _), + LPARAM(0), + SMTO_ABORTIFHUNG, + 250, + Some(&mut result), + ) + }; + if ret.0 != 0 && result != 0 { + return unsafe { CopyIcon(HICON(result as _)) }.ok(); + } None } @@ -278,12 +285,17 @@ fn get_exe_icon(module_path: &str) -> Option { let info = get_shfileinfo(module_path)?; for shil in [SHIL_JUMBO, SHIL_EXTRALARGE, SHIL_LARGE] { unsafe { - let list = SHGetImageList::(shil).ok()?; - let hicon = list.GetIcon(info.iIcon, 1u32).ok()?; - if is_valid_icon(hicon) { - return Some(hicon); - } else { - let _ = DestroyIcon(hicon); + let Some(list) = SHGetImageList::(shil).ok() else { + continue; + }; + let Some(hicon) = list.GetIcon(info.iIcon, 1u32).ok() else { + continue; + }; + match is_valid_icon(hicon) { + Some(true) => return Some(hicon), + _ => { + let _ = DestroyIcon(hicon); + } } } } @@ -323,11 +335,9 @@ fn get_shfileinfo(module_path: &str) -> Option { /// while the rest is stretched/padded garbage — e.g. the icon of `hh.exe` /// (Windows's help viewer, reused by AutoHotKey, etc. to display their *.chm help). /// These look ugly in the switcher and are better replaced with a fallback icon. -pub fn is_valid_icon(hicon: HICON) -> bool { - let Some(bounds) = get_icon_bounds(hicon) else { - return false; - }; - !is_topleft_icon(&bounds) +fn is_valid_icon(hicon: HICON) -> Option { + let bounds = get_icon_bounds(hicon)?; + Some(!is_topleft_icon(&bounds)) } struct IconBounds {