Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

621 lines
16 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. CorrectBitmapHeader.cpp
  5. Abstract:
  6. If a BITMAPINFOHEADER specifies a non BI_RGB value for biCompression, it
  7. is supposed to specify a non zero biSizeImage.
  8. Notes:
  9. This is a general purpose shim.
  10. History:
  11. 10/18/2000 maonis Created
  12. 03/15/2001 robkenny Converted to CString
  13. 02/14/2002 mnikkel Converted to strsafe
  14. --*/
  15. #include "precomp.h"
  16. #include <userenv.h>
  17. IMPLEMENT_SHIM_BEGIN(CorrectBitmapHeader)
  18. #include "ShimHookMacro.h"
  19. APIHOOK_ENUM_BEGIN
  20. APIHOOK_ENUM_ENTRY(LoadImageA)
  21. APIHOOK_ENUM_ENTRY(LoadBitmapA)
  22. APIHOOK_ENUM_END
  23. typedef HBITMAP (*_pfn_LoadBitmapA)(HINSTANCE hinst, LPCSTR lpszName);
  24. BOOL CheckForTemporaryDirA(
  25. LPSTR pszEnvVar,
  26. LPSTR pszTemporaryDir,
  27. DWORD dwTempSize
  28. )
  29. {
  30. // Get Environment Variable string
  31. DWORD dwSize = GetEnvironmentVariableA(pszEnvVar, pszTemporaryDir, dwTempSize);
  32. // If dwSize is zero then we failed to find the string. Fail.
  33. if (dwSize == 0)
  34. {
  35. return FALSE;
  36. }
  37. // If dwSize is greater than dwTempSize then the buffer is too small. Fail with DPFN.
  38. if (dwSize > dwTempSize)
  39. {
  40. DPFN( eDbgLevelError, "[CheckTemporaryDirA] Buffer to hold %s directory path is too small.",
  41. pszEnvVar);
  42. return FALSE;
  43. }
  44. // Check to see if the string is a directory path.
  45. DWORD dwAttrib = GetFileAttributesA(pszTemporaryDir);
  46. if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
  47. !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
  48. {
  49. return FALSE;
  50. }
  51. // We have a valid directory.
  52. return TRUE;
  53. }
  54. /*++
  55. Function Description:
  56. Get the temporary directory. We don't use GetTempPath here because it doesn't verify if
  57. the temporary directory (specified by either the TEMP or the TMP enviorment variable) exists.
  58. If no TEMP or TMP is defined or the directory doesn't exist we get the user profile directory.
  59. Arguments:
  60. IN/OUT pszTemporaryDir - buffer to hold the temp directory upon return.
  61. Return Value:
  62. TRUE - we are able to find an appropriate temporary directory.
  63. FALSE otherwise.
  64. - History:
  65. 10/18/2000 maonis Created
  66. 02/20/2002 mnikkel Converted to strsafe.
  67. --*/
  68. BOOL
  69. GetTemporaryDirA(
  70. LPSTR pszTemporaryDir,
  71. DWORD dwTempSize
  72. )
  73. {
  74. // Sanity check
  75. if (pszTemporaryDir == NULL || dwTempSize < 1)
  76. {
  77. return FALSE;
  78. }
  79. // Check for a TEMP environment variable
  80. if (CheckForTemporaryDirA("TEMP", pszTemporaryDir, dwTempSize) == FALSE)
  81. {
  82. // Didn't find TEMP, try TMP
  83. if (CheckForTemporaryDirA("TMP", pszTemporaryDir, dwTempSize) == FALSE)
  84. {
  85. HANDLE hToken = INVALID_HANDLE_VALUE;
  86. if (OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken) &&
  87. GetUserProfileDirectoryA(hToken, pszTemporaryDir, &dwTempSize))
  88. {
  89. DWORD dwAttrib =GetFileAttributesA(pszTemporaryDir);
  90. if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
  91. !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))
  92. {
  93. DPFN( eDbgLevelError, "[GetTemporaryDirA] Unable to find an appropriate temp directory");
  94. return FALSE;
  95. }
  96. }
  97. else
  98. {
  99. DPFN( eDbgLevelError, "[GetTemporaryDirA] Unable to find an appropriate temp directory");
  100. return FALSE;
  101. }
  102. }
  103. else
  104. {
  105. DPFN( eDbgLevelInfo, "[GetTemporaryDirA] found TMP var");
  106. }
  107. }
  108. else
  109. {
  110. DPFN( eDbgLevelInfo, "[GetTemporaryDirA] found TEMP var");
  111. }
  112. return TRUE;
  113. }
  114. /*++
  115. Function Description:
  116. Copy the original file to a temporary file in the temporary directory. We use GetTempFileName
  117. to generate a temporary name but append .bmp to the filename because LoadImage doesn't recognize
  118. it if it doesn't have a .bmp extension.
  119. Arguments:
  120. IN pszFile - the name of the original file.
  121. IN/OUT pszNewFile - buffer to hold the new file name upon return.
  122. Return Value:
  123. TRUE - we are able to create a temporary file.
  124. FALSE otherwise.
  125. History:
  126. 10/18/2000 maonis Created
  127. --*/
  128. BOOL
  129. CreateTempFileA(
  130. LPCSTR pszFile,
  131. LPSTR pszNewFile,
  132. DWORD dwNewSize
  133. )
  134. {
  135. CHAR szDir[MAX_PATH];
  136. CHAR szTempFile[MAX_PATH];
  137. if (pszFile != NULL && pszNewFile != NULL && dwNewSize > 0)
  138. {
  139. // Find a temporary directory we can use
  140. if (GetTemporaryDirA(szDir, MAX_PATH))
  141. {
  142. // create a temp file name
  143. if (GetTempFileNameA(szDir, "abc", 0, szTempFile) != 0)
  144. {
  145. // Copy temp path to buffer
  146. if (StringCchCopyA(pszNewFile, dwNewSize, szTempFile) == S_OK &&
  147. StringCchCatA(pszNewFile, dwNewSize, ".bmp") == S_OK)
  148. {
  149. if (MoveFileA(szTempFile, pszNewFile) &&
  150. CopyFileA(pszFile, pszNewFile, FALSE) &&
  151. SetFileAttributesA(pszNewFile, FILE_ATTRIBUTE_NORMAL))
  152. {
  153. return TRUE;
  154. }
  155. }
  156. }
  157. }
  158. }
  159. return FALSE;
  160. }
  161. /*++
  162. Function Description:
  163. Clean up the mapped file.
  164. Arguments:
  165. IN hFile - handle to the file.
  166. IN hFileMap - handle to the file view.
  167. IN pFileMap - pointer to the file view.
  168. Return Value:
  169. VOID.
  170. History:
  171. 10/18/2000 maonis Created
  172. --*/
  173. VOID
  174. CleanupFileMapping(
  175. HANDLE hFile,
  176. HANDLE hFileMap,
  177. LPVOID pFileMap)
  178. {
  179. if (pFileMap != NULL)
  180. {
  181. UnmapViewOfFile(pFileMap);
  182. }
  183. if (hFileMap)
  184. {
  185. CloseHandle(hFileMap);
  186. }
  187. if (hFile && (hFile != INVALID_HANDLE_VALUE))
  188. {
  189. CloseHandle(hFile);
  190. }
  191. }
  192. /*++
  193. Function Description:
  194. Examine the BITMAPINFOHEADER from a bitmap file and decide if we need to fix it.
  195. Arguments:
  196. IN pszFile - the name of the .bmp file.
  197. IN/OUT pszNewFile - buffer to hold the temporary file name if the function returns TRUE.
  198. Return Value:
  199. TRUE - We need to correct the header and we successfully copied the file to a temporary file.
  200. FALSE - Either we don't need to correct the header or we failed to create a temporary file.
  201. History:
  202. 10/18/2000 maonis Created
  203. --*/
  204. BOOL
  205. ProcessHeaderInFileA(
  206. LPCSTR pszFile,
  207. LPSTR pszNewFile,
  208. DWORD dwNewSize
  209. )
  210. {
  211. BOOL fIsSuccess = FALSE;
  212. HANDLE hFile = NULL;
  213. HANDLE hFileMap = NULL;
  214. LPBYTE pFileMap = NULL;
  215. if (pszFile == NULL || pszNewFile == NULL || dwNewSize < 1)
  216. {
  217. goto EXIT;
  218. }
  219. if (!IsBadReadPtr(pszFile, 1))
  220. {
  221. hFile = CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  222. if (hFile != INVALID_HANDLE_VALUE)
  223. {
  224. hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  225. if (hFileMap != NULL)
  226. {
  227. pFileMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
  228. if (pFileMap != NULL)
  229. {
  230. BITMAPINFOHEADER* pbih = (BITMAPINFOHEADER*)(pFileMap + sizeof(BITMAPFILEHEADER));
  231. if (pbih->biSizeImage == 0 && pbih->biCompression != BI_RGB)
  232. {
  233. // We need to correct the header by creating a new bmp file that
  234. // is identical to the original file except the header has correct
  235. // image size.
  236. if (CreateTempFileA(pszFile, pszNewFile, dwNewSize))
  237. {
  238. DPFN( eDbgLevelInfo, "[ProcessHeaderInFileA] Created a temp file %s", pszNewFile);
  239. fIsSuccess = TRUE;
  240. }
  241. else
  242. {
  243. DPFN( eDbgLevelError, "[ProcessHeaderInFileA] Error create the temp file");
  244. }
  245. }
  246. else
  247. {
  248. DPFN( eDbgLevelInfo, "[ProcessHeaderInFileA] The Bitmap header looks OK");
  249. }
  250. }
  251. }
  252. }
  253. }
  254. EXIT:
  255. CleanupFileMapping(hFile, hFileMap, pFileMap);
  256. return fIsSuccess;
  257. }
  258. /*++
  259. Function Description:
  260. Make the biSizeImage field of the BITMAPINFOHEADER struct the size of the bitmap data.
  261. Arguments:
  262. IN pszFile - the name of the .bmp file.
  263. Return Value:
  264. TRUE - We successfully corrected the header.
  265. FALSE otherwise.
  266. History:
  267. 10/18/2000 maonis Created
  268. --*/
  269. BOOL FixHeaderInFileA(
  270. LPCSTR pszFile
  271. )
  272. {
  273. BOOL fIsSuccess = FALSE;
  274. HANDLE hFileMap = NULL;
  275. LPBYTE pFileMap = NULL;
  276. if (pszFile == NULL)
  277. {
  278. return fIsSuccess;
  279. }
  280. // Open file
  281. HANDLE hFile = CreateFileA(
  282. pszFile,
  283. GENERIC_READ | GENERIC_WRITE,
  284. FILE_SHARE_READ | FILE_SHARE_WRITE,
  285. NULL,
  286. OPEN_EXISTING,
  287. 0,
  288. NULL);
  289. if (hFile != INVALID_HANDLE_VALUE)
  290. {
  291. hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
  292. if (hFileMap != NULL)
  293. {
  294. pFileMap = (LPBYTE)MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);
  295. if (pFileMap != NULL)
  296. {
  297. BITMAPFILEHEADER* pbfh = (BITMAPFILEHEADER*)pFileMap;
  298. BITMAPINFOHEADER* pbih = (BITMAPINFOHEADER*)(pFileMap + sizeof(BITMAPFILEHEADER));
  299. // We make the image size the bitmap data size.
  300. pbih->biSizeImage = GetFileSize(hFile, NULL) - pbfh->bfOffBits;
  301. fIsSuccess = TRUE;
  302. }
  303. }
  304. }
  305. CleanupFileMapping(hFile, hFileMap, pFileMap);
  306. return fIsSuccess;
  307. }
  308. /*++
  309. Function Description:
  310. Adopted from the HowManyColors in \windows\core\ntuser\client\clres.c.
  311. Arguments:
  312. IN pbih - the BITMAPINFOHEADER* pointer.
  313. Return Value:
  314. The number of entries in the color table.
  315. History:
  316. 10/18/2000 maonis Created
  317. --*/
  318. DWORD HowManyColors(
  319. BITMAPINFOHEADER* pbih
  320. )
  321. {
  322. if (pbih->biClrUsed)
  323. {
  324. // If the bitmap header explicitly provides the number of colors
  325. // in the color table, use it.
  326. return (DWORD)pbih->biClrUsed;
  327. }
  328. else if (pbih->biBitCount <= 8)
  329. {
  330. // If the bitmap header describes a pallete-bassed bitmap
  331. // (8bpp or less) then the color table must be big enough
  332. // to hold all palette indecies.
  333. return (1 << pbih->biBitCount);
  334. }
  335. else
  336. {
  337. // For highcolor+ bitmaps, there's no need for a color table.
  338. // However, 16bpp and 32bpp bitmaps contain 3 DWORDS that
  339. // describe the masks for the red, green, and blue components
  340. // of entry in the bitmap.
  341. if (pbih->biCompression == BI_BITFIELDS)
  342. {
  343. return 3;
  344. }
  345. }
  346. return 0;
  347. }
  348. /*++
  349. Function Description:
  350. Examine the BITMAPINFOHEADER in a bitmap resource and fix it as necessary.
  351. Arguments:
  352. IN hinst - the module instance where the bitmap resource resides.
  353. IN pszName - the resource name.
  354. OUT phglbBmp - the handle to the resource global memory.
  355. Return Value:
  356. TRUE - We successfully corrected the bitmap header if necessary.
  357. FALSE - otherwise.
  358. History:
  359. 10/18/2000 maonis Created
  360. --*/
  361. BOOL ProcessAndFixHeaderInResourceA(
  362. HINSTANCE hinst, // handle to instance
  363. LPCSTR pszName, // name or identifier of the image
  364. HGLOBAL* phglbBmp
  365. )
  366. {
  367. HRSRC hrcBmp = FindResourceA(hinst, pszName, (LPCSTR)RT_BITMAP);
  368. if (hrcBmp != NULL && phglbBmp)
  369. {
  370. *phglbBmp = LoadResource(hinst, hrcBmp);
  371. if (*phglbBmp != NULL)
  372. {
  373. BITMAPINFOHEADER* pbih = (BITMAPINFOHEADER*)LockResource(*phglbBmp);
  374. if (pbih && pbih->biSizeImage == 0 && pbih->biCompression != BI_RGB)
  375. {
  376. // We need to correct the header by setting the right size in memory.
  377. pbih->biSizeImage =
  378. SizeofResource(hinst, hrcBmp) -
  379. sizeof(BITMAPINFOHEADER) -
  380. HowManyColors(pbih) * sizeof(RGBQUAD);
  381. return TRUE;
  382. }
  383. }
  384. }
  385. return FALSE;
  386. }
  387. HANDLE
  388. APIHOOK(LoadImageA)(
  389. HINSTANCE hinst, // handle to instance
  390. LPCSTR lpszName, // name or identifier of the image
  391. UINT uType, // image type
  392. int cxDesired, // desired width
  393. int cyDesired, // desired height
  394. UINT fuLoad // load options
  395. )
  396. {
  397. // First call LoadImage see if it succeeds.
  398. HANDLE hImage = ORIGINAL_API(LoadImageA)(
  399. hinst,
  400. lpszName,
  401. uType,
  402. cxDesired,
  403. cyDesired,
  404. fuLoad);
  405. if (hImage)
  406. {
  407. return hImage;
  408. }
  409. if (uType != IMAGE_BITMAP)
  410. {
  411. DPFN( eDbgLevelInfo, "We don't fix the non-bitmap types");
  412. return NULL;
  413. }
  414. // It failed. We'll correct the header.
  415. if (fuLoad & LR_LOADFROMFILE)
  416. {
  417. CHAR szNewFile[MAX_PATH];
  418. if (ProcessHeaderInFileA(lpszName, szNewFile, MAX_PATH))
  419. {
  420. // We now fix the bad header.
  421. if (FixHeaderInFileA(szNewFile))
  422. {
  423. // Call the API with the new file.
  424. hImage = ORIGINAL_API(LoadImageA)(hinst, szNewFile, uType, cxDesired, cyDesired, fuLoad);
  425. // Delete the temporary file.
  426. DeleteFileA(szNewFile);
  427. }
  428. else
  429. {
  430. DPFN( eDbgLevelError, "[LoadImageA] Error fixing the bad header in bmp file");
  431. }
  432. }
  433. }
  434. else
  435. {
  436. HGLOBAL hglbBmp = NULL;
  437. if (ProcessAndFixHeaderInResourceA(hinst, lpszName, &hglbBmp))
  438. {
  439. hImage = ORIGINAL_API(LoadImageA)(hinst, lpszName, uType, cxDesired, cyDesired, fuLoad);
  440. FreeResource(hglbBmp);
  441. }
  442. }
  443. if (hImage)
  444. {
  445. LOGN( eDbgLevelInfo, "Bitmap header corrected");
  446. }
  447. return hImage;
  448. }
  449. HBITMAP
  450. APIHOOK(LoadBitmapA)(
  451. HINSTANCE hInstance, // handle to application instance
  452. LPCSTR lpBitmapName // name of bitmap resource
  453. )
  454. {
  455. // First call LoadImage see if it succeeds.
  456. HBITMAP hImage = ORIGINAL_API(LoadBitmapA)(hInstance, lpBitmapName);
  457. if (hImage)
  458. {
  459. return hImage;
  460. }
  461. HGLOBAL hglbBmp = NULL;
  462. if (ProcessAndFixHeaderInResourceA(hInstance, lpBitmapName, &hglbBmp))
  463. {
  464. hImage = ORIGINAL_API(LoadBitmapA)(hInstance, lpBitmapName);
  465. if (hImage)
  466. {
  467. LOGN( eDbgLevelInfo, "Bitmap header corrected");
  468. }
  469. FreeResource(hglbBmp);
  470. }
  471. return hImage;
  472. }
  473. /*++
  474. Register hooked functions
  475. --*/
  476. HOOK_BEGIN
  477. APIHOOK_ENTRY(USER32.DLL, LoadImageA)
  478. APIHOOK_ENTRY(USER32.DLL, LoadBitmapA)
  479. HOOK_END
  480. IMPLEMENT_SHIM_END