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

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

2012-09-14 22:56 工业·编程 ⁄ 共 5590字 ⁄ 字号 暂无评论

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

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

 

 

    调用代码为:

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

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

 

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

 

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

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

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

 

给我留言

留言无头像?