5. Windows程序图标提取方法

2026-01-24 13:10:23 | 限时活动 | admin | 6251°c

1 介绍

一些效率工具常常需要显示其它应用程序的图标,而找到电脑上其它安装的程序本身不难,难的是怎么才能拿到该程序的图标。

经过我的搜寻,网上并没有好用的现成库,所以本文介绍一下我的实现方案。

2 简单介绍

exe程序图标在编译的时候就已经被编译进了exe文件中,所以想要拿到图标,就得解析exe文件格式,也叫PE文件,其内部构造是比较复杂的,又尤其是各种资源。

但好在有现成的库可以帮我们完成这一解析操作:pelite。

这里用的是rust库,其它语言一般也会有对应的库实现。

首先在依赖中引入该库:

[dependencies]

pelite = "0.10.0"

它的使用还是比较简单的:

use pelite::pe32::{Pe, PeFile};

use pelite::FileMap;

fn main() {

let file_map = FileMap::open(r"D:\Install\TreeSizeProv9.exe").unwrap();

let file = PeFile::from_bytes(file_map.as_ref()).unwrap_or_else(|e| {

println!("{}", e);

panic!();

});

let resources = file.resources().unwrap();

for (name, group) in resources.icons().filter_map(Result::ok) {

for entry in group.entries() {

match group.image(entry.nId) {

Ok(image) => {

//得到图片数据

}

Err(err) => {

println!("{}: Error {}!", name, err)

}

}

}

}

}

上面的代码基本就是直接复制的该库实例代码,只不过由于我这里暂时是32位的程序,所以最前面导入的是pe32中的包。

提取图标的时候有几点要注意:

图标文件本身内部并不仅仅只有一张图片,它可能是很多张图片,并且这些图片的格式可以不一样。

上面循环中得到的图片数据是一个图标文件中的某一张图片,如果该图片为png格式的,那可以直接保存,但如果不是,直接保存是无效的。

图标格式可以参考官方:Icons | Microsoft Learn。

说实话icon格式还是比较复杂的,经过我的实践,主要会遇到以下几个问题:

直接使用上面的示例代码提取图片,会导致没有png图片的icon图标应用提取失败

直接将icon图片整个保存,会导致某些图片呈现的效果非常模糊

3 图标提取

为了解决上面我遇到的两个问题,经过长时间摸索,最终总结出一个比较完美的方案:

fn get_icon_to_path(path: &str, save_path: &str) -> bool {

//不是exe文件结尾,直接跳过。

if !(path.ends_with(".exe") || path.ends_with(".EXE")) {

return false;

}

let file_map = pelite::FileMap::open(path);

if let Err(_e) = file_map {

return false;

}

let file_map = file_map.unwrap();

let file = pelite::PeFile::from_bytes(file_map.as_ref());

if let Err(_e) = file {

return false;

}

let file = file.unwrap();

let mut icons = file.resources().unwrap().icons();

let res = icons.next();

if let None = res {

return false;

}

let res = res.unwrap();

if let Err(_e) = res {

return false;

}

//只取第一组图标

let (_, group) = res.unwrap();

let mut f = false;

for entry in group.entries() {

// Fetch the image data for this entry

match group.image(entry.nId) {

Ok(image) => {

// Check if the image data starts with the PNG magic bytes

if image.starts_with(b"\x89PNG") {

std::fs::write(save_path, image).unwrap();

f = true;

}

}

Err(_) => {}

}

}

if f {

return true;

}

let mut f = std::fs::File::create(save_path).unwrap();

match group.write(&mut f) {

Err(_) => false,

Ok(_) => true,

}

}

上面是我写的一个专门用于提取exe程序图标的函数,其中第一个参数为exe文件的路径,第二个参数为图标的保存位置。

逻辑并不难理解:

首先遍历exe文件中第一组icon中的所有图片,只要为png图片就对其进行保存,一般大图都放在了后面,这样可以保证最后保存的是最大的一张png图片。

如果前面没有找到png图片,那么就直接将整个icon图标数据写入文件中。