本文分享&备忘最近了解到的icon资源在windows平台下相关的一部分知识,所有测试代码都尽可能的依赖win32 API实现。更全面的知识,参考文末列出的”参考资料“。

     关键字:Icon资源存储结构、windows shell下显示哪个图标、如何获取EXE各种长宽的显示图标。

    一个icon资源(可以是*.ico文件,也可以是windows资源节区里的icon group),可以包含多张图片,这些图片有着各自的size或者颜色深度,这些图片可以是bmp格式或者png格式(vista之后支持,一般256*256时使用)。

一、Icon文件的组成

    Icon文件结构由两部分组成:icon文件头和多张图片数据,图片可以是bmp、png:
1、Icon文件头
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource Type (1 for icons)
WORD idCount; // How many images?
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
    其中idReserved必须是0;idType对于ICON文件来说必须是1;idCount指明icon文件有多少张图片,也就指明了接下来有多少个ICONDIRENTRY结构体数据。
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved ( must be 0)
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // How many bytes in this resource?
DWORD dwImageOffset; // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
    其中dwBytesInRes指明本图片占用多少字节,dwImageOffset指明本图片数据开始位置相对文件头的偏移。颜色深度:bColorCount = 1 << (wBitCount * wPlanes),如果大于等于8,则填0。
2、图片数据
    如果是位图格式图片:
typdef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
icHeader中只有部分信息是icon文件需要的。icXOR也称为“image“,icAND也称为“mask”。
 
一个有两张位图图片的ico文件,文件头内容(前0x15字节是icon文件头 + ICONDIRENTRY 的内容):
 
 
如果是PNG格式图片,则Icon文件头后边接着的是png数据格式,譬如:
icon中含有一张png图片,文件头内容(前0x15字节是icon文件头 + ICONDIRENTRY 的内容):
 
 
    png格式API相关:虽然LoadImage在vista版本下可以加载含有png格式图片的ico文件,但是windows没有直接API支持应用程序对png、jpeg格式文件的解压使用。在显示png、jpeg格式数据时,有StretchDIBits、SetDIBitsToDevice两API可以使用,但必须先检查硬件是否支持,因为这属于硬件级别的解码支持。想在GetDIBits中使用BI_PNG,获取PNG格式的数据是行不通的,BITMAPINFOHEADER结构体中的biCompression值BI_PNG,仅仅在上述两API中生效。
    为什么在icon文件中有png格式存储:*.ico文件中含有png格式,这是vista以及之后的版本才做的支持,目的是为了减小256*256 ico 文件的size,一般的ico编辑工具都会默认把ico文件中256*256 size的图片保存为png格式,这是微软推荐的保存格式(参考资料5)。xp以及之前的系统用不到256*256格式的ico,所以没有支持。

二、Icon在windows中的显示

    这里的icon特指两种情形:(1)EXE中的icon group资源;(2)*.ico文件。
    EXE文件在windows shell下显示哪个icon,按照MSDN文文件档(参考资料1)的介绍,是这样子:
桌面图标显示哪个ICON图片,有两个步骤,1、选择序号最小的RT_GROUP_ICON,优先字母序号,再找数字序号;2、在RT_GROUP_ICON里面选择合适的RC_ICON:(1)size跟要求的最接近;(2)如果size符合要求的有多个,则选择颜色深度跟显示系统颜色深度匹配的;(3)如果颜色深度没有匹配的,则选择在显示系统支持范围内颜色深度值大的;(4)如果所有的颜色深度都大于显示系统支持范围,则选择颜色深度最低的;(5)系统把8位以及大于8位的颜色深度当作相同深度,这时,系统选择最先遍历到的RC_ICON;(6)如果显示系统的颜色深度是8位,则系统会在16色(颜色深度4位) 和256色(颜色深度8位)之间选择16色的,并且使用系统默认调色板。(这一条规律似乎跟(3)矛盾,有人说这是在安全模式下才成立的)。
    在RT_GROUP_ICON选定的情况下,按照我自己的实践测试:
    自己绘制一个*.ico文件并对各个size做标记,可以发现windows7下exe在选择显示哪个图标的时候,只用到:16 * 16(文件夹内小图标),32 * 32(属性、桌面小图标), 48 * 48(中等图标), 256 * 256(大图标)这四种size,xp下则使用前3种。如果ico中含有png格式,则需要使用VS2008以及以上的IDE,否则rc无法对其进行编译。对于windows显示何种图标参考资料4、5、6有更详细的介绍。

三、从PE文件中提取icon资源

    对于EXE文件,最简单的是使用SHGetFileInfo API,但只能获取16* 16,32 * 32 size的图标。该api如果只指定SHGFI_ICON,则表明使用默认的32*32,如果指定SHGFI_LARGEICON,则表明是32*32,如果指定SHGFI_SMALLICON,则表明是16*16。SHGetFileInfo 还可以根据文件后缀名获取某类文件的图标。
    示例代码:
1、某文件的32 * 32 pixels icon
//获取exe的32 * 32 icon
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo(L"D:\\a.exe", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_LARGEICON);
HICON icon_handle = sfi.hIcon;
2、某类文件的32 * 32 pixels icon
//获取*.rmvb文件类型的32 * 32 icon
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo(L"*.rmvb", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES);
HICON icon_handle = sfi.hIcon;
3、 提取其它size的图标,譬如: 48 * 48、256 * 256,则需要使用另外的方法
//获取48 * 48 或者 256 * 256 pixels icon
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
::SHGetFileInfo(L"D:\\b.exe", FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX);
HIMAGELIST* imageList = NULL;
//SHIL_EXTRALARGE获取48 * 48的图标, SHIL_JUMBO 获取256 * 256的图标。
HRESULT hResult = ::SHGetImageList(SHIL_EXTRALARGE , IID_IImageList, (void**)&imageList);
HICON icon_handle = NULL;
if (hResult == S_OK)
{
hResult = ((IImageList*)imageList)->GetIcon(sfi.iIcon, ILD_NORMAL, &icon_handle);
}
     获取到HICON之后,可以使用DrawIcon API执行绘制,也可以使用AlphaBlend“重造轮子”执行绘制。如果要保存为文件,则构造一个ICON文件头,再把HICON转换为DIB数据拼在ICON文件头后边,注意:hbmColor和hbmMask这两个句柄的DIB数据都要有。我试过直接拿bmp位图数据修改:前头加上icon文件头,BITMAPINFOHEADER的biHeight *= 2,保存后各种size的图片都变残缺了(好像是被挖去了一个二维码),只有256*256的图片少部分像素丢失,显示稍微正常点,可能是因为256*256的图片mask部分用处不大的缘故,另外,还发现这样子的256*256 icon图片是留有透明通道的,这也说明,*.bmp位图文件中是可以有透明通道的,只是显示图片的工具把它忽略了。(2014.3.22 部分内容补充)
    上边所说到的ico资源,都是在windows shell下能显示的图片,如果要提取没有显示的图片,则要利用资源枚举系列函数:EnumResourceNames、FindResouce、LoadResource、LockResource、CreateIconFromResourceEx。
本文所在:http://www.cnblogs.com/cswuyg/p/3603707.html

四、参考资料

1、ICON结构,很全面的知识:http://msdn.microsoft.com/en-us/library/ms997538.aspx
2、ICON发展历史系列,涉及一些MSDN没有谈到的细节:
3、HICON保存为*.ico文件,对于256格式的icon图片,保存时没有保存为png,其中一个答案的代码:“WriteFile(hFile, &padding, 4 - bmp.bmWidthBytes, &nWritten, 0);”有错误,应该是:“ WriteFile(hFile, &padding, 4 - (bmp.bmWidthBytes & 3), &nWritten, 0); ”,该答案对于低于8颜色深度的图片没有做处理,另一个使用GetDiBits实现的答案更好。
5、ICO 文件格式以及发展历史:http://en.wikipedia.org/wiki/ICO_(file_format)
7、不支持在应用程序中使用jpeg、png格式的解压,仅仅StretchDIBits、SetDIBitsToDevice两API支持直接向输出设备输出jpeg、png压缩格式数据,这是硬件支持的,使用时需要先判断硬件是否支持: http://msdn.microsoft.com/en-us/library/dd145023%28VS.85%29.aspx
8、获取exe *.ico文件中所有size的图片,批评者的观点很全面:http://stackoverflow.com/questions/16330403/get-hbitmaps-for-all-sizes-and-depths-of-a-file-type-icon-c
9、总结了IOS、Windows上的各种icon size对比:http://www.visualpharm.com/articles/icon_sizes.html
 

Icon资源详解[1]的更多相关文章

  1. Icon资源详解[2]

    本文分享&备忘最近了解到的icon资源在windows平台下相关的一部分知识.所有测试代码都尽可能的依赖win32 API实现.通过源码可以了解其结构,同时它们也是可复用的代码积累.     ...

  2. VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)

    上一节讲了标签控件Tab Control以后,常用控件的内容就全部讲完了,当然并没有包括所有控件,主要是一些很常用很重要的控件.本节开始鸡啄米将为大家讲解菜单的概念及使用. 菜单简介 菜单在界面设计中 ...

  3. VS2010-MFC(菜单:VS2010菜单资源详解)

    转自:http://www.jizhuomi.com/software/210.html 上一节讲了标签控件Tab Control以后,常用控件的内容就全部讲完了,当然并没有包括所有控件,主要是一些很 ...

  4. Android下的xml资源详解

    最近遇到的bitmap资源 <?xml version= "1.0" encoding ="utf-8"?> <bitmap xmlns:an ...

  5. kubernetes系列08—service资源详解

    本文收录在容器技术学习系列文章总目录 1.认识service 1.1 为什么要使用service Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结 ...

  6. 2、puppet资源详解

    定义puppet资源 puppet资源抽象 资源定义 每一个资源有一个type.一个title和一个属性集合(attribute) type {'title':   //type表示资源类型,  ti ...

  7. Java使用wait() notify()方法操作共享资源详解_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 Java多个线程共享资源: 1)wait().notify()和notifyAll()方法是本地方法,并且为final方 ...

  8. Kubernetes-Ingress资源详解

    什么是Ingress #阿里云称之为ingress路由!在 Kubernetes 集群中,主要用于接入外部请求到k8s内部,Ingress是授权入站连接到达集群服务的规则集合,为您提供七层负载均衡能力 ...

  9. Kubernetes-Service资源详解

    service的三种工作模式:(userstats(效率低).iptables.ipvs) service可以自动实现负载均衡.service自动实现了负载均衡,service通过selector标签 ...

随机推荐

  1. lazyload懒加载的使用

    1.引用<script src="http://a.tbcdn.cn/apps/baron/js/??lib/tmm/tmm.js,lib/lazyload/lazyload.js?2 ...

  2. ubuntu12.04 gdb安装使用

    参考文档:http://blog.csdn.net/haoel/article/details/2879 http://www.programlife.net/gdb-manual.html [新手笔 ...

  3. Redis实践操作之—— keyspace notification(键空间通知)

    一.需求分析: 设置了生存时间的Key,在过期时能不能有所提示? 如果能对过期Key有个监听,如何对过期Key进行一个回调处理? 如何使用 Redis 来实现定时任务? 二.序言: 本文所说的定时任务 ...

  4. [转载] C++ 程序员快过来围观:非常实用全面的 C++ 资源

    原文: http://codecloud.net/c-plus-plus-resource-2983.html 绝对是c++开发者的福音啊, 必须推荐. 这次的资源涉及到了标准库.Web应用框架.人工 ...

  5. IO端口、IO内存、IO空间、内存空间的含义和联系

    1,IO空间:X86一个特有的空间,与内存空间独立的空间,同样利用IO空间可以操作数据,只不过是利用对应的IO端口操作函数,例如inb(), inbw(), inl(); outb(), outw() ...

  6. Android自动接听&挂断电话(包含怎么应对4.1以上版本的权限检

    一  前言 这两天要研究类似白名单黑名单以及手势自动接听的一些功能,所以呢,自然而然的涉及到怎么自动接听/挂断电话的功能了.对于自动接听这一块,android4.1版本及其以上的版本和之前的版本处理逻 ...

  7. 提高iOS开发效率的第三方框架等

    http://zhangmingwei.iteye.com/blog/2208783 http://www.kuqin.com/shuoit/20150703/346900.html

  8. Tomcat:IOException while loading persisted sessions: java.io.EOFException解决手记

    原文:http://blog.csdn.net/lifuxiangcaohui/article/details/37659905 一直用tomcat一段时间都正常无事,最近一次启动tomcat就发生以 ...

  9. Linux 系统时间查看 及 时区修改(自动同步时间)

    1:使用date命令查看时区 [root@db-server ~]# date -R   Sun, 11 Jan 2015 07:10:28 -0800   [root@db-server ~]#   ...

  10. /dev/sda3: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY

    系统强制断电后,出现以下错误: /dev/sda3: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY 启动系统后在字符界面有两个选项,输入root密码进入维护模 ...