现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

解决LoadImage加载图片报告内存不足的错误

2014-02-01 21:54 工业·编程 ⁄ 共 3175字 ⁄ 字号 暂无评论

    在使用Win32 API LoadImage加载大量图片,或者加载很大的图片的时候,你可能会碰到图片加载失败,而且调用GetLastError显示错误是“内存不足,无法完成此操作!”,打开任务管理器,却发现系统里面还有大量的内存空闲。比如使用下面的代码你就会碰到这个错误—当然你要准备大量的BMP图片:

#include "StdAfx.h"
#include "SampleBase.h"
#include <windows.h>
#include <shlwapi.h>
#include <stack>
#include <string>
 
#ifdef UNICODE
#  define tstring wstring
#else
#  define tstring string
#endif
 
using namespace std;
 
void CLoadImageIssueSample::Run(HWND hwnd)
{
    WIN32_FIND_DATA findFileData;
    ZeroMemory(&findFileData, sizeof(WIN32_FIND_DATA));
    TCHAR szCurDir[MAX_PATH];
 
    ::GetCurrentDirectory(MAX_PATH, szCurDir);
 
    HANDLE handle = FindFirstFile(TEXT("Resources//CLoadImageIssueSample//*"), &findFileData);
    stack<tstring> dirStack;
    tstring baseDir(szCurDir);
    baseDir += TEXT("//");
    baseDir += TEXT("Resources//CLoadImageIssueSample");
    while ( handle != INVALID_HANDLE_VALUE )
    {
        do
        {
            tstring fileName(baseDir);
            fileName += TEXT("//");
            fileName += findFileData.cFileName;
 
            if ( (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==
                 FILE_ATTRIBUTE_DIRECTORY )
            {
                if ( StrCmp(findFileData.cFileName, TEXT(".")) == 0 ||
                     StrCmp(findFileData.cFileName, TEXT("..")) == 0 )
                     continue;
                else
                {
                    dirStack.push(fileName);
                    // 并不删除cFileName,到时一起释放
                    ZeroMemory(&findFileData, sizeof(WIN32_FIND_DATA));
                }
            }
            else
            {
                if ( NULL == ::LoadImage(NULL, fileName.c_str(),
                                         IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE) )
                {  
                    LPVOID lpMsgBuf;
                    DWORD dw = GetLastError();
 
                    FormatMessage(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER |
                        FORMAT_MESSAGE_FROM_SYSTEM |
                        FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL,
                        dw,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPTSTR) &lpMsgBuf,
                        0, NULL );
 
                    MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK);
                    LocalFree(lpMsgBuf);
                }
 
                ZeroMemory(&findFileData, sizeof(WIN32_FIND_DATA));
            }
        } while ( FindNextFile(handle, &findFileData) != 0 );
 
        FindClose(handle);
        if ( !dirStack.empty() )
        {
            baseDir = dirStack.top();
            tstring format = baseDir + TEXT("//*");
            handle = FindFirstFile(format.c_str(), &findFileData);
            dirStack.pop();
        }
    }
}
 
这个错误是因为使用LoadImage加载图片资源时,LoadImage默认创建的是DDB格式的图像,而DDB图像是分配在Desktop Heap上面的,在Windows XP和Windows 2003里,这个Desktop Heap是被其他很多运行在同一个Windows会话(Session)里面的程序公用的,这个Heap的大小默认只有20M。虽然你可以在注册表里面更改成更大的值,但是在32位Windows上面Session View Space的大小有限,而Desktop Heap是Session View Space的一部分,这就意味着如果你将Desktop Heap的值改得很大,会影响在Session View Space里面其他资源的内存分配
 
解决方案有两个:
1.         使用64位的Windows,或者升级到Vista。
2.         或者修改代码,让LoadImage以DIB格式加载图片资源,这样你的图像资源就不是分配在Desktop Heap上面了,也就跳过了20M内存的限制。也就是说你将代码改成下面这样子就可以了:
if ( NULL == ::LoadImage(NULL, fileName.c_str(),
                                         IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION) )

给我留言

留言无头像?