以 PE 方式从 Exe 或 DLL 获取图标






4.89/5 (21投票s)
2005 年 1 月 14 日
5分钟阅读

130071

6359
以 PE 的方式从 Exe 或 DLL 获取图标,或者如何模拟 PrivateExtractIcons。
引言
我想从 exe 或 DLL 中提取图标。嗯,很容易,你可能会想。是的,当然。你知道那个带有非常漂亮的彩色图标的小对话框
但是,在你的应用程序中,你使用了标准的提取图标的方法( ExtractIconEx
),如果你请求回收站(resID 32),你会得到这个
问题在于 ExtractIcon
或 ExtractIconEx
。你无法指定你想要的图标的大小或颜色。
当然,shell 可以通过函数 SHGetFileInfo
来帮助你。但对于 DLL 的大小和第 32 个图标呢?
系统和图标的通用性
首先,我们需要对 XP 等系统中的图标进行一些解释。根据文章 Win32 中的图标,我们获得了一些信息。
Windows 使用不同大小的图标
Location |
图标大小 |
桌面 |
Shell 大 |
窗口标题栏 |
系统小 |
<Alt><Tab> 对话框 |
系统大 |
开始菜单 |
Shell 小 / Shell 大 |
在 Windows 中,系统维护着小图标和大图标的概念。此外,shell 也有小图标和大图标的概念。这意味着,总共有四种不同大小的图标——系统小、系统大、Shell 小和 Shell 大。
系统大尺寸由视频驱动程序定义,因此无法动态更改。可以通过调用 GetSystemMetrics
并使用 SM_CXICON
和 SM_CYICON
参数来查询系统大尺寸。
Shell 小尺寸由 Windows 定义,目前 Windows 不支持更改此值,也没有直接查询此值的方法。但今天在我的系统中,它的大小似乎是 Large Icon Size 的一半,而且我认为微软不会很快改变这一点。
Shell 大尺寸存储在注册表下的以下键中
HKEY_CURRENT_USER\Control Panel\desktop\WindowMetrics\Shell Icon Size
可以通过修改注册表或从“显示属性”对话框的“外观”选项卡来更改 Shell 大尺寸,该选项允许的值范围是 16 到 72。
SDK 方式的 ExtractIcon
这是 SDK 中的解决方案
但是使用这些函数,你无法指定大小或颜色。所以所有的图标都总是以 DEFAULTCOLOR
、DEFAULTSIZE
的形式出现。
PrivateExtractIcons
看起来很棒……
你可以指定首选大小和标志“LR_COLOR
”。
但在这篇文章的结尾,你可以看到
“建议不要在新程序中使用它,因为它在 Windows 的后续版本中可能会被更改或不可用。”因为这个函数是微软在 政府反垄断诉讼和解 之后于 2002 年发布的。
警告很清楚,不要使用它。
原始方式的 ExtractIcon
好的,那么我们如何使用已记录的信息来模拟 PrivateExtractIcon
呢?
首先,我对可移植可执行文件格式做一个快速的解释,因为资源就存储在其中。
PE 格式
标题
可移植可执行文件 (PE) 的整个格式由 MS-DOS MZ 头、实模式存根程序、PE 文件签名、PE 文件头、PE 可选头、所有节头以及最后所有的节体组成。
MS-DOS 头占据 PE 文件的前 64 字节。此头的 contents 由 IMAGE_DOS_HEADER
数据结构表示。此结构中的 e_magic
字段用于标识 MS-DOS 兼容文件类型。所有 MS-DOS 兼容的可执行文件都将此值设置为 0x54AD,它代表 ASCII 字符 MZ。最后一个字段 e_lfanew
是一个 4 字节的偏移量,指向 PE 文件头所在的位置。
实模式存根程序是当可执行文件加载时由 MS-DOS 运行的实际程序。对于实际的 MS-DOS 可执行映像文件,应用程序从这里开始执行。
对于 PE 文件格式,此签名出现在 PE 文件头结构之前。
NT 的签名是
#define IMAGE_NT_SIGNATURE 0x00004550// PE00
一旦我们获得了文件签名的位置,PE 文件就在其后四字节。可执行文件接下来的 224 字节构成了 PE 可选头。与可选头对应的信息由 IMAGE_OPTIONAL_HEADER
数据结构定义。可选头包含有关可执行映像的大多数有意义的信息,例如初始堆栈大小、程序入口点位置、首选基址、操作系统版本、节对齐信息……
节表
节表是 IMAGE_NUMBER_OF_DIRECTORY ENTRIES
(为条目保留 16 个空间)IMAGE_DATA_DIRECTORYs
的数组。这些目录中的每一个都描述了位于目录条目之后某个节中的特定信息的位置(32 位 RVA,称为“相对虚拟地址”)和大小(也是 32 位,称为“原始数据大小”)。
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
资源,如对话框、菜单、图标等,位于由 IMAGE_DIRECTORY_ENTRY_RESOURCE
指向的数据目录中。
最顶层的目录类似于文件系统的根目录。根目录以下的每个目录条目始终是一个目录。这些二级目录中的每一个都对应一个资源类型(字符串表、对话框、菜单……)。在每个二级“资源类型”目录下方,您会找到三级子目录。每个资源实例都有一个三级子目录。
例如,如果有五个图标,则会有一个二级图标目录,其下方有五个目录条目。这五个目录条目中的每一个本身都是一个目录。目录条目的名称对应于资源实例的名称或 ID。在这些目录条目中的每一个下方都有一个单独的项目,其中包含指向资源数据的偏移量。
Root \
DIRECTORY_ENTRY \
IDR_ICON_1 \
offset to data (
in relative virtual address form (RVA) )
IDR_ICON_2 \
offset to data
IDR_ICON_3 \
offset to data
IDR_ICON_4 \
offset to data
IDR_ICON_5 \
offset to data
所以现在简单部分来了,在 PE 中找到地址后,我们只需调用 LookupIconIdFromDirectoryEx
来查找正确图标的资源 ID,然后调用 CreateIconFromResourceEx
。
使用代码
非常简单,调用: ExtractIcons::Get
,参数与 PrivateExtractIcons
相同。
启动演示并尝试运行代码。
结论
我不确定原始方式是否比 PrivateExtractIcons
更好,但我很确定 PE 格式不会很快被微软更改……。
如果您正在寻找 Win64 中 PE 的更改,您可以检查此代码是否有效(目前未经测试)。
无论如何,它都能正常工作,现在我可以使用 shell32 中的精美图标,具有正确的大小和正确的颜色。
参考 PE 和资源
当然,
参考图标