545 lines
13 KiB
C++
545 lines
13 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CorrectBitmapHeader.cpp
|
|
|
|
Abstract:
|
|
If a BITMAPINFOHEADER specifies a non BI_RGB value for biCompression, it
|
|
is supposed to specify a non zero biSizeImage.
|
|
|
|
Notes:
|
|
|
|
This is a general purpose shim.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
03/15/2001 robkenny Converted to CString
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include <userenv.h>
|
|
|
|
// This module has been given an official blessing to use the str routines.
|
|
#include "LegalStr.h"
|
|
|
|
IMPLEMENT_SHIM_BEGIN(CorrectBitmapHeader)
|
|
#include "ShimHookMacro.h"
|
|
|
|
APIHOOK_ENUM_BEGIN
|
|
APIHOOK_ENUM_ENTRY(LoadImageA)
|
|
APIHOOK_ENUM_ENTRY(LoadBitmapA)
|
|
APIHOOK_ENUM_END
|
|
|
|
typedef HBITMAP (*_pfn_LoadBitmapA)(HINSTANCE hinst, LPCSTR lpszName);
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Get the temporary directory. We don't use GetTempPath here because it doesn't verify if
|
|
the temporary directory (specified by either the TEMP or the TMP enviorment variable) exists.
|
|
If no TEMP or TMP is defined or the directory doesn't exist we get the user profile directory.
|
|
|
|
Arguments:
|
|
|
|
IN/OUT pszTemparoryDir - buffer to hold the temp directory upon return.
|
|
|
|
Return Value:
|
|
|
|
TRUE - we are able to find an appropriate temporary directory.
|
|
FALSE otherwise.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
GetTemporaryDirA(
|
|
LPSTR pszTemparoryDir
|
|
)
|
|
{
|
|
if ((GetEnvironmentVariableA("TEMP", pszTemparoryDir, MAX_PATH) == 0) ||
|
|
!(GetFileAttributesA(pszTemparoryDir) & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
if ((GetEnvironmentVariableA("TMP", pszTemparoryDir, MAX_PATH) == 0) ||
|
|
!(GetFileAttributesA(pszTemparoryDir) & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
DWORD dwSize = MAX_PATH;
|
|
|
|
if ((OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken) != 0) &&
|
|
(GetUserProfileDirectoryA(hToken, pszTemparoryDir, &dwSize) != 0) &&
|
|
(GetFileAttributesA(pszTemparoryDir) & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
// All failed. If the user has a system like that, we might as well
|
|
// just forget about fixing this for him.
|
|
DPFN( eDbgLevelError, "[GetTemporaryDirA] Error finding an appropriate temp directory");
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelInfo, "[GetTemporaryDirA] found TMP var");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelInfo, "[GetTemporaryDirA] found TEMP var");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Copy the original file to a temporary file in the temporary directory. We use GetTempFileName
|
|
to generate a temporary name but append .bmp to the filename because LoadImage doesn't recognize
|
|
it if it doesn't have a .bmp extension.
|
|
|
|
Arguments:
|
|
|
|
IN pszFile - the name of the original file.
|
|
IN/OUT pszNewFile - buffer to hold the new file name upon return.
|
|
|
|
Return Value:
|
|
|
|
TRUE - we are able to create a temporary file.
|
|
FALSE otherwise.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
CreateTempFileA(
|
|
LPCSTR pszFile,
|
|
LPSTR pszNewFile
|
|
)
|
|
{
|
|
CHAR szDir[MAX_PATH];
|
|
CHAR szTempFile[MAX_PATH];
|
|
return (GetTemporaryDirA(szDir) &&
|
|
(GetTempFileNameA(szDir, "abc", 0, szTempFile) != 0) &&
|
|
strcpy(pszNewFile, szTempFile) &&
|
|
strcat(pszNewFile, ".bmp") &&
|
|
MoveFileA(szTempFile, pszNewFile) &&
|
|
CopyFileA(pszFile, pszNewFile, FALSE) &&
|
|
SetFileAttributesA(pszNewFile, FILE_ATTRIBUTE_NORMAL));
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Clean up the mapped file.
|
|
|
|
Arguments:
|
|
|
|
IN hFile - handle to the file.
|
|
IN hFileMap - handle to the file view.
|
|
IN pFileMap - pointer to the file view.
|
|
|
|
Return Value:
|
|
|
|
VOID.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
CleanupFileMapping(
|
|
HANDLE hFile,
|
|
HANDLE hFileMap,
|
|
LPVOID pFileMap)
|
|
{
|
|
if (pFileMap != NULL)
|
|
{
|
|
UnmapViewOfFile(pFileMap);
|
|
}
|
|
|
|
if (hFileMap)
|
|
{
|
|
CloseHandle(hFileMap);
|
|
}
|
|
|
|
if (hFile && (hFile != INVALID_HANDLE_VALUE))
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Examine the BITMAPINFOHEADER from a bitmap file and decide if we need to fix it.
|
|
|
|
Arguments:
|
|
|
|
IN pszFile - the name of the .bmp file.
|
|
IN/OUT pszNewFile - buffer to hold the temporary file name if the function returns TRUE.
|
|
|
|
Return Value:
|
|
|
|
TRUE - We need to correct the header and we successfully copied the file to a temporary file.
|
|
FALSE - Either we don't need to correct the header or we failed to create a temporary file.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
ProcessHeaderInFileA(
|
|
LPCSTR pszFile,
|
|
LPSTR pszNewFile
|
|
)
|
|
{
|
|
BOOL fIsSuccess = TRUE;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hFileMap = NULL;
|
|
LPBYTE pFileMap = NULL;
|
|
BITMAPINFOHEADER* pbih = NULL;
|
|
|
|
if (!IsBadReadPtr(pszFile, 1) &&
|
|
((hFile = CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) != INVALID_HANDLE_VALUE) &&
|
|
((hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL) &&
|
|
((pFileMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0)) != NULL))
|
|
{
|
|
pbih = (BITMAPINFOHEADER*)(pFileMap + sizeof(BITMAPFILEHEADER));
|
|
|
|
if (pbih->biSizeImage == 0 && pbih->biCompression != BI_RGB)
|
|
{
|
|
// We need to correct the header by creating a new bmp file that
|
|
// is identical to the original file except the header has correct
|
|
// image size.
|
|
if (CreateTempFileA(pszFile, pszNewFile))
|
|
{
|
|
DPFN( eDbgLevelInfo, "[FixHeaderInFileA] Created a temp file %s", pszNewFile);
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, "[FixHeaderInFileA] Error create the temp file");
|
|
fIsSuccess = FALSE;
|
|
goto EXIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelInfo, "[FixHeaderInFileA] The Bitmap header looks OK");
|
|
fIsSuccess = FALSE;
|
|
goto EXIT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, "[FixHeaderInFileA] Error creating file mapping");
|
|
fIsSuccess = FALSE;
|
|
goto EXIT;
|
|
}
|
|
|
|
EXIT:
|
|
|
|
CleanupFileMapping(hFile, hFileMap, pFileMap);
|
|
return fIsSuccess;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Make the biSizeImage field of the BITMAPINFOHEADER struct the size of the bitmap data.
|
|
|
|
Arguments:
|
|
|
|
IN pszFile - the name of the .bmp file.
|
|
|
|
Return Value:
|
|
|
|
TRUE - We successfully corrected the header.
|
|
FALSE otherwise.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
BOOL FixHeaderInFileA(
|
|
LPCSTR pszFile
|
|
)
|
|
{
|
|
BOOL fIsSuccess = TRUE;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hFileMap = NULL;
|
|
LPBYTE pFileMap = NULL;
|
|
BITMAPINFOHEADER* pbih = NULL;
|
|
BITMAPFILEHEADER* pbfh = NULL;
|
|
|
|
if (((hFile = CreateFileA(
|
|
pszFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL)) != INVALID_HANDLE_VALUE) &&
|
|
((hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL)) != NULL) &&
|
|
((pFileMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0)) != NULL))
|
|
{
|
|
pbfh = (BITMAPFILEHEADER*)pFileMap;
|
|
pbih = (BITMAPINFOHEADER*)(pFileMap + sizeof(BITMAPFILEHEADER));
|
|
|
|
// We make the image size the bitmap data size.
|
|
pbih->biSizeImage = GetFileSize(hFile, NULL) - pbfh->bfOffBits;
|
|
}
|
|
else
|
|
{
|
|
fIsSuccess = FALSE;
|
|
}
|
|
|
|
CleanupFileMapping(hFile, hFileMap, pFileMap);
|
|
return fIsSuccess;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Adopted from the HowManyColors in \windows\core\ntuser\client\clres.c.
|
|
|
|
Arguments:
|
|
|
|
IN pbih - the BITMAPINFOHEADER* pointer.
|
|
|
|
Return Value:
|
|
|
|
The number of entries in the color table.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
DWORD HowManyColors(
|
|
BITMAPINFOHEADER* pbih
|
|
)
|
|
{
|
|
if (pbih->biClrUsed)
|
|
{
|
|
// If the bitmap header explicitly provides the number of colors
|
|
// in the color table, use it.
|
|
return (DWORD)pbih->biClrUsed;
|
|
}
|
|
else if (pbih->biBitCount <= 8)
|
|
{
|
|
// If the bitmap header describes a pallete-bassed bitmap
|
|
// (8bpp or less) then the color table must be big enough
|
|
// to hold all palette indecies.
|
|
return (1 << pbih->biBitCount);
|
|
}
|
|
else
|
|
{
|
|
// For highcolor+ bitmaps, there's no need for a color table.
|
|
// However, 16bpp and 32bpp bitmaps contain 3 DWORDS that
|
|
// describe the masks for the red, green, and blue components
|
|
// of entry in the bitmap.
|
|
if (pbih->biCompression == BI_BITFIELDS)
|
|
{
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Examine the BITMAPINFOHEADER in a bitmap resource and fix it as necessary.
|
|
|
|
Arguments:
|
|
|
|
IN hinst - the module instance where the bitmap resource resides.
|
|
IN pszName - the resource name.
|
|
OUT phglbBmp - the handle to the resource global memory.
|
|
|
|
Return Value:
|
|
|
|
TRUE - We successfully corrected the bitmap header if necessary.
|
|
FALSE - otherwise.
|
|
|
|
History:
|
|
|
|
10/18/2000 maonis Created
|
|
|
|
--*/
|
|
|
|
BOOL ProcessAndFixHeaderInResourceA(
|
|
HINSTANCE hinst, // handle to instance
|
|
LPCSTR pszName, // name or identifier of the image
|
|
HGLOBAL* phglbBmp
|
|
)
|
|
{
|
|
HRSRC hrcBmp = NULL;
|
|
BITMAPINFOHEADER* pbih = NULL;
|
|
|
|
if (((hrcBmp = FindResourceA(hinst, pszName, (LPCSTR)RT_BITMAP)) != NULL) &&
|
|
((*phglbBmp = LoadResource(hinst, hrcBmp)) != NULL) &&
|
|
((pbih = (BITMAPINFOHEADER*)LockResource(*phglbBmp))))
|
|
{
|
|
if (pbih->biSizeImage == 0 && pbih->biCompression != BI_RGB)
|
|
{
|
|
// We need to correct the header by setting the right size in memory.
|
|
pbih->biSizeImage =
|
|
SizeofResource(hinst, hrcBmp) -
|
|
sizeof(BITMAPINFOHEADER) -
|
|
HowManyColors(pbih) * sizeof(RGBQUAD);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
HANDLE
|
|
APIHOOK(LoadImageA)(
|
|
HINSTANCE hinst, // handle to instance
|
|
LPCSTR lpszName, // name or identifier of the image
|
|
UINT uType, // image type
|
|
int cxDesired, // desired width
|
|
int cyDesired, // desired height
|
|
UINT fuLoad // load options
|
|
)
|
|
{
|
|
HANDLE hImage = INVALID_HANDLE_VALUE;
|
|
|
|
// First call LoadImage see if it succeeds.
|
|
if (hImage = ORIGINAL_API(LoadImageA)(
|
|
hinst,
|
|
lpszName,
|
|
uType,
|
|
cxDesired,
|
|
cyDesired,
|
|
fuLoad))
|
|
{
|
|
return hImage;
|
|
}
|
|
|
|
if (uType != IMAGE_BITMAP)
|
|
{
|
|
DPFN( eDbgLevelInfo, "We don't fix the non-bitmap types");
|
|
return NULL;
|
|
}
|
|
|
|
// It failed. We'll correct the header.
|
|
if (fuLoad & LR_LOADFROMFILE)
|
|
{
|
|
CHAR szNewFile[MAX_PATH];
|
|
|
|
if (ProcessHeaderInFileA(lpszName, szNewFile))
|
|
{
|
|
// We now fix the bad header.
|
|
if (FixHeaderInFileA(szNewFile))
|
|
{
|
|
// Call the API with the new file.
|
|
hImage = ORIGINAL_API(LoadImageA)(hinst, szNewFile, uType, cxDesired, cyDesired, fuLoad);
|
|
|
|
// Delete the temporary file.
|
|
DeleteFileA(szNewFile);
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, "[LoadImageA] Error fixing the bad header in bmp file");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HGLOBAL hglbBmp = NULL;
|
|
|
|
if (ProcessAndFixHeaderInResourceA(hinst, lpszName, &hglbBmp))
|
|
{
|
|
hImage = ORIGINAL_API(LoadImageA)(hinst, lpszName, uType, cxDesired, cyDesired, fuLoad);
|
|
|
|
FreeResource(hglbBmp);
|
|
}
|
|
}
|
|
|
|
if (hImage)
|
|
{
|
|
LOGN( eDbgLevelInfo, "Bitmap header corrected");
|
|
}
|
|
|
|
return hImage;
|
|
}
|
|
|
|
HBITMAP
|
|
APIHOOK(LoadBitmapA)(
|
|
HINSTANCE hInstance, // handle to application instance
|
|
LPCSTR lpBitmapName // name of bitmap resource
|
|
)
|
|
{
|
|
HBITMAP hImage = NULL;
|
|
|
|
// First call LoadImage see if it succeeds.
|
|
if (hImage = ORIGINAL_API(LoadBitmapA)(hInstance, lpBitmapName))
|
|
{
|
|
return hImage;
|
|
}
|
|
|
|
HGLOBAL hglbBmp = NULL;
|
|
|
|
if (ProcessAndFixHeaderInResourceA(hInstance, lpBitmapName, &hglbBmp))
|
|
{
|
|
hImage = ORIGINAL_API(LoadBitmapA)(hInstance, lpBitmapName);
|
|
|
|
if (hImage)
|
|
{
|
|
LOGN( eDbgLevelInfo, "Bitmap header corrected");
|
|
}
|
|
|
|
FreeResource(hglbBmp);
|
|
}
|
|
|
|
return hImage;
|
|
}
|
|
|
|
/*++
|
|
|
|
Register hooked functions
|
|
|
|
--*/
|
|
|
|
HOOK_BEGIN
|
|
|
|
APIHOOK_ENTRY(USER32.DLL, LoadImageA)
|
|
APIHOOK_ENTRY(USER32.DLL, LoadBitmapA)
|
|
|
|
HOOK_END
|
|
|
|
IMPLEMENT_SHIM_END
|
|
|