Windows2003-3790/windows/appcompat/shims/layer/correctbitmapheader.cpp
2020-09-30 16:53:55 +02:00

622 lines
16 KiB
C++

/*++
Copyright (c) 2000-2002 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
02/14/2002 mnikkel Converted to strsafe
--*/
#include "precomp.h"
#include <userenv.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);
BOOL CheckForTemporaryDirA(
LPSTR pszEnvVar,
LPSTR pszTemporaryDir,
DWORD dwTempSize
)
{
// Get Environment Variable string
DWORD dwSize = GetEnvironmentVariableA(pszEnvVar, pszTemporaryDir, dwTempSize);
// If dwSize is zero then we failed to find the string. Fail.
if (dwSize == 0)
{
return FALSE;
}
// If dwSize is greater than dwTempSize then the buffer is too small. Fail with DPFN.
if (dwSize > dwTempSize)
{
DPFN( eDbgLevelError, "[CheckTemporaryDirA] Buffer to hold %s directory path is too small.",
pszEnvVar);
return FALSE;
}
// Check to see if the string is a directory path.
DWORD dwAttrib = GetFileAttributesA(pszTemporaryDir);
if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
{
return FALSE;
}
// We have a valid directory.
return TRUE;
}
/*++
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 pszTemporaryDir - 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
02/20/2002 mnikkel Converted to strsafe.
--*/
BOOL
GetTemporaryDirA(
LPSTR pszTemporaryDir,
DWORD dwTempSize
)
{
// Sanity check
if (pszTemporaryDir == NULL || dwTempSize < 1)
{
return FALSE;
}
// Check for a TEMP environment variable
if (CheckForTemporaryDirA("TEMP", pszTemporaryDir, dwTempSize) == FALSE)
{
// Didn't find TEMP, try TMP
if (CheckForTemporaryDirA("TMP", pszTemporaryDir, dwTempSize) == FALSE)
{
HANDLE hToken = INVALID_HANDLE_VALUE;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken) &&
GetUserProfileDirectoryA(hToken, pszTemporaryDir, &dwTempSize))
{
DWORD dwAttrib =GetFileAttributesA(pszTemporaryDir);
if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
{
DPFN( eDbgLevelError, "[GetTemporaryDirA] Unable to find an appropriate temp directory");
return FALSE;
}
}
else
{
DPFN( eDbgLevelError, "[GetTemporaryDirA] Unable to find 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,
DWORD dwNewSize
)
{
CHAR szDir[MAX_PATH];
CHAR szTempFile[MAX_PATH];
if (pszFile != NULL && pszNewFile != NULL && dwNewSize > 0)
{
// Find a temporary directory we can use
if (GetTemporaryDirA(szDir, MAX_PATH))
{
// create a temp file name
if (GetTempFileNameA(szDir, "abc", 0, szTempFile) != 0)
{
// Copy temp path to buffer
if (StringCchCopyA(pszNewFile, dwNewSize, szTempFile) == S_OK &&
StringCchCatA(pszNewFile, dwNewSize, ".bmp") == S_OK)
{
if (MoveFileA(szTempFile, pszNewFile) &&
CopyFileA(pszFile, pszNewFile, FALSE) &&
SetFileAttributesA(pszNewFile, FILE_ATTRIBUTE_NORMAL))
{
return TRUE;
}
}
}
}
}
return FALSE;
}
/*++
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,
DWORD dwNewSize
)
{
BOOL fIsSuccess = FALSE;
HANDLE hFile = NULL;
HANDLE hFileMap = NULL;
LPBYTE pFileMap = NULL;
if (pszFile == NULL || pszNewFile == NULL || dwNewSize < 1)
{
goto EXIT;
}
if (!IsBadReadPtr(pszFile, 1))
{
hFile = CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hFileMap != NULL)
{
pFileMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
if (pFileMap != NULL)
{
BITMAPINFOHEADER* 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, dwNewSize))
{
DPFN( eDbgLevelInfo, "[ProcessHeaderInFileA] Created a temp file %s", pszNewFile);
fIsSuccess = TRUE;
}
else
{
DPFN( eDbgLevelError, "[ProcessHeaderInFileA] Error create the temp file");
}
}
else
{
DPFN( eDbgLevelInfo, "[ProcessHeaderInFileA] The Bitmap header looks OK");
}
}
}
}
}
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 = FALSE;
HANDLE hFileMap = NULL;
LPBYTE pFileMap = NULL;
if (pszFile == NULL)
{
return fIsSuccess;
}
// Open file
HANDLE hFile = CreateFileA(
pszFile,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hFileMap != NULL)
{
pFileMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);
if (pFileMap != NULL)
{
BITMAPFILEHEADER* pbfh = (BITMAPFILEHEADER*)pFileMap;
BITMAPINFOHEADER* pbih = (BITMAPINFOHEADER*)(pFileMap + sizeof(BITMAPFILEHEADER));
// We make the image size the bitmap data size.
pbih->biSizeImage = GetFileSize(hFile, NULL) - pbfh->bfOffBits;
fIsSuccess = TRUE;
}
}
}
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 = FindResourceA(hinst, pszName, (LPCSTR)RT_BITMAP);
if (hrcBmp != NULL && phglbBmp)
{
*phglbBmp = LoadResource(hinst, hrcBmp);
if (*phglbBmp != NULL)
{
BITMAPINFOHEADER* pbih = (BITMAPINFOHEADER*)LockResource(*phglbBmp);
if (pbih && 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
)
{
// First call LoadImage see if it succeeds.
HANDLE hImage = ORIGINAL_API(LoadImageA)(
hinst,
lpszName,
uType,
cxDesired,
cyDesired,
fuLoad);
if (hImage)
{
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, MAX_PATH))
{
// 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
)
{
// First call LoadImage see if it succeeds.
HBITMAP hImage = ORIGINAL_API(LoadBitmapA)(hInstance, lpBitmapName);
if (hImage)
{
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