【windows gdi+】GDI+ Image类加载图片时异常问题处理与分析

 问题描述:

项目里一个控件,须要加载本地图片,单张第一次加载的时候能够的,可是从新选择其余图片,会出现图片显示异常的现象。markdown

直接上图,图片部分显示不全了。编码

问题分析:

一开始怀疑现象是有个图片重复选择后显示是好的,其余某几个图很容易复现,就怀疑是图片问题,查了半天jpg图片完整性,发现图片也没什么区别,后面又查分辨率,在DrawImage()里修改分辨率为偶数了也不行。。。最后只能看代码了,发现是代码里本身清理GLOBAL内存了。其实以前有项目我遇到清理GLOBAL内存致使图片显示不全或者异常的问题。时间过久忘记了。仍是记录一下吧。spa

知识附录:


1, 是Bitmap(RT_BITMAP)类型的图片没法加载, RT_BITMAP是预约义类型, 资源里面没有bmp文件的头, SizeofResource 的返回值要比图片文件少几个字节,由于少了这几个字节, 因此GDI+会返回invalid parameter错误。

2, 从IStream里面建立出来的Image对象会引用到堆里面的GLOBAL内存, 若是GLOBAL内存hBuffer被释放了, 建立的Image的内容就会被破坏,有时只能画出一小部分图片, 有时整个图片有大片的乱码,好像是编码失败的样子, 视当时的内存情况而定。code

另外调用Image的Clone也没用,深层次想Clone不是真正的深拷贝,仍是依赖那个GLOBAL内存的,若是那个内存被销毁了,Clone的图片也会异常。orm

只有在销毁或者析构的时候才能清理那个GLOBAL内存,不然会致使显示异常(不是100%出现,而是间歇性出现,可是确定会出现)。对象

这是看到的demo,拷贝过来的,能够看看,注意看注释。图片

​

CYourClass::~CYourClass()
{
    for(IMG_VECTOR::iterator it = m_arImage.begin(); it != m_arImage.end(); it++)
        delete *it;
    for(HGLB_VECTOR::iterator it = m_arGlobal.begin(); it != m_arGlobal.end(); it++)
    {
        ::GlobalUnlock(*it);
        ::GlobalFree(*it);
    }
}


void CYourClass::AddImage(HMODULE hInst, UINT nResourceID, LPCTSTR lpType)
{
    if(lpType == RT_BITMAP)
    {
        //GDI+ can not load RT_BITMAP resouce, 
        //because they are predefined resource, 
        //they don't contains the image file header.
        assert(FALSE);
        return;
    }

    HRSRC hResource = ::FindResource(hInst, MAKEINTRESOURCE(nResourceID), lpType);
    if (!hResource)
        return;

    DWORD imageSize = ::SizeofResource(hInst, hResource);
    if (!imageSize)
        return;

    const void* pResourceData = ::LockResource(::LoadResource(hInst, hResource));
    if (!pResourceData)
        return;

    HGLOBAL hBuffer = ::GlobalAlloc(GMEM_FIXED, imageSize);
    if (NULL == hBuffer)
        return;

    void* pBuffer = ::GlobalLock(hBuffer);
    if (pBuffer)
    {
        CopyMemory(pBuffer, pResourceData, imageSize);
        IStream* pStream = NULL;
        if (::CreateStreamOnHGlobal(hBuffer, FALSE, &pStream) == S_OK)
        {
            Gdiplus::Image * pImage = Gdiplus::Image::FromStream(pStream);
            pStream->Release();
            if (pImage)
            { 
                if (pImage->GetLastStatus() == Gdiplus::Ok &&
                    pImage->GetWidth() > 0)
                {
                    m_arImage.push_back(pImage);

                    //it seems the image will take usage of the global memory.
                    //so the global memory should be kept until the image destroy.
                    //GDI++此种打开本地文件的方式占用了这片内存,只有在你销毁图片时才能销毁这片    
                    //内存,不然会破坏你的图片,致使显示异常
                    m_arGlobal.push_back(hBuffer);
                    return;
                }

                delete pImage;
            }
        }
        ::GlobalUnlock(hBuffer);
    }
    ::GlobalFree(hBuffer);
}

​
复制代码

\ip

相关文章
相关标签/搜索