VC如何将资源中包含的位图释放成文件

前两天一个朋友叫我帮他写一段代码,想把资源中包含的图片释放成磁盘文件,我想这个应该很简单啊,于是便写了如下函数:

    BOOL FreeSource_1(LPCTSTR pszResName,LPCTSTR pszResType,LPCTSTR szFileName)  
    {  
        BOOL bRet = TRUE;  
        HINSTANCE hInst = GetModuleHandle(NULL);  
      
        //判断指定的资源是否存在  
        HRSRC hResInfo = FindResource(hInst,pszResName,pszResType);  
        if(hResInfo == NULL)  
            return FALSE;  
      
        HANDLE hFile = NULL;  
        DWORD dwWritten = 0;  
      
        //调入指定的资源到内存  
        HGLOBAL hResData = LoadResource(hInst,hResInfo);  
        LPVOID pvResData = LockResource(hResData);  
        DWORD dwResSize = SizeofResource(hInst,hResInfo);  
      
        //创建目标文件并保存数据至文件  
        hFile = CreateFile(  
            szFileName,  
            GENERIC_WRITE,  
            FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,  
            NULL,  
            CREATE_ALWAYS,  
            FILE_ATTRIBUTE_HIDDEN, NULL  
        );  
      
        WriteFile(hFile, pvResData, dwResSize, &dwWritten, NULL);  
      
        //释放有关内存资源  
        CloseHandle(hFile);  
        FreeResource(hResData);  
      
        return TRUE;  
      
    }  


    调用代码为:

    FreeSource(MAKEINTRESOURCE(IDB_BITMAP1), RT_BITMAP, "C://TEMP1.BMP");  
    FreeSource(MAKEINTRESOURCE(IDR_JPG1), "JPG", "C://TEMP2.JPG");  


    奇怪的是,上述代码对自定义类型的资源,如GIF、JPEG甚至EXE都能正确释放,但对于BMP类型的却无能为力。虽然保存到了磁盘上,却无法打开,提示为格式错误!

 

    于是,我又自作聪明地使用流技术,把内存中的图像数据写回磁盘,代码如下:

BOOL FreeSource_2(LPCTSTR pszResName,LPCTSTR pszResType,LPCTSTR szFileName)
{
	BOOL bRet = TRUE;
	HINSTANCE hInst = GetModuleHandle(NULL);

	//判断指定的资源是否存在
	HRSRC hResInfo = FindResource(hInst,pszResName,pszResType);
	if(hResInfo == NULL)return FALSE;

	HANDLE hFile = NULL;
	DWORD dwWritten = 0;

	//直接保存自定义资源,如JPG、GIF、EXE等
	if(pszResType != RT_BITMAP)
	{
		//使用LoadResource调入指定的资源到内存
		HGLOBAL hResData = LoadResource(hInst,hResInfo);
		LPVOID pvResData = LockResource(hResData);
		DWORD dwResSize = SizeofResource(hInst,hResInfo);

		//创建目标文件并保存数据至文件
		hFile = CreateFile(
			szFileName,
			GENERIC_WRITE, 
			FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
			NULL,
			CREATE_ALWAYS,
			FILE_ATTRIBUTE_HIDDEN,
			NULL
		);

		WriteFile(hFile, pvResData, dwResSize, &dwWritten, NULL);
 
		//释放有关内存资源
		CloseHandle(hFile);
		FreeResource(hResData);
	}
	else //使用流技术保存BITMAP位图
	{
		//使用LoadBitmap调入指定的位图资源
		HBITMAP hBitmap = LoadBitmap(hInst, pszResName);

		if(hBitmap == NULL)return FALSE;

		//创建一个IPicture接口的实例
		PICTDESC ps;
		IPicture* pIPic=NULL;

		ps.cbSizeofstruct = sizeof(PICTDESC);
		ps.picType = PICTYPE_BITMAP;
		ps.bmp.hbitmap = hBitmap;
		ps.bmp.hpal = NULL;
		if(OleCreatePictureIndirect(&ps, IID_IPicture, TRUE,(LPVOID*)&pIPic) != ERROR_SUCCESS)
		{
			DeleteObject(hBitmap);
			return FALSE;
		}

		//将IPicture接口实例中的数据写入流接口中
		LONG lSize = 0;
		IStream* pIStm = NULL;
		CreateStreamOnHGlobal(NULL,TRUE,&pIStm);
		if(pIPic->SaveAsFile(pIStm,TRUE,&lSize) != ERROR_SUCCESS)
		{
			pIPic->Release();
			DeleteObject(hBitmap);
			return FALSE;
		}

		//保存流接口中的数据
		LARGE_INTEGER li;
		li.HighPart = 0;
		li.LowPart = 0;
		ULARGE_INTEGER uli;
		uli.HighPart = 0;
		uli.LowPart = 0;
		pIStm->Seek(li, STREAM_SEEK_SET, &uli);

		//创建目标文件并循环读取流中的数据保存至文件
		char buffer[1024];
		hFile = CreateFile(
			szFileName,
			GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
			NULL,
			CREATE_ALWAYS,
			FILE_ATTRIBUTE_HIDDEN,
			NULL);

		DWORD dwRead = 1;
		while(dwRead>0)
		{
			pIStm->Read(buffer, 1024, &dwRead);
			if(dwRead > 0)
				WriteFile(hFile, buffer, dwRead, &dwWritten, NULL);
		}

		//释放有关内存资源
		CloseHandle(hFile);
		DeleteObject(hBitmap);
		pIPic->Release();
		pIStm->Release();
	}
	return TRUE;
}


   果然不出所料,资源中所包含的位图被成功地保存到磁盘,并能被正确地打开了。于是我把代码给了朋友,算是完成了一个小任务。

 

    今天下午,突然想起第一个函数写成的文件总比源文件少14个字节,使用流技术会不会自动补全啊,于是对比了一下图片文件,意外发现生成的目标文件竟然比源文件多出了2倍多的字节,我顿时惊呆了。这是为什么呢?

