在今天较少有情况需要去研究图像的文件格式,除非某些特定场合。在这里我还是去先做了这样一个小工具,目前已经支持展示BMP, ICO, CUR文件格式内容。实际上只要你知道一个文件的格式定义,你就可以去展示任何类型的文件(诸如EXE, DLL, PSD等等),例如下一步我可能考虑增加展示PE文件格式(可执行文件)。当然了,这里展示的都是二进制文件,也就是通常需要用16进制编辑器查看的文件,像TXT那样的文本文件是没有文件格式定义这种概念的(它只有编码的概念)。我制作它的本意是把它用于展示ICO文件格式,而BMP文件的格式是最基本的,所以它是我第一个加入的功能,关于BMP的文件格式主要参考了MSDN中的相关说明。
本文的关键词:TreeList Control,BMP文件格式,CxImage,MFC,C++,VC。
本文的参考资料请见本文结尾。
该工具的截图如下(下图为展示BMP文件的内容),树节点前的图标标示数字表示的是该节点数据的 sizeof 的值(bytes):
(1)本文参考和使用了 TreeList 控件,关于该控件的原始来源可能不是非常确切,我没有去考究。可以从参考资料上获取资料。关于该控件,相当于TreeCtrl 和 ListView 的综合体,不属于标准控件。所以需要自定义。我的最初想法是以VC中的List控件为基础,在内存中维护一个Tree的数据结构,然后通过List的用户绘制来完成这个控件。不过我在 codeproject.com 上看到的 TreeList 控件基本都是以 TreeCtrl 为基础 + 用户绘制来实现的。大家都这样选择,有可能是因为:
(a)最初作者采用了以TreeCtrl为基础实现,后续的人员参考了它,从而被继承下来。
(b)用TreeCtrl为基础要比以List为基础实现更方便,工作量更小。
但不管怎么样,我们可以比较下这两种方式。
(a)TreeCtrl为基础:优点是从基础控件继承得到了键盘的方向键导航,鼠标点击,双击等对Tree的处理,不需要我们做更多编码。缺点是鼠标操作不够友好,TreeCtrl的 HitTest 几乎失效(因为基础控件的节点文本无法完全模拟看到的效果),做水平滚动时,由于基础控件没有移动,所以对鼠标的一些操作也不够友好。(这些都属于原控件的一些缺点,我对源码家了很多修正和功能,使大部分瑕疵和缺点被弥补和修正)。
(b)List为基础,优点时自定义绘制非常灵活,相对于前者可以更方便的把Tree所在的列放在中间(而不是常见的第一列),由于该控件的行为和外观本质上更接近List,所以可以想见,这样实现的控件对滚动,标题头等鼠标操作都会非常正确。缺点是我们必须自己维护一个TreeCtrl的逻辑结构,动态的对List进行Item的增删处理,对Tree的相关鼠标键盘(例如折叠展开按钮)操作也必须手工添加。这样无疑对我们来说是一个不小的维护负担。并且由于精力限制,暂时我们就取用前人已经得到的成果。
由于两者都必须采用自定义绘制来实现,所以可以很方便的控制列的外观,例如可以比较自由的绘制进度条,图标,文本等等。
我对原始的TreeCtrl做了很多修改,添加了一些特性,例如是否绘制Grid网格,设置背景色,网格颜色,设置是否可以点击标题头进行排序等,同时也修改了一些绘制和鼠标响应的代码,基本上是这个控件的可用性,易用性,用户友好型改进很多,它可以基本流畅的使用和完成我想要的功能了,即清晰的展示一系列文件信息。关于该控件,也许我还可以单独再写一篇文章去介绍它,包括我对它的改进,以及如何把它添加到你的MFC应用程序中。这篇文章可以看着是对这个控件的一个范例应用。
(2)由于位图有很多种,例如索引,真彩色,等,不一定和我们绘制的DC能完全兼容,所以我最终还是决定引入了CxImage的代码,来做预览图的绘制。当然,如果你不需要预览图片,那么所有CxImage的代码是不需要的。
本工具可结合16进制编辑器,去查看文件的原始数据,其中“地址”为文件地址(16进制),大小为10进制(字节为单位)。对于BMP的文件格式来说已经基本没有什么技术上的研究点可言,不过对于ICO,GIF, JPG等其他图像类型的文件格式,则依然还是不够明确的,当然除非做一些文件格式插件,则没有必要去关注这些内容。下一步我打算增加对ICO文件(图标,光标)的文件格式的展示。
【注意】为了简单起见,在代码中我并没有去严格校验文件数据的合法性,我假设读取的BMP文件都是正确的有效的位图文件,所有的内存分配也假设不会失败。这样可以使我们的相关读取文件的代码变得非常简洁和直观,但在实际开发工作中是不能省略对异常情况的处理的。
这个小程序是使用VC6 + MFC来开发的,由IDE自动生成的一些特征符号,我并没有去改动,例如应用程序的图标,关于对话框等,这些外在特征都和特定的IDE版本和技术相关联,你可以很明显的看出它是用VC6和MFC开发的。对TreeList控件代码中的例如窗口类名等信息也未作改动,以示对原作者的尊重。
【更新】:
2010-9-6 13:31 增加对ico, cur文件格式的展示(尚未提供预览功能),cur为光标文件,与ico格式相同;效果如下图所示(下图为展示ICO文件的内容):
2010-9-6 14:58 修正读取尺寸超过255的超级大图标时,对数据块大小计算有误的BUG(此时应以bitmapInfoHeader中的图像宽高数据为准)。
2010-9-7 2:10 增加对PE文件格式的查看。可以查看EXE/DLL/OCX 文件。效果如下图所示:
2010-9-7 21:46 TreeList控件原作者使用CString去维护节点的多列文本,但是在VC6里面释放节点数据时经常触发异常(具体原因目前我还不是很清楚,可能和VC6携带的MFC类库有关),因此我把该CString对象改为C++标准库中的string对象。以解决清空控件节点时偶然触发的异常。
PE文件格式是通常做破解,病毒的人非常熟悉的,或者说想要研究这个领域的人所必须熟悉的格式,尽管从上图来看看起来似乎很简单,那是因为我把那些复杂的节点折叠了。但是实际上里面有些节点非常复杂(例如IMAGE_OPTIONAL_HEADER),子结点非常多,尽管添加子结点的代码主要只有一句,但这个工作仍然让我专门开启了另一个小项目,来辅助我完成添加子结点的代码(我把相关结构的定义拷贝到一个文本文件中,然后用这个小程序逐行读取,并解析出成员的类型和名称,拼装成添加子结点的代码,写入到另一个文本文件。从最终源码中你可以看到,展示PE格式时,填充 TreeList 控件代码,远远超过此前的BMP和ICO文件格式的展示),上图所打开的也就是这个辅助小程序的EXE文件。
由于PE格式里面涉及很多结构和成员,不管是阅读哪里的资料,你要弄清楚它都是需要花一定的精力的,因此这个小工具也许会带来一定帮助。这个工具把“线性的字节流”转换成文件格式定义所采用的“结构化的描述”,把二者结合在一起,因此能给你形成一个非常直观的认识,对于快速理解一种不够熟悉的文件格式非常有帮助,我个人觉得这很棒。像PE这样较为复杂的文件格式,如果你不是长期的接触,也许很快就会遗忘他的细节。
当然,这个工具开发到这里也并不是我最终的目的,因为我的最终目的是为某款商业软件制作一种文件格式插件,所以,在制作之前,了解清楚要处理的文件格式是非常必要的,从而这个小工具的“雏形”在这时出现在我的脑海,然后我就先把它创造出来了。
本文的源代码下载链接(2014-5-31 更新):
https://files.cnblogs.com/hoodlum1980/BmpFileView_Src.rar (obsoleted)
https://files.cnblogs.com/hoodlum1980/BmpFileView_V2_Src.zip
可执行文件的下载链接(2014-5-31 更新):
https://files.cnblogs.com/hoodlum1980/BmpFileView_V2_Bin.zip
【参考资料:】
关于TreeList控件,在codeproject网站上搜索 TreeList 即可。由于我对原来的控件基础上做了大量的修改和完善,所以如果你需要这个控件,我建议使用本文中我所提供的控件代码,它比原来的控件功能更强更完善更可用一些。
(1)Extended Tree List Control;
关于CxImage,可以加载多种图像格式(例如BMP,JPG,GIF,ICO,PNG等等)的文件。
(3)CxImage version 6.0.0 02/Feb/2008
关于ICO文件格式,主要参考微软提的一个纯 win32 开发的一个windows应用程序:
(4)ICONPRO 工具源码以及其帮助文件;
关于PE文件格式,主要参考了我几年前的发布的“窗口查看器”中的“PE文件格式查看器”组件(但显然现在的工具在外观上更棒。):
(5)终于解决了在c#里面用鼠标查找窗口的问题,原来如此简单。
(6)《看雪论坛精华》的相关合集中关于PE格式的相关文章,零散篇幅较多,具体的文献和作者不详陈了,资料来自于看雪论坛。
【附加资源】并非我在本文中所参考的资料,但也可供感兴趣的读者参考。
这里有一些位图的范例:
(1)Example BMP images (all Windows V3 except as indicated);
BMP文件格式的文档:
(2)BMP file format;
【维护历史】
(1)去掉了不实用的 CxImage 相关的预览图像的代码,调整了 CTreeList 控件的绘制细节(使网格和 Header 更加对齐,使单个网格可以自定义字体颜色),增加了文件打开时的 FilterString 并默认后缀过滤器为受支持的全部文件类型。使对话框从固定大小改为可调整大小,可以最大化,同时控件根据窗口大小重新布局(这样可以让程序更加有效利用显示器尺寸,展示更多行列内容)。“备注”和“类型”列增加了字体颜色,“大小”列的内容从 10 进制数字调整为 16 进制数据,个别列的格式改为靠右对齐。-- hoodlum1980, ON 2014-5-31。