zoukankan      html  css  js  c++  java
  • Icon资源详解[1]

         本文分享&备忘最近了解到的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
     
  • 相关阅读:
    如何在服务器上添加本地驱动器
    JQ查找到带有某个字符,并起类名,然后替换这个某个字符
    使用IWMS的网站打开显示“未能加载文件或程序集”,解决方案
    表头固顶结构
    在页面中有overflow-y:auto属性的div,当出现滚动条,点击返回顶部按钮,内容回这个div最顶部
    在文档页面整个区域出现导航随内容滚动高亮显示效果
    在文档页面局部出现导航随内容滚动高亮显示效果
    在Hmtl页面中只让其中单独的一个div隐藏滚动条但是仍可滚动浏览下边的内容
    GC参考手册 —— GC 调优(工具篇)
    GC参考手册 —— GC 算法(实现篇)
  • 原文地址:https://www.cnblogs.com/cswuyg/p/3603707.html
Copyright © 2011-2022 走看看