14字节!14字节?我脑海里反复想到这个数字,因为按照常理,第一个函数便能正确运行的啊。第二函数虽然运行成功了,但文件大小又有了出入。这中间一定有鬼。

肯定是VC开发环境对BITMAP、ICON之类的资源作了手脚。

14字节!——这不正是位图文件头的结构大小吗?哦,也许是VC对位图资源忽略了文件头。于是赶快实验,便写了第三个函数,代码如下:

    BOOL FreeSource_3(LPCTSTR pszResName,LPCTSTR pszResType,LPCTSTR szFileName)  
    {  
        BOOL bRet = TRUE;  
        HINSTANCE hInst = GetModuleHandle(NULL);  
      
        //判断指定的资源是否存在  
        HRSRC hResInfo = FindResource(hInst,pszResName,pszResType);  
        if(hResInfo == NULL)  
            return FALSE;  
      
        HANDLE hFile = NULL;  
        DWORD dwWritten = 0;  
        //调入指定的资源到内存  
        HGLOBAL hResData = LoadResource(hInst,hResInfo);  
        LPVOID pvResData = LockResource(hResData);  
        DWORD dwResSize = SizeofResource(hInst,hResInfo);  
      
        //创建目标文件并保存数据至文件  
        hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);  
        if(pszResType == RT_BITMAP)  
        {  
            //获得位图信息头  
            BITMAPINFOHEADER bmpInfoHdr;  
            CopyMemory(&bmpInfoHdr, pvResData, sizeof(BITMAPINFOHEADER));  
            //获得位图颜色表中RGBQUAD结构的个数  
            long nColorEntries;  
            switch(bmpInfoHdr.biBitCount)  
            {  
            case 1:  
                nColorEntries = 2;  
                break;  
            case 4:  
                nColorEntries = 16;  
                break;  
            case 8:  
                nColorEntries = 256;  
                break;  
            default:  
                nColorEntries = 0;  
            }  
            //根据位图信息头创建位图文件头  
            BITMAPFILEHEADER bmpFileHdr;  
            bmpFileHdr.bfType = 0x4d42;  // 'BM'  
            bmpFileHdr.bfSize = sizeof(BITMAPINFOHEADER) + dwResSize;  
            bmpFileHdr.bfReserved1 = bmpFileHdr.bfReserved2 = 0;  
            bmpFileHdr.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorEntries;  
            //写入位图文件头  
            WriteFile(hFile, &bmpFileHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);  
        }  
        WriteFile(hFile, pvResData, dwResSize, &dwWritten, NULL);  
        //释放有关内存资源  
        CloseHandle(hFile);  
        FreeResource(hResData);  
        return TRUE;  
    };  


    这下,资源中包含的位图被正确地释放到了文件中,目标文件的大小也与被包含的资源文件一样。

    总结:VC在对BITMAP资源包含时,去除了源文件的文件头,使用流技术时,会自动将位图转换成24位真彩色,而我测试时用了一幅256色位图,所以造成目标文件的尺寸大于源文件的现象。

    由于API,现在编程非常简单,但越是简单,越不能掉以轻心,否则,摘不尽的BUG会毁掉你辛苦完成的程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值