图片释放成磁盘文件

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

 

  1. BOOL FreeSource_1(LPCTSTR pszResName,LPCTSTR pszResType,LPCTSTR szFileName)   
  2. {   
  3.     BOOL bRet = TRUE;   
  4.     HINSTANCE hInst = GetModuleHandle(NULL);   
  5.   
  6.     //判断指定的资源是否存在   
  7.     HRSRC hResInfo = FindResource(hInst,pszResName,pszResType);   
  8.     if(hResInfo == NULL)   
  9.         return FALSE;   
  10.   
  11.     HANDLE hFile = NULL;   
  12.     DWORD dwWritten = 0;   
  13.   
  14.     //调入指定的资源到内存   
  15.     HGLOBAL hResData = LoadResource(hInst,hResInfo);   
  16.     LPVOID pvResData = LockResource(hResData);   
  17.     DWORD dwResSize = SizeofResource(hInst,hResInfo);   
  18.   
  19.     //创建目标文件并保存数据至文件   
  20.     hFile = CreateFile(   
  21.         szFileName,   
  22.         GENERIC_WRITE,   
  23.         FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,   
  24.         NULL,   
  25.         CREATE_ALWAYS,   
  26.         FILE_ATTRIBUTE_HIDDEN, NULL   
  27.     );   
  28.   
  29.     WriteFile(hFile, pvResData, dwResSize, &dwWritten, NULL);   
  30.   
  31.     //释放有关内存资源   
  32.     CloseHandle(hFile);   
  33.     FreeResource(hResData);   
  34.   
  35.     return TRUE;   
  36.   
  37. }  

  

 

 

    调用代码为:

 

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

  

 

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

 

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

 

  1. BOOL FreeSource_2(LPCTSTR pszResName,LPCTSTR pszResType,LPCTSTR szFileName)   
  2. {   
  3.     BOOL bRet = TRUE;   
  4.     HINSTANCE hInst = GetModuleHandle(NULL);   
  5.   
  6.     //判断指定的资源是否存在   
  7.     HRSRC hResInfo = FindResource(hInst,pszResName,pszResType);   
  8.     if(hResInfo == NULL)return FALSE;   
  9.   
  10.     HANDLE hFile = NULL;   
  11.     DWORD dwWritten = 0;   
  12.   
  13.     //直接保存自定义资源,如JPG、GIF、EXE等   
  14.     if(pszResType != RT_BITMAP)   
  15.     {   
  16.         //使用LoadResource调入指定的资源到内存   
  17.         HGLOBAL hResData = LoadResource(hInst,hResInfo);   
  18.         LPVOID pvResData = LockResource(hResData);   
  19.         DWORD dwResSize = SizeofResource(hInst,hResInfo);   
  20.   
  21.         //创建目标文件并保存数据至文件   
  22.         hFile = CreateFile(   
  23.             szFileName,   
  24.             GENERIC_WRITE,    
  25.             FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,   
  26.             NULL,   
  27.             CREATE_ALWAYS,   
  28.             FILE_ATTRIBUTE_HIDDEN,   
  29.             NULL   
  30.         );   
  31.   
  32.         WriteFile(hFile, pvResData, dwResSize, &dwWritten, NULL);   
  33.     
  34.         //释放有关内存资源   
  35.         CloseHandle(hFile);   
  36.         FreeResource(hResData);   
  37.     }   
  38.     else //使用流技术保存BITMAP位图   
  39.     {   
  40.         //使用LoadBitmap调入指定的位图资源   
  41.         HBITMAP hBitmap = LoadBitmap(hInst, pszResName);   
  42.   
  43.         if(hBitmap == NULL)return FALSE;   
  44.   
  45.         //创建一个IPicture接口的实例   
  46.         PICTDESC ps;   
  47.         IPicture* pIPic=NULL;   
  48.   
  49.         ps.cbSizeofstruct = sizeof(PICTDESC);   
  50.         ps.picType = PICTYPE_BITMAP;   
  51.         ps.bmp.hbitmap = hBitmap;   
  52.         ps.bmp.hpal = NULL;   
  53.         if(OleCreatePictureIndirect(&ps, IID_IPicture, TRUE,(LPVOID*)&pIPic) != ERROR_SUCCESS)   
  54.         {   
  55.             DeleteObject(hBitmap);   
  56.             return FALSE;   
  57.         }   
  58.   
  59.         //将IPicture接口实例中的数据写入流接口中   
  60.         LONG lSize = 0;   
  61.         IStream* pIStm = NULL;   
  62.         CreateStreamOnHGlobal(NULL,TRUE,&pIStm);   
  63.         if(pIPic->SaveAsFile(pIStm,TRUE,&lSize) != ERROR_SUCCESS)   
  64.         {   
  65.             pIPic->Release();   
  66.             DeleteObject(hBitmap);   
  67.             return FALSE;   
  68.         }   
  69.   
  70.         //保存流接口中的数据   
  71.         LARGE_INTEGER li;   
  72.         li.HighPart = 0;   
  73.         li.LowPart = 0;   
  74.         ULARGE_INTEGER uli;   
  75.         uli.HighPart = 0;   
  76.         uli.LowPart = 0;   
  77.         pIStm->Seek(li, STREAM_SEEK_SET, &uli);   
  78.   
  79.         //创建目标文件并循环读取流中的数据保存至文件   
  80.         char buffer[1024];   
  81.         hFile = CreateFile(   
  82.             szFileName,   
  83.             GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,   
  84.             NULL,   
  85.             CREATE_ALWAYS,   
  86.             FILE_ATTRIBUTE_HIDDEN,   
  87.             NULL);   
  88.   
  89.         DWORD dwRead = 1;   
  90.         while(dwRead>0)   
  91.         {   
  92.             pIStm->Read(buffer, 1024, &dwRead);   
  93.             if(dwRead > 0)   
  94.                 WriteFile(hFile, buffer, dwRead, &dwWritten, NULL);   
  95.         }   
  96.   
  97.         //释放有关内存资源   
  98.         CloseHandle(hFile);   
  99.         DeleteObject(hBitmap);   
  100.         pIPic->Release();   
  101.         pIStm->Release();   
  102.     }   
  103.     return TRUE;   
  104. }  

 

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

 

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

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

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

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

 

  1. BOOL FreeSource_3(LPCTSTR pszResName,LPCTSTR pszResType,LPCTSTR szFileName)   
  2. {   
  3.     BOOL bRet = TRUE;   
  4.     HINSTANCE hInst = GetModuleHandle(NULL);   
  5.   
  6.     //判断指定的资源是否存在   
  7.     HRSRC hResInfo = FindResource(hInst,pszResName,pszResType);   
  8.     if(hResInfo == NULL)   
  9.         return FALSE;   
  10.   
  11.     HANDLE hFile = NULL;   
  12.     DWORD dwWritten = 0;   
  13.     //调入指定的资源到内存   
  14.     HGLOBAL hResData = LoadResource(hInst,hResInfo);   
  15.     LPVOID pvResData = LockResource(hResData);   
  16.     DWORD dwResSize = SizeofResource(hInst,hResInfo);   
  17.   
  18.     //创建目标文件并保存数据至文件   
  19.     hFile = CreateFile(szFileName, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);   
  20.     if(pszResType == RT_BITMAP)   
  21.     {   
  22.         //获得位图信息头   
  23.         BITMAPINFOHEADER bmpInfoHdr;   
  24.         CopyMemory(&bmpInfoHdr, pvResData, sizeof(BITMAPINFOHEADER));   
  25.         //获得位图颜色表中RGBQUAD结构的个数   
  26.         long nColorEntries;   
  27.         switch(bmpInfoHdr.biBitCount)   
  28.         {   
  29.         case 1:   
  30.             nColorEntries = 2;   
  31.             break;   
  32.         case 4:   
  33.             nColorEntries = 16;   
  34.             break;   
  35.         case 8:   
  36.             nColorEntries = 256;   
  37.             break;   
  38.         default:   
  39.             nColorEntries = 0;   
  40.         }   
  41.         //根据位图信息头创建位图文件头   
  42.         BITMAPFILEHEADER bmpFileHdr;   
  43.         bmpFileHdr.bfType = 0x4d42;  // 'BM'   
  44.         bmpFileHdr.bfSize = sizeof(BITMAPINFOHEADER) + dwResSize;   
  45.         bmpFileHdr.bfReserved1 = bmpFileHdr.bfReserved2 = 0;   
  46.         bmpFileHdr.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * nColorEntries;   
  47.         //写入位图文件头   
  48.         WriteFile(hFile, &bmpFileHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);   
  49.     }   
  50.     WriteFile(hFile, pvResData, dwResSize, &dwWritten, NULL);   
  51.     //释放有关内存资源   
  52.     CloseHandle(hFile);   
  53.     FreeResource(hResData);   
  54.     return TRUE;   
  55. };  

  

 

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值