CreateCompatibleBitmap返回错误码8的原因及解决方案

来源:互联网 发布:淘宝seo视频教程 编辑:程序博客网 时间:2024/05/21 19:45

转自:http://hi.baidu.com/li_kun_peng/item/785d84f5ec21014c932af2b2


最近测试程序,发现一个图片显示程序在一台512M内存的机器上同时打开5个以后,就无法显示图片了。在网上查了一下,CSDN有袁峰回答了一个类似问题。根据袁峰的提示,我找了他的书《Windows图形编程》(英文名:Windows Graphics Programming Win32 GDL and DirectDraw),借鉴他的源代码,很快解决了问题。



现在回过头来总结一下我程序问题的解决过程。


我的绘图程序用的是DDB,创建bitmap的时候用的CreateCompatibleBitmap。这个函数的好处是简单,好调用,而且可以创建与显示设备兼容的位图。在测试程序的时候,发现运行五个程序实例后,从第六个开始就无法显示图片了。跟踪调试一下,发现是调CreateCompatibleBitmap出错,GetLastError返回错误码为8,意思是没内存了。在我的程序无法显示图片时,用mspaint.exe画图程序却还能打开。问题比较明显了,肯定是我的程序有问题,不够完善。


上网一查,有人说是没有合理释放Create的GDI对象,导致GDI句柄超限或内存泄漏。可用任务管理器查看GDI对象和内存,发现我的程序占用的内存和GDI对象句柄都不高,都远低于画图程序。可画图程序偏偏就基本可以无休止地打开任意多个实例并显示图片。再在网上找,在CSDN发现了袁峰前辈针对类似问题的回帖,真是一语道破天机,真乃高人也!


问题的本质在于我调用CreateCompatibleBitmap创建DDB位图。此函数创建位图时用的是系统内核的分页内存,这是稀有资源。我要显示的图片3000多×3000多,24dpp,粗算一下要 30多兆内存。在运行我程序的时候,我打开任务管理器的性能页,通过“核心内存”查看程序打开前后分页内存数的变化。结果真是每运行一个程序实例,分页内存数就增加30多兆。运行第五个的时候,由于系统无法再分配出30多兆分页内存了,所以调用 CreateCompatibleBitmap就不能成功。


问题发现了,如何解决呢?还是袁前辈给出了办法,就是用CreateDIBSection创建位图。刚好手头有一本袁前辈的《Windows图形编程》,阅读了第10章的部分小节,参考了随书光盘中的部分代码,很快就解决了问题。CreateDIBSection的好处是,它使用虚拟内存创建位图。这样运行程序的实例数就只限于pagefile和磁盘空间大小了。不过CreateDIBSection比CreateCompatibleBitmap要难调用一些。当然,有袁前辈的示例代码就很简单了。


我并不是要专于GDI编程,只是偶然间需要实现一个显示照片的程序。如果不是袁前辈的回帖、书籍和示例代码,我真不知道要花多少时间才能找到问题并解决之。所以,真的要感谢袁前辈!


袁前辈本科毕业于上海大学,研究生在南京大学读,获得了硕士和博士学位。《Windows图形编程》一书很有深度,非国内同类著作所能比拟,让人敬佩!
使用CreateCompatibleBitmap创建位图的时候,返回错误码8:存储空间不足,无法处理此命令。
原因:此函数创建位图时用的是系统内核的分页内存,这是稀有资源(可从任务管理器性能页的核心内存项查看),因此,如果位图比较大的话,就报错了。
解决方案:CreateDIBSection。
这个函数不再从系统内核的分页内存中获取资源了,而是从物理内存和虚拟内存中获取,因此,原则上对位图的大小限制要小得多。
附上一小段CreateDIBSection的用法示例:
   BITMAPINFOHEADER bmih;
    memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
    bmih.biSize = sizeof(BITMAPINFOHEADER);
    bmih.biBitCount = 24;
    bmih.biCompression = BI_RGB;
    bmih.biPlanes = 1;
    bmih.biWidth = info.nImageWidth;
    bmih.biHeight = info.nImageHeight;
    BITMAPINFO bmi;
    memset(&bmi, 0, sizeof(BITMAPINFO));
    bmi.bmiHeader = bmih;
    void* p;
    hBitmap = ::CreateDIBSection(cdc.GetSafeHdc(), &bmi, DIB_RGB_COLORS, &p, NULL, 0);