在Rust中从EXE提取图标,如何获取Vec<RgbaImage<u8>>集合?
从EXE文件提取图标并生成Vec<RgbaImage>集合
核心依赖
需要在Cargo.toml中添加以下依赖:
[dependencies] winapi = { version = "0.3", features = ["winbase", "wingdi", "winuser", "libloaderapi"] } image = "0.24" bevy_math = "0.12" # 根据你使用的Bevy版本调整
实现步骤与代码示例
1. 加载EXE并枚举图标资源
通过Windows API加载目标EXE,枚举其中的图标资源并获取图标句柄:
use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; use winapi::{ shared::minwindef::{FALSE, HMODULE, UINT, TRUE}, um::{ libloaderapi::{FreeLibrary, LoadLibraryExW}, winbase::{LOAD_LIBRARY_AS_DATAFILE, LOAD_LIBRARY_AS_IMAGE_RESOURCE}, winuser::{CreateIconFromResourceEx, DestroyIcon, EnumResourceNamesW, FindResourceW, HICON, LR_DEFAULTCOLOR}, winbase::{LockResource, LoadResource, SizeofResource}, }, }; fn load_exe_icons(exe_path: &str) -> Vec<HICON> { let wide_path: Vec<u16> = OsStr::new(exe_path).encode_wide().chain(std::iter::once(0)).collect(); let h_module = unsafe { LoadLibraryExW(wide_path.as_ptr(), std::ptr::null_mut(), LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE) }; if h_module.is_null() { return Vec::new(); } let mut icons = Vec::new(); unsafe { EnumResourceNamesW( h_module, winapi::um::winuser::RT_GROUP_ICON, Some(enum_icon_resources), &mut icons as *mut Vec<HICON> as _, ); FreeLibrary(h_module); } icons } unsafe extern "system" fn enum_icon_resources( h_module: HMODULE, _type: *const u16, name: *const u16, lparam: UINT, ) -> BOOL { let icons = &mut *(lparam as *mut Vec<HICON>); let h_res_info = FindResourceW(h_module, name, winapi::um::winuser::RT_GROUP_ICON); if h_res_info.is_null() { return TRUE; } let h_res_data = LoadResource(h_module, h_res_info); if h_res_data.is_null() { return TRUE; } let res_data = LockResource(h_res_data) as *const u8; let res_size = SizeofResource(h_module, h_res_info); let h_icon = CreateIconFromResourceEx( res_data, res_size as UINT, TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR, ); if !h_icon.is_null() { icons.push(h_icon); } TRUE }
2. 将HICON转换为RgbaImage
利用Windows GDI将图标绘制到位图,再提取像素数据转换为image::RgbaImage,同时使用Bevy的UVec2处理尺寸:
use image::{Rgba, RgbaImage}; use bevy_math::UVec2; use winapi::{ shared::minwindef::{HDC, HBITMAP, DWORD, FALSE, TRUE}, um::{ wingdi::{CreateCompatibleDC, CreateCompatibleBitmap, DeleteDC, DeleteObject, GetDIBits, SelectObject, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS}, winuser::{DrawIconEx, GetIconInfo, ICONINFO, DI_NORMAL}, }, }; fn hicon_to_rgba_image(h_icon: HICON) -> Option<RgbaImage<u8>> { unsafe { let mut icon_info = ICONINFO::default(); if GetIconInfo(h_icon, &mut icon_info) == FALSE { DestroyIcon(h_icon); return None; } let h_dc = CreateCompatibleDC(std::ptr::null_mut()); if h_dc.is_null() { DeleteObject(icon_info.hbmColor as _); DeleteObject(icon_info.hbmMask as _); DestroyIcon(h_icon); return None; } let width = icon_info.xHotspot * 2; let height = icon_info.yHotspot * 2; let h_bitmap = CreateCompatibleBitmap(h_dc, width as i32, height as i32); if h_bitmap.is_null() { DeleteDC(h_dc); DeleteObject(icon_info.hbmColor as _); DeleteObject(icon_info.hbmMask as _); DestroyIcon(h_icon); return None; } let old_bitmap = SelectObject(h_dc, h_bitmap as _); DrawIconEx(h_dc, 0, 0, h_icon, width as i32, height as i32, 0, std::ptr::null_mut(), DI_NORMAL); let mut bmi_header = BITMAPINFOHEADER { biSize: std::mem::size_of::<BITMAPINFOHEADER>() as DWORD, biWidth: width as i32, biHeight: -(height as i32), // 以图像顶部为原点 biPlanes: 1, biBitCount: 32, biCompression: BI_RGB, biSizeImage: 0, biXPelsPerMeter: 0, biYPelsPerMeter: 0, biClrUsed: 0, biClrImportant: 0, }; let buffer_size = (width * height * 4) as usize; let mut buffer = vec![0u8; buffer_size]; GetDIBits( h_dc, h_bitmap, 0, height as u32, buffer.as_mut_ptr() as _, &mut bmi_header as *mut _ as _, DIB_RGB_COLORS, ); // 转换为RgbaImage,使用Bevy的UVec2定义尺寸 let image_size = UVec2::new(width as u32, height as u32); let mut image = RgbaImage::new(image_size.x, image_size.y); for y in 0..image_size.y { for x in 0..image_size.x { let idx = (y * image_size.x + x) as usize * 4; let b = buffer[idx]; let g = buffer[idx + 1]; let r = buffer[idx + 2]; let a = buffer[idx + 3]; image.put_pixel(x, y, Rgba([r, g, b, a])); } } // 清理Windows资源,避免泄漏 SelectObject(h_dc, old_bitmap); DeleteObject(h_bitmap as _); DeleteDC(h_dc); DeleteObject(icon_info.hbmColor as _); DeleteObject(icon_info.hbmMask as _); DestroyIcon(h_icon); Some(image) } }
3. 整合为目标集合
将上述两个函数结合,最终得到Vec<RgbaImage<u8>>:
fn extract_exe_icons(exe_path: &str) -> Vec<RgbaImage<u8>> { let h_icons = load_exe_icons(exe_path); h_icons.into_iter().filter_map(hicon_to_rgba_image).collect() }
注意事项
- 所有Windows API调用需检查返回值,确保资源正确释放
- 若需要指定提取特定尺寸的图标,可修改
CreateIconFromResourceEx中的宽高参数 - 代码中使用Bevy的
UVec2处理图像尺寸,符合框架的数学结构使用习惯
内容的提问来源于stack exchange,提问作者TeamDman
相关产品推荐
相关产品推荐

