Windows图标提取技术解析

在Windows系统中,图标是用户界面的重要组成部分,它们不仅美观,而且提供了直观的视觉提示。然而,在开发应用程序时,经常需要提取特定大小和颜色的图标,这并非易事。本文将探讨如何使用标准API以及直接操作PE文件格式来提取图标。

图标提取的挑战

在Windows系统中,图标提取通常使用ExtractIconEx函数。但是,这个函数并不能指定图标的大小或颜色。例如,当尝试提取回收站图标(资源ID为32)时,可能会遇到问题。

系统图标概览

在Windows XP等系统中,图标有不同的大小。以下是一些常见的图标大小和它们的位置:

  • 桌面:Shell Large
  • 窗口标题栏:System Small
  • Alt+Tab对话框:System Large
  • 开始菜单:Shell Small / Shell Large

Windows系统维护了两种大小的图标:系统小图标和系统大图标。此外,Shell也有小图标和大图标的概念。这意味着Windows总共有四种不同的图标大小。

图标大小的确定

系统大图标的大小由视频驱动程序定义,因此不能动态更改。可以通过调用GetSystemMetrics函数并使用SM_CXICONSM_CYICON参数来查询系统大图标的大小。

Shell小图标的大小由Windows定义,目前Windows不支持更改此值,也没有直接的方法来查询此值。但是,在系统中,Shell小图标的大小似乎是大图标大小的一半,认为微软不会很快改变这一点。

Shell大图标的大小存储在注册表中的以下键下:

HKEY_CURRENT_USER\Control Panel\desktop\WindowMetrics\Shell Icon Size

可以通过修改注册表或从显示属性对话框的“外观”标签页来更改Shell大图标的大小,允许的值从16到72。

使用SDK方式提取图标

SDK提供了ExtractIconEx函数的解决方案,但是这些函数不能指定大小或颜色。因此,所有图标总是以默认颜色和默认大小的形式出现。

虽然PrivateExtractIcons看起来不错,因为它允许指定首选大小和“LR_COLOR”标志,但是文章最后提到:“不建议在新程序中使用它,因为在后续版本的Windows中可能会更改或不可用。” 这个函数是在2002年微软政府反垄断案和解后发布的。

直接操作PE文件格式提取图标

那么,如何使用文档化的信息来模拟PrivateExtractIcon呢?首先,将简要解释可移植可执行文件格式,因为资源存储在其中。

PE文件格式由MS-DOS MZ头、Real-Mode Stub Program、PE文件签名、PE文件头、PE可选头、所有节头和最后所有节体组成。

MS-DOS头占用PE文件的前64字节。这个头的内容由IMAGE_DOS_HEADER数据结构表示。这个结构的e_magic字段用于识别MS-DOS兼容的文件类型。所有MS-DOS兼容的可执行文件都将此值设置为0x54AD,代表ASCII字符“MZ”。最后一个字段e_lfanew是一个4字节的偏移量,指向文件中PE文件头的位置。

对于PE文件格式,这个签名出现在PE文件头结构之前。NT的签名为:

#define IMAGE_NT_SIGNATURE 0x00004550 // PE00

一旦有了文件签名的位置,PE文件就会在四个字节后跟随。可执行文件中的下224字节构成了PE可选头。可选头包含有关可执行映像的大部分有意义的信息,如初始栈大小、程序入口点位置、首选基址、操作系统版本、节对齐信息等。

节表是一个IMAGE_NUMBEROF_DIRECTORY_ENTRIES(16个空间保留用于条目)IMAGE_DATA_DIRECTORYs的数组。每个目录描述了位于节表条目之后的某个节中的特定信息的位置(32位RVA称为“相对虚拟地址”)和大小(也是32位,称为“原始数据大小”)。

资源,如对话框、菜单、图标等,位于由IMAGE_DIRECTORY_ENTRY_RESOURCE指向的数据目录中。

顶级目录类似于文件系统的根目录。“根”目录下的每个目录条目总是一个目录。这些第二级目录中的每一个都对应于资源类型(字符串表、对话框、菜单等)。在每个第二级“资源类型”目录下,将找到第三级子目录。每个第三级子目录对应于资源实例。

例如,如果有五个图标,就会有一个第二级图标目录,下面有五个目录条目。这五个目录条目本身是一个目录。目录条目的名称对应于资源实例的名称或ID。在这些目录条目下是一个包含资源数据偏移量的单个项目。

找到PE中的地址后,只需要调用LookupIconIdFromDirectoryEx来找到正确图标的资源ID,然后调用CreateIconFromResourceEx

使用以下代码:

ExtractIcons::Get

PrivateExtractIcons相同的参数。

启动演示并尝试代码。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485