加载位图在 Windows 平台下是最稀松平常不过的事情。在最开始的设计中,Windows 希望可执行文件是一个自包容的个体,所以设计的 API 几乎都是只针对模块体内自带的资源的。后来终于发现大家对界面美化的需求日益迫切,尤其是经常需要对外部的位图文件进行加载,所以又另外增加了一个 API,既可以加载可执行模块内部的资源,又可以加载外部的资源,资源类型限于图标、光标和位图,这个 API 就是 LoadImage。
前两天在对 32 位位图进行研究时发现,LoadImage 竟然不能加载 32 位的位图文件(不过加载 32 位的位图资源是可以的)。网上也有人提出了此问题(http://groups.google.com/group/microsoft.public.win32.programmer.gdi/browse_thread/thread/9651a931dedd4acf/935122f52374326c%23935122f52374326c),一个来自于微软的的技术人员声称“应该”是可以的,但事实是老汉又遇到了同样的问题。好像以前也曾经遇到过 32 位位图的加载问题,不过姑且不论解决办法,连是否找到了解决办法都已经忘怀了。
幸好,32 位位图是位图格式里最简单的一个,所以自力更生也不需大费周折,于是有了下面的代码。为了方便地进行文件读写,还写了一个简单的文件映射类,该映射类的用于共享内存目的的构造函数没有写,谁要用的话,请自行补充完整。
注意,代码没有对位图文件中可能存在的 ICM 信息进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
class CFileMapping { protected: HANDLE _hFile; HANDLE _hFileMapping; PVOID _pvView; public: // Ctor for real file mapping object CFileMapping(LPCTSTR pszFile, DWORD dwAccess = FILE_GENERIC_READ | FILE_GENERIC_WRITE, DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE, PSECURITY_ATTRIBUTES psa = NULL, BOOL bMap = TRUE, LPCTSTR pszObject = NULL) : _hFile(NULL), _hFileMapping(NULL), _pvView(NULL) { _hFile = CreateFile(pszFile, dwAccess, dwShareMode, psa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(!_hFile) return; _hFileMapping = CreateFileMapping(_hFile, psa, PAGE_READWRITE, 0, 0, pszObject); if(_hFileMapping && bMap) _pvView = Map(); } // Ctor for shared memory object, not implemented, your duty? :-) CFileMapping(DWORD dwMaximumSizeLow, DWORD dwMaximumSizeHigh = 0, LPCTSTR pszObject = NULL) : _hFile(NULL), _hFileMapping(NULL), _pvView(NULL) { } ~CFileMapping() { Unmap(); if(_hFileMapping) CloseHandle(_hFileMapping); if(_hFile && _hFile != INVALID_HANDLE_VALUE) CloseHandle(_hFileMapping); } PVOID Map(SIZE_T dwNumberOfBytesToMap = 0, DWORD dwFileOffsetLow = 0, DWORD dwFileOffsetHigh = 0, DWORD dwAccess = FILE_MAP_ALL_ACCESS, PVOID pvBaseAddress = NULL) { Unmap(); if(_hFileMapping == NULL) return NULL; _pvView = MapViewOfFileEx(_hFileMapping, dwAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap, pvBaseAddress); return _pvView; } void Unmap() { if(_pvView) { UnmapViewOfFile(_pvView); _pvView = NULL; } } BOOL IsMapped() { return _pvView != NULL; } PVOID GetView() { return _pvView; } }; HBITMAP WINAPI LoadBitmap32Bits(LPCTSTR pszFile) { CFileMapping fm(pszFile); PBYTE pBytes = (PBYTE)fm.GetView(); if(!pBytes) return NULL; PBITMAPFILEHEADER pbfh = (PBITMAPFILEHEADER)pBytes; PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER)(pBytes + sizeof(BITMAPFILEHEADER)); if(pbih->biBitCount != 32) return NULL; PBYTE pBits = NULL; HBITMAP hbmp = CreateDIBSection(NULL, (PBITMAPINFO)pbih, DIB_RGB_COLORS, (PVOID*)&pBits, NULL, 0); if(!hbmp || !pBits) return NULL; pBytes += (pbfh->bfOffBits ? pbfh->bfOffBits : (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))); SIZE_T size = pbih->biWidth * pbih->biHeight * 4; CopyMemory(pBits, pBytes, size); return hbmp; }</span> |
更新(2007-07-24):
事实证明,老汉写的程序也是会有问题的。上面这个代码里有两个问题:
1、
if(!_hFile) 应该改成
if(!_hFile || _hFile == INVALID_HANDLE_VALE)
2、
FILE_GENERIC_READ | FILE_GENERIC_WRITE 应该改成
GENERIC_READ | GENERIC_WRITE
第一个显然是错误。第二个则不太明显,但是,如果不改,在 Windows CE 下运行就会失败,错误码为 87(参数错误)。
另外,下面再把上面空缺的那个构造函数的内容补上吧:
1 2 3 |
_hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, dwMaximumSizeLow, pszObject); if(_hFileMapping) _pvView = Map(dwMaximumSizeLow, 0, 0, FILE_MAP_ALL_ACCESS, NULL); |