本文分享&备忘最近了解到的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. git学习笔记11-git多人协作-实际多人怎么开发

    当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin. 要查看远程库的信息,用git remote: $ git r ...

  2. WCF学习资源收集汇总

    1.WCF编程 http://www.cnblogs.com/wengyuli/category/217446.html 2.wcf热门问题编程示例 http://blog.csdn.net/book ...

  3. (linux) 常用命令 干货

    内容提要 1. 第一难度, 重要 梯队 (grep, awk, sed, find, ) 2. 第二常用, 重要 梯队 (ps, ls, date, fuser, netstat, kill) 3. ...

  4. java实现excel与mysql的导入导出

    注意:编码前先导入poi相关jar包 1 /** * 读excel 到list * * @param file excel file * @param fields 字段数组 * @return * ...

  5. JavaScript的事件对象_鼠标事件

    鼠标事件是 Web 上面最常用的一类事件,毕竟鼠标还是最主要的定位设备.那么通过事件对象可以获取到鼠标按钮信息和屏幕坐标获取等. 一.鼠标按钮 只有在主鼠标按钮被单击时(常规一般是鼠标左键)才会触发 ...

  6. python把str转换为int

    def str2int(s): def fn(x,y): return x+y def char2num(s): ':9}[s] return reduce(fn,map(char2num,s)) ' ...

  7. python的变量传递

    python中变量都被视为对象的引用.python函数调用传递参数的时候,不允许程序员选择传值还是传引用,python参数传递采用的都是“传对象引用”的方式.     这种方式相当于传值和传引用的结合 ...

  8. libsvm-3.21使用文档

    Libsvm is a simple, easy-to-use, and efficient software for SVM classification and regression. (可用于分 ...

  9. imageview圆角的实现

    介绍一种简单的.另类的实现,就是把图片在显示前处理成圆角的,但是不改变存储的图片.相当于经过了图像过滤. 需要调用的图像工具类是 package com.gaosi.util; /** * @auth ...

  10. OneProxy主从延迟检测

    OneProxy具有主从延迟检测功能,检测方式有两种. 1.通过MySQL本身提供的延迟信息,即在从库中执行show slave status; 查看Seconds_Behind_Master值: 2 ...