前两天一个朋友叫我帮他写一段代码,想把资源中包含的图片释放成磁盘文件,我想这个应该很简单啊,于是便写了如下函数:
- 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类型的却无能为力。虽然保存到了磁盘上,却无法打开,提示为格式错误!
于是,我又自作聪明地使用流技术,把内存中的图像数据写回磁盘,代码如下:
果然不出所料,资源中所包含的位图被成功地保存到磁盘,并能被正确地打开了。于是我把代码给了朋友,算是完成了一个小任务。
今天下午,突然想起第一个函数写成的文件总比源文件少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会毁掉你辛苦完成的程序。