You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 13:17:43