Source code of Windows XP (NT5)
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.

1148 lines
37 KiB

  1. /*****************************************************************************\
  2. FILE: themeutils.cpp
  3. DESCRIPTION:
  4. This class will load and save the "Theme" settings from their persisted
  5. state.
  6. BryanSt 5/26/2000
  7. Copyright (C) Microsoft Corp 2000-2000. All rights reserved.
  8. \*****************************************************************************/
  9. #include "priv.h"
  10. #include "ThemePg.h"
  11. #include "ThemeFile.h"
  12. // Determine if we need to get this value from the effects tab.
  13. BOOL g_bGradient = TRUE;
  14. BOOL g_bReadOK, g_bWroteOK; // Save: read from reg/sys, write to file
  15. // Apply: not implemented since ignoring results anyway
  16. #define MAX_MSGLEN 512 // TRANSLATORS: English strs max 256
  17. // MAX_MSGLEN must be at least 2xMAX_STRLEN
  18. // MAX_MSGLEN must be at least 2xMAX_PATH
  19. TCHAR szCurDir[MAX_PATH+1]; // last dir opened a theme file from
  20. TCHAR szMsg[MAX_MSGLEN+1]; // scratch buffer
  21. //
  22. // *Path
  23. //
  24. // These routines help to make themes transportable between computers.
  25. // The problem is that the registry keeps filenames for the various
  26. // theme elements and, of course, these are hard-coded paths that vary
  27. // from machine to machine.
  28. //
  29. // The way we work around this problem is by storing filenames in the
  30. // theme file as _relative_ paths: relative to the theme file directory
  31. // or the Windows directory. (Actually, these routines are set up to
  32. // be relative to any number of directories.) When saving a filename to
  33. // a theme, we check to see if any relative paths can be abstracted out.
  34. // When retrieving a filename from a theme, we take the abstract placeholder
  35. // and replace it with the current sessions instances.
  36. // these must parallel each other. abstract strs must start with %
  37. void ExpandSZ(LPTSTR pszSrc, int cchSize)
  38. {
  39. LPTSTR pszTmp = (LPTSTR)LocalAlloc(GPTR, (MAX_PATH * sizeof(TCHAR)));
  40. if (pszTmp)
  41. {
  42. if (ExpandEnvironmentStrings(pszSrc, pszTmp, MAX_PATH))
  43. {
  44. StrCpyN(pszSrc, pszTmp, cchSize);
  45. }
  46. LocalFree(pszTmp);
  47. }
  48. }
  49. // HandGet
  50. //
  51. // Just a little helper routine, gets an individual string value from the
  52. // registry and returns it to the caller. Takes care of registry headaches,
  53. // including a paranoid length check before getting the string.
  54. //
  55. // NOTE that this function thinks it's getting a string value. If it's
  56. // another kind, this function will do OK: but the caller may be surprised
  57. // if expecting a string.
  58. //
  59. // Returns: success of string retrieval
  60. BOOL HandGet(HKEY hKeyRoot, LPTSTR lpszSubKey, LPTSTR lpszValName, LPTSTR lpszRet)
  61. {
  62. LONG lret;
  63. HKEY hKey; // cur open key
  64. BOOL bOK = TRUE;
  65. DWORD dwSize = 0;
  66. DWORD dwType;
  67. // inits
  68. // get subkey
  69. lret = RegOpenKeyEx( hKeyRoot, lpszSubKey,
  70. (DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKey );
  71. if (lret != ERROR_SUCCESS)
  72. {
  73. return (FALSE);
  74. }
  75. // now do our paranoid check of data size
  76. lret = RegQueryValueEx(hKey, lpszValName,
  77. (LPDWORD)NULL,
  78. (LPDWORD)&dwType,
  79. (LPBYTE)NULL, // null for size info only
  80. (LPDWORD)&dwSize );
  81. if (ERROR_SUCCESS == lret)
  82. { // saw something there
  83. // here's the size check before getting the data
  84. if (dwSize > (DWORD)(MAX_PATH * sizeof(TCHAR)))
  85. { // if string too big
  86. bOK = FALSE; // can't read, so very bad news
  87. g_bReadOK = FALSE;
  88. }
  89. else
  90. { // size is OK to continue
  91. // now really get the value
  92. lret = RegQueryValueEx(hKey, lpszValName,
  93. (LPDWORD)NULL,
  94. (LPDWORD)&dwType,
  95. (LPBYTE)lpszRet, // getting actual value
  96. (LPDWORD)&dwSize);
  97. // If this is an EXPAND_SZ we need to expand it...
  98. if (REG_EXPAND_SZ == dwType)
  99. {
  100. ExpandSZ(lpszRet, MAX_PATH);
  101. }
  102. if (ERROR_SUCCESS != lret)
  103. bOK = FALSE;
  104. }
  105. }
  106. else
  107. {
  108. bOK = FALSE;
  109. }
  110. // cleanup
  111. // close subkey
  112. RegCloseKey(hKey);
  113. return (bOK);
  114. }
  115. HRESULT _GetPlus98ThemesDir(LPTSTR pszPath, DWORD cchSize)
  116. {
  117. HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_PLUS98DIR, SZ_REGVALUE_PLUS98DIR, pszPath, cchSize);
  118. if (SUCCEEDED(hr))
  119. {
  120. TCHAR szSubDir[MAX_PATH];
  121. LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
  122. PathAppend(pszPath, szSubDir);
  123. }
  124. return hr;
  125. }
  126. HRESULT _GetPlus95ThemesDir(LPTSTR pszPath, DWORD cchSize)
  127. {
  128. HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_PLUS95DIR, SZ_REGVALUE_PLUS98DIR, pszPath, cchSize);
  129. if (SUCCEEDED(hr))
  130. {
  131. TCHAR szSubDir[MAX_PATH];
  132. LPTSTR pszFile = PathFindFileName(pszPath);
  133. if (pszFile)
  134. {
  135. // Plus!95 DestPath has "Plus!.dll" on the end so get rid of that.
  136. pszFile[0] = 0;
  137. }
  138. // Tack on a "Themes" onto the path
  139. LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
  140. PathAppend(pszPath, szSubDir);
  141. }
  142. return hr;
  143. }
  144. HRESULT _GetKidsThemesDir(LPTSTR pszPath, DWORD cchSize)
  145. {
  146. HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_KIDSDIR, SZ_REGVALUE_KIDSDIR, pszPath, cchSize);
  147. if (SUCCEEDED(hr))
  148. {
  149. TCHAR szSubDir[MAX_PATH];
  150. // Tack a "\Plus! for Kids\Themes" onto the path
  151. PathAppend(pszPath, TEXT("Plus! for Kids"));
  152. LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
  153. PathAppend(pszPath, szSubDir);
  154. }
  155. return hr;
  156. }
  157. HRESULT _GetHardDirThemesDir(LPTSTR pszPath, DWORD cchSize)
  158. {
  159. HRESULT hr = HrRegGetPath(HKEY_LOCAL_MACHINE, SZ_REGKEY_PROGRAMFILES, SZ_REGVALUE_PROGRAMFILESDIR, pszPath, cchSize);
  160. if (SUCCEEDED(hr))
  161. {
  162. TCHAR szSubDir[MAX_PATH];
  163. PathAppend(pszPath, TEXT("Plus!"));
  164. LoadString(HINST_THISDLL, IDS_THEMES_SUBDIR, szSubDir, ARRAYSIZE(szSubDir));
  165. PathAppend(pszPath, szSubDir);
  166. }
  167. return hr;
  168. }
  169. /*****************************************************************************\
  170. DESCRIPTION:
  171. Find any one of the directories that a previous plus pack may have
  172. installed. This may include Plus! 95, 98, kids, etc.
  173. \*****************************************************************************/
  174. HRESULT GetPlusThemeDir(IN LPTSTR pszPath, IN int cchSize)
  175. {
  176. HRESULT hr = S_OK;
  177. // The follwoing directories can contain themes:
  178. // Plus!98 Install Path\Themes
  179. // Plus!95 Install Path\Themes
  180. // Kids for Plus! Install Path\Themes
  181. // Program Files\Plus!\Themes
  182. hr = _GetPlus98ThemesDir(pszPath, cchSize);
  183. if (FAILED(hr))
  184. {
  185. hr = _GetPlus95ThemesDir(pszPath, cchSize);
  186. if (FAILED(hr))
  187. {
  188. hr = _GetKidsThemesDir(pszPath, cchSize);
  189. if (FAILED(hr))
  190. {
  191. hr = _GetHardDirThemesDir(pszPath, cchSize);
  192. }
  193. }
  194. }
  195. return hr;
  196. }
  197. TCHAR g_szThemeDir[MAX_PATH]; // dir of most theme files
  198. TCHAR g_szWinDir[MAX_PATH]; // Windows directory
  199. LPTSTR g_pszThemeValues[] = {g_szThemeDir, g_szWinDir, g_szWinDir};
  200. LPCTSTR g_pszThemeTokens[] = {TEXT("%ThemeDir%"), TEXT("%WinDir%"), TEXT("%SystemRoot%")};
  201. void ReplaceStringToken(IN LPCTSTR pszSource, IN LPCTSTR pszToken, IN LPCTSTR pszReplacement, IN LPTSTR pszDest, IN int cchSize)
  202. {
  203. LPCTSTR pszLastPart = &pszSource[lstrlen(pszToken)];
  204. if (L'\\' == pszLastPart[0])
  205. {
  206. pszLastPart++; // Skip past any slashes
  207. }
  208. StrCpyN(pszDest, pszReplacement, cchSize);
  209. PathAppend(pszDest, pszLastPart);
  210. }
  211. /*****************************************************************************\
  212. DESCRIPTION:
  213. Find any tokens in the path (%ThemeDir%, %WinDir%) and replace them
  214. with the correct paths.
  215. \*****************************************************************************/
  216. HRESULT ExpandThemeTokens(IN LPCTSTR pszThemeFile, IN LPTSTR pszPath, IN int cchSize)
  217. {
  218. HRESULT hr = S_OK;
  219. int nIndex;
  220. TCHAR szFinalPath[MAX_PATH];
  221. TCHAR szOriginalPath[MAX_PATH];
  222. szFinalPath[0] = 0;
  223. StrCpyN(szFinalPath, pszPath, ARRAYSIZE(szFinalPath));
  224. StrCpyN(szOriginalPath, pszPath, ARRAYSIZE(szOriginalPath));
  225. InitFrost();
  226. AssertMsg((0 != g_szThemeDir[0]), TEXT("Someone needs to call InitFrost() first to in this."));
  227. AssertMsg((0 != g_szWinDir[0]), TEXT("Someone needs to call InitFrost() first to in this."));
  228. for (nIndex = 0; nIndex < ARRAYSIZE(g_pszThemeTokens); nIndex++)
  229. {
  230. if (!StrCmpNI(g_pszThemeTokens[nIndex], szFinalPath, lstrlen(g_pszThemeTokens[nIndex]) - 1))
  231. {
  232. // We found the token to replace.
  233. TCHAR szTempPath[MAX_PATH];
  234. StrCpyN(szTempPath, szFinalPath, ARRAYSIZE(szTempPath));
  235. ReplaceStringToken(szTempPath, g_pszThemeTokens[nIndex], g_pszThemeValues[nIndex], szFinalPath, ARRAYSIZE(szFinalPath));
  236. if ((0 == nIndex) && !PathFileExists(szFinalPath))
  237. {
  238. // Sometimes the .theme file will not be in the Theme directory, so we need to try
  239. // the directory containing the .theme file.
  240. TCHAR szThemeDir[MAX_PATH];
  241. StrCpyN(szThemeDir, pszThemeFile, ARRAYSIZE(szThemeDir));
  242. PathRemoveFileSpec(szThemeDir);
  243. StrCpyN(szTempPath, szOriginalPath, ARRAYSIZE(szTempPath));
  244. ReplaceStringToken(szTempPath, g_pszThemeTokens[nIndex], szThemeDir, szFinalPath, ARRAYSIZE(szFinalPath));
  245. }
  246. else
  247. {
  248. // It worked
  249. }
  250. hr = S_OK;
  251. break;
  252. }
  253. }
  254. if (0 == SHExpandEnvironmentStringsForUserW(NULL, szFinalPath, pszPath, cchSize))
  255. {
  256. StrCpyN(pszPath, szFinalPath, cchSize);
  257. }
  258. return hr;
  259. }
  260. //
  261. // ConfirmFile
  262. //
  263. // This function does the "smart" file searching that's supposed to be
  264. // built into each resource file reference in applying themes.
  265. //
  266. // First see if the full pathname + file given actually exists.
  267. // If it does not, then try looking for the same filename (stripped from path)
  268. // in other standard directories, in this order:
  269. // Current Theme file directory
  270. // Theme switcher THEMES subdirectory
  271. // Windows directory
  272. // Windows/MEDIA directory
  273. // Windows/CURSORS directory
  274. // Windows/SYSTEM directory
  275. //
  276. // Input: LPTSTR lpszPath full pathname
  277. // BOOL bUpdate whether to alter the filename string with found file
  278. // Returns: int flag telling if and how file has been confirmed
  279. // CF_EXISTS pathname passed in was actual file
  280. // CF_FOUND file did not exist, but found same filename elsewhere
  281. // CF_NOTFOUND file did not exist, could not find elsewhere
  282. //
  283. int ConfirmFile(IN LPTSTR lpszPath, IN BOOL bUpdate)
  284. {
  285. TCHAR szWork[MAX_PATH+1];
  286. TCHAR szTest[MAX_PATH+1];
  287. int iret = CF_NOTFOUND; // default value
  288. LPTSTR lpFile;
  289. LPTSTR lpNumber;
  290. HANDLE hTest;
  291. // special case easy return: if it's null, then trivially satisfied.
  292. if (!*lpszPath)
  293. return CF_EXISTS; // NO WORK EXIT
  294. // Inits
  295. // copy pathname to a work string for the function
  296. StrCpyN(szWork, lpszPath, ARRAYSIZE(szWork));
  297. // input can be of the form foo.dll,13. need to strip off that comma,#
  298. // but hold onto it to put back at the end if we change the pathname
  299. lpNumber = StrChr(szWork, TEXT(','));
  300. if (lpNumber && *lpNumber)
  301. {
  302. // if there is a comma
  303. lpFile = lpNumber; // temp
  304. lpNumber = CharNext(lpNumber);// hold onto number
  305. *lpFile = 0;
  306. }
  307. // TODO: In Blackcomb we should call SHPathPrepareForWrite() in case
  308. // szWork is stored on removable media that the user should insert.
  309. // Do the checks
  310. // *** first check if the given file just exists as is
  311. hTest = CreateFile(szWork, GENERIC_READ, FILE_SHARE_READ,
  312. (LPSECURITY_ATTRIBUTES)NULL,
  313. OPEN_EXISTING,
  314. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  315. if (hTest != INVALID_HANDLE_VALUE)
  316. {
  317. // success
  318. iret = CF_EXISTS; // assign ret value
  319. // don't need to worry about bUpdate: found with input string
  320. }
  321. else // otherwise, let's go searching for the same filename in other dirs
  322. {
  323. // get ptr to the filename separated from the path
  324. lpFile = PathFindFileName(szWork);
  325. // *** try the cur theme file dir
  326. StrCpyN(szTest, szCurDir, ARRAYSIZE(szTest));
  327. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  328. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  329. (LPSECURITY_ATTRIBUTES)NULL,
  330. OPEN_EXISTING,
  331. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  332. if (hTest != INVALID_HANDLE_VALUE)
  333. { // success
  334. iret = CF_FOUND; // assign ret value
  335. }
  336. else // *** otherwise try the Theme switcher THEMES subdirectory
  337. {
  338. StrCpyN(szTest, g_szThemeDir, ARRAYSIZE(szTest));
  339. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  340. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  341. (LPSECURITY_ATTRIBUTES)NULL,
  342. OPEN_EXISTING,
  343. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  344. if (hTest != INVALID_HANDLE_VALUE)
  345. { // success
  346. iret = CF_FOUND; // assign ret value
  347. }
  348. else // *** otherwise try the win dir
  349. {
  350. StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
  351. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  352. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  353. (LPSECURITY_ATTRIBUTES)NULL,
  354. OPEN_EXISTING,
  355. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  356. if (hTest != INVALID_HANDLE_VALUE)
  357. { // success
  358. iret = CF_FOUND; // assign ret value
  359. }
  360. else // *** otherwise try the win/media dir
  361. {
  362. // can get this one directly from Registry
  363. HandGet(HKEY_LOCAL_MACHINE,
  364. TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"),
  365. TEXT("MediaPath"), szTest);
  366. #ifdef THEYREMOVEREGSETTING
  367. StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
  368. StrCatBuff(szTest, TEXT("Media\\"), ARRAYSIZE(szTest));
  369. #endif
  370. StrCatBuff(szTest, TEXT("\\"), ARRAYSIZE(szTest));
  371. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  372. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  373. (LPSECURITY_ATTRIBUTES)NULL,
  374. OPEN_EXISTING,
  375. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  376. if (hTest != INVALID_HANDLE_VALUE)
  377. { // success
  378. iret = CF_FOUND; // assign ret value
  379. }
  380. else // *** otherwise try the win/cursors dir
  381. {
  382. StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
  383. StrCatBuff(szTest, TEXT("CURSORS\\"), ARRAYSIZE(szTest));
  384. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  385. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  386. (LPSECURITY_ATTRIBUTES)NULL,
  387. OPEN_EXISTING,
  388. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  389. if (hTest != INVALID_HANDLE_VALUE)
  390. { // success
  391. iret = CF_FOUND; // assign ret value
  392. }
  393. else // *** otherwise try the win/system dir
  394. {
  395. StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
  396. StrCatBuff(szTest, TEXT("SYSTEM\\"), ARRAYSIZE(szTest));
  397. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  398. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  399. (LPSECURITY_ATTRIBUTES)NULL,
  400. OPEN_EXISTING,
  401. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  402. if (hTest != INVALID_HANDLE_VALUE)
  403. { // success
  404. iret = CF_FOUND; // assign ret value
  405. }
  406. else // *** otherwise try the win/system32 dir
  407. {
  408. StrCpyN(szTest, g_szWinDir, ARRAYSIZE(szTest));
  409. StrCatBuff(szTest, TEXT("SYSTEM32\\"), ARRAYSIZE(szTest));
  410. StrCatBuff(szTest, lpFile, ARRAYSIZE(szTest));
  411. hTest = CreateFile(szTest, GENERIC_READ, FILE_SHARE_READ,
  412. (LPSECURITY_ATTRIBUTES)NULL,
  413. OPEN_EXISTING,
  414. FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
  415. if (hTest != INVALID_HANDLE_VALUE)
  416. { // success
  417. iret = CF_FOUND; // assign ret value
  418. }
  419. }
  420. }
  421. }
  422. }
  423. }
  424. }
  425. // if found anywhere other than orig, copy found path/str as requested
  426. if ((iret == CF_FOUND) && bUpdate)
  427. {
  428. StrCpy(lpszPath, szTest);
  429. // if we stripped off a number, let's add it back on
  430. if (lpNumber && *lpNumber)
  431. {
  432. StrCatBuff(lpszPath, TEXT(","), MAX_PATH);
  433. StrCatBuff(lpszPath, lpNumber, MAX_PATH);
  434. }
  435. } // endif found file by searching
  436. }
  437. // cleanup
  438. if (iret != CF_NOTFOUND)
  439. CloseHandle(hTest); // close file if opened
  440. return (iret);
  441. }
  442. // InitFrost
  443. // Since there are no window classes to register, this routine just loads
  444. // the strings and, since there's only one instance, calls InitInstance().
  445. //
  446. // Returns: success of initialization
  447. void InitFrost(void)
  448. {
  449. static BOOL s_fInited = FALSE;
  450. if (FALSE == s_fInited)
  451. {
  452. BOOL bret;
  453. HDC hdc;
  454. s_fInited = TRUE;
  455. if (!GetWindowsDirectory(g_szWinDir, ARRAYSIZE(g_szWinDir)))
  456. {
  457. g_szWinDir[0] = 0;
  458. }
  459. if (FAILED(GetPlusThemeDir(g_szThemeDir, ARRAYSIZE(g_szThemeDir))))
  460. {
  461. StrCpyN(g_szThemeDir, g_szWinDir, ARRAYSIZE(g_szThemeDir));
  462. }
  463. // Initialize our g_bGradient flag
  464. // We may need to get the g_bGradient flag from the Effects tab.
  465. hdc = GetDC(NULL);
  466. g_bGradient = (BOOL)(GetDeviceCaps(hdc, BITSPIXEL) > 8);
  467. ReleaseDC(NULL, hdc);
  468. // init directory strings
  469. szCurDir[0];
  470. // default current dir
  471. StrCpyN(szCurDir, g_szThemeDir, ARRAYSIZE(szCurDir));
  472. // Windows directory
  473. if (TEXT('\\') != g_szWinDir[lstrlen(g_szWinDir)-1])
  474. {
  475. StrCatBuff(g_szWinDir, TEXT("\\"), ARRAYSIZE(g_szWinDir));
  476. }
  477. // see if there is a previous theme file to return to
  478. bret = HandGet(HKEY_CURRENT_USER, SZ_REGKEY_CURRENTTHEME, NULL, szMsg);
  479. if (bret)
  480. {
  481. // get init cur dir from prev theme file
  482. StrCpyN(szCurDir, szMsg, ARRAYSIZE(szCurDir));
  483. PathFindFileName(szCurDir)[0] = 0;
  484. }
  485. }
  486. }
  487. // ascii to integer conversion routine
  488. //
  489. // ***DEBUG*** int'l: is this true?
  490. // These don't need to be UNICODE/international ready, since they
  491. // *only* deal with strings from the Registry and our own private
  492. // INI files.
  493. /* CAREFUL!! This atoi just IGNORES non-numerics like decimal points!!! */
  494. /* checks for (>=1) leading negative sign */
  495. int latoi(LPSTR s)
  496. {
  497. int n;
  498. LPSTR pS;
  499. BOOL bSeenNum;
  500. BOOL bNeg;
  501. n=0;
  502. bSeenNum = bNeg = FALSE;
  503. for (pS=s; *pS; pS++) {
  504. if ((*pS >= '0') && (*pS <= '9')) {
  505. bSeenNum = TRUE;
  506. n = n*10 + (*pS - '0');
  507. }
  508. if (!bSeenNum && (*pS == '-')) {
  509. bNeg = TRUE;
  510. }
  511. }
  512. if (bNeg) {
  513. n = -n;
  514. }
  515. return(n);
  516. }
  517. //
  518. // Utility routine for above; takes ASCII string to binary in
  519. // global pValue[] buffer.
  520. //
  521. // Since the values this guy is manipulating is purely ASCII
  522. // numerics we should be able to get away with this char pointer
  523. // arithmetic. If they were not simple ASCII numerics I think
  524. // we could get into trouble with some DBCS chars
  525. //
  526. // Uses: writes binary data to global pValue[]
  527. //
  528. int WriteBytesToBuffer(IN LPTSTR pszInput, IN void * pOut, IN int cbSize)
  529. {
  530. LPTSTR lpszCur, lpszNext, lpszEnd;
  531. BYTE * pbCur = (BYTE *)pOut;
  532. int iTemp, iBytes;
  533. #ifdef UNICODE
  534. CHAR szTempA[10];
  535. #endif
  536. // inits
  537. lpszNext = pszInput;
  538. iBytes = 0;
  539. lpszEnd = pszInput + lstrlen(pszInput); // points to null term
  540. // translating loop
  541. while (*lpszNext && (lpszNext < lpszEnd) && (iBytes < cbSize))
  542. {
  543. // update str pointers
  544. // hold onto your starting place
  545. lpszCur = lpszNext;
  546. // advance pointer to next and null terminate cur
  547. while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
  548. *lpszNext = 0; lpszNext++;
  549. // on last number, this leaves lpszNext pointing past lpszEnd
  550. // translate this string-number into binary number and place in
  551. // output buffer.
  552. #ifdef UNICODE
  553. wcstombs(szTempA, lpszCur, sizeof(szTempA));
  554. iTemp = latoi(szTempA);
  555. #else // !UNICODE
  556. iTemp = latoi(lpszCur);
  557. #endif
  558. *pbCur = (BYTE)iTemp;
  559. pbCur++; // incr byte loc in output buffer
  560. // keep track of your bytes
  561. iBytes++;
  562. }
  563. //
  564. // cleanup
  565. return (iBytes);
  566. }
  567. HRESULT ConvertBinaryToINIByteString(BYTE * pBytes, DWORD cbSize, LPWSTR * ppszStringOut)
  568. {
  569. HRESULT hr = E_OUTOFMEMORY;
  570. *ppszStringOut = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * ((4 * cbSize) + 1));
  571. if (*ppszStringOut)
  572. {
  573. LPWSTR pszCurrent = *ppszStringOut;
  574. DWORD dwIndex;
  575. TCHAR szTemp[10];
  576. for (dwIndex = 0; dwIndex < cbSize; dwIndex++)
  577. {
  578. wnsprintf(szTemp, ARRAYSIZE(szTemp), TEXT("%d "), pBytes[dwIndex]);
  579. StrCpy(pszCurrent, szTemp);
  580. pszCurrent += lstrlen(szTemp);
  581. }
  582. hr = S_OK;
  583. }
  584. return hr;
  585. }
  586. void ConvertLogFontToWIDE(LPLOGFONTA aLF, LPLOGFONTW wLF)
  587. {
  588. ZeroMemory(wLF, sizeof(wLF));
  589. wLF->lfHeight = aLF->lfHeight;
  590. wLF->lfWidth = aLF->lfWidth;
  591. wLF->lfEscapement = aLF->lfEscapement;
  592. wLF->lfOrientation = aLF->lfOrientation;
  593. wLF->lfWeight = aLF->lfWeight;
  594. wLF->lfItalic = aLF->lfItalic;
  595. wLF->lfUnderline = aLF->lfUnderline;
  596. wLF->lfStrikeOut = aLF->lfStrikeOut;
  597. wLF->lfCharSet = aLF->lfCharSet;
  598. wLF->lfOutPrecision = aLF->lfOutPrecision;
  599. wLF->lfClipPrecision = aLF->lfClipPrecision;
  600. wLF->lfQuality = aLF->lfQuality;
  601. wLF->lfPitchAndFamily = aLF->lfPitchAndFamily;
  602. MultiByteToWideChar(CP_ACP, 0, aLF->lfFaceName, -1, wLF->lfFaceName, LF_FACESIZE);
  603. }
  604. void ConvertIconMetricsToWIDE(LPICONMETRICSA aIM, LPICONMETRICSW wIM)
  605. {
  606. ZeroMemory(wIM, sizeof(wIM));
  607. wIM->cbSize = sizeof(*wIM);
  608. wIM->iHorzSpacing = aIM->iHorzSpacing;
  609. wIM->iVertSpacing = aIM->iVertSpacing;
  610. wIM->iTitleWrap = aIM->iTitleWrap;
  611. ConvertLogFontToWIDE(&aIM->lfFont, &wIM->lfFont);
  612. }
  613. void ConvertNCMetricsToWIDE(LPNONCLIENTMETRICSA aNCM, LPNONCLIENTMETRICSW wNCM)
  614. {
  615. ZeroMemory(wNCM, sizeof(wNCM));
  616. wNCM->cbSize = sizeof(*wNCM);
  617. wNCM->iBorderWidth = aNCM->iBorderWidth;
  618. wNCM->iScrollWidth = aNCM->iScrollWidth;
  619. wNCM->iScrollHeight = aNCM->iScrollHeight;
  620. wNCM->iCaptionWidth = aNCM->iCaptionWidth;
  621. wNCM->iCaptionHeight = aNCM->iCaptionHeight;
  622. ConvertLogFontToWIDE(&aNCM->lfCaptionFont, &wNCM->lfCaptionFont);
  623. wNCM->iSmCaptionWidth = aNCM->iSmCaptionWidth;
  624. wNCM->iSmCaptionHeight = aNCM->iSmCaptionHeight;
  625. ConvertLogFontToWIDE(&aNCM->lfSmCaptionFont, &wNCM->lfSmCaptionFont);
  626. wNCM->iMenuWidth = aNCM->iMenuWidth;
  627. wNCM->iMenuHeight = aNCM->iMenuHeight;
  628. ConvertLogFontToWIDE(&aNCM->lfMenuFont, &wNCM->lfMenuFont);
  629. ConvertLogFontToWIDE(&aNCM->lfStatusFont, &wNCM->lfStatusFont);
  630. ConvertLogFontToWIDE(&aNCM->lfMessageFont, &wNCM->lfMessageFont);
  631. }
  632. void ConvertLogFontToANSI(LPLOGFONTW wLF, LPLOGFONTA aLF)
  633. {
  634. ZeroMemory(aLF, sizeof(aLF));
  635. aLF->lfHeight = wLF->lfHeight;
  636. aLF->lfWidth = wLF->lfWidth;
  637. aLF->lfEscapement = wLF->lfEscapement;
  638. aLF->lfOrientation = wLF->lfOrientation;
  639. aLF->lfWeight = wLF->lfWeight;
  640. aLF->lfItalic = wLF->lfItalic;
  641. aLF->lfUnderline = wLF->lfUnderline;
  642. aLF->lfStrikeOut = wLF->lfStrikeOut;
  643. aLF->lfCharSet = wLF->lfCharSet;
  644. aLF->lfOutPrecision = wLF->lfOutPrecision;
  645. aLF->lfClipPrecision = wLF->lfClipPrecision;
  646. aLF->lfQuality = wLF->lfQuality;
  647. aLF->lfPitchAndFamily = wLF->lfPitchAndFamily;
  648. SHUnicodeToAnsi(wLF->lfFaceName, aLF->lfFaceName, ARRAYSIZE(aLF->lfFaceName));
  649. }
  650. void ConvertIconMetricsToANSI(LPICONMETRICSW wIM, LPICONMETRICSA aIM)
  651. {
  652. ZeroMemory(aIM, sizeof(aIM));
  653. aIM->cbSize = sizeof(aIM);
  654. aIM->iHorzSpacing = wIM->iHorzSpacing;
  655. aIM->iVertSpacing = wIM->iVertSpacing;
  656. aIM->iTitleWrap = wIM->iTitleWrap;
  657. ConvertLogFontToANSI(&wIM->lfFont, &aIM->lfFont);
  658. }
  659. void ConvertNCMetricsToANSI(LPNONCLIENTMETRICSW wNCM, LPNONCLIENTMETRICSA aNCM)
  660. {
  661. ZeroMemory(aNCM, sizeof(aNCM));
  662. aNCM->cbSize = sizeof(*aNCM);
  663. aNCM->iBorderWidth = wNCM->iBorderWidth;
  664. aNCM->iScrollWidth = wNCM->iScrollWidth;
  665. aNCM->iScrollHeight = wNCM->iScrollHeight;
  666. aNCM->iCaptionWidth = wNCM->iCaptionWidth;
  667. aNCM->iCaptionHeight = wNCM->iCaptionHeight;
  668. ConvertLogFontToANSI(&wNCM->lfCaptionFont, &aNCM->lfCaptionFont);
  669. aNCM->iSmCaptionWidth = wNCM->iSmCaptionWidth;
  670. aNCM->iSmCaptionHeight = wNCM->iSmCaptionHeight;
  671. ConvertLogFontToANSI(&wNCM->lfSmCaptionFont, &aNCM->lfSmCaptionFont);
  672. aNCM->iMenuWidth = wNCM->iMenuWidth;
  673. aNCM->iMenuHeight = wNCM->iMenuHeight;
  674. ConvertLogFontToANSI(&wNCM->lfMenuFont, &aNCM->lfMenuFont);
  675. ConvertLogFontToANSI(&wNCM->lfStatusFont, &aNCM->lfStatusFont);
  676. ConvertLogFontToANSI(&wNCM->lfMessageFont, &aNCM->lfMessageFont);
  677. }
  678. HRESULT GetIconMetricsFromSysMetricsAll(SYSTEMMETRICSALL * pSystemMetrics, LPICONMETRICSA pIconMetrics, DWORD cchSize)
  679. {
  680. HRESULT hr = E_INVALIDARG;
  681. if (pIconMetrics && (sizeof(*pIconMetrics) == cchSize))
  682. {
  683. ZeroMemory(pIconMetrics, sizeof(*pIconMetrics));
  684. pIconMetrics->cbSize = sizeof(*pIconMetrics);
  685. SystemParametersInfoA(SPI_GETICONMETRICS, sizeof(*pIconMetrics), pIconMetrics, 0);
  686. ConvertLogFontToANSI(&pSystemMetrics->schemeData.lfIconTitle, &pIconMetrics->lfFont);
  687. hr = S_OK;
  688. }
  689. return hr;
  690. }
  691. //
  692. // TransmitFontCharacteristics
  693. //
  694. // This is actually a pretty key function. See, font characteristics are
  695. // all set together: a LOGFONT has name and style and size info all in one.
  696. // But when you are setting all the nonclient metrics like window caption
  697. // and menu size, you need to stretch the font sizes with it. But we give the
  698. // user a choice of changing window sizes without "changing" the font; i.e.
  699. // without applying a new font name and style from the theme.
  700. //
  701. // So we need to be able to pick apart the name and style from the size
  702. // characteristics. And here it is.
  703. //
  704. // Really just a helper routine for the above function, so we don't have all
  705. // this gunk inline five times.
  706. //
  707. void TransmitFontCharacteristics(PLOGFONT plfDst, PLOGFONT plfSrc, int iXmit)
  708. {
  709. switch (iXmit)
  710. {
  711. case TFC_SIZE:
  712. plfDst->lfHeight = plfSrc->lfHeight;
  713. plfDst->lfWidth = plfSrc->lfWidth;
  714. break;
  715. case TFC_STYLE:
  716. plfDst->lfEscapement = plfSrc->lfEscapement;
  717. plfDst->lfOrientation = plfSrc->lfOrientation;
  718. plfDst->lfWeight = plfSrc->lfWeight;
  719. plfDst->lfItalic = plfSrc->lfItalic;
  720. plfDst->lfUnderline = plfSrc->lfUnderline;
  721. plfDst->lfStrikeOut = plfSrc->lfStrikeOut;
  722. plfDst->lfCharSet = plfSrc->lfCharSet;
  723. plfDst->lfOutPrecision = plfSrc->lfOutPrecision;
  724. plfDst->lfClipPrecision = plfSrc->lfClipPrecision;
  725. plfDst->lfQuality = plfSrc->lfQuality;
  726. plfDst->lfPitchAndFamily = plfSrc->lfPitchAndFamily;
  727. lstrcpy(plfDst->lfFaceName, plfSrc->lfFaceName);
  728. break;
  729. }
  730. }
  731. // RGB to String to RGB utilities.
  732. COLORREF RGBStringToColor(LPTSTR lpszRGB)
  733. {
  734. LPTSTR lpszCur, lpszNext;
  735. BYTE bRed, bGreen, bBlue;
  736. #ifdef UNICODE
  737. CHAR szTempA[10];
  738. #endif
  739. // inits
  740. lpszNext = lpszRGB;
  741. // set up R for translation
  742. lpszCur = lpszNext;
  743. while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
  744. *lpszNext = 0; lpszNext++;
  745. // get Red
  746. #ifdef UNICODE
  747. wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA));
  748. bRed = (BYTE)latoi(szTempA);
  749. #else // !UNICODE
  750. bRed = (BYTE)latoi(lpszCur);
  751. #endif
  752. // set up G for translation
  753. lpszCur = lpszNext;
  754. while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
  755. *lpszNext = 0; lpszNext++;
  756. // get Green
  757. #ifdef UNICODE
  758. wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA));
  759. bGreen = (BYTE)latoi(szTempA);
  760. #else // !UNICODE
  761. bGreen = (BYTE)latoi(lpszCur);
  762. #endif
  763. // set up B for translation
  764. lpszCur = lpszNext;
  765. while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; }
  766. *lpszNext = 0; lpszNext++;
  767. // get Blue
  768. #ifdef UNICODE
  769. wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA));
  770. bBlue = (BYTE)latoi(szTempA);
  771. #else // !UNICODE
  772. bBlue = (BYTE)latoi(lpszCur);
  773. #endif
  774. // OK, now combine them all for the big finish.....!
  775. return(RGB(bRed, bGreen, bBlue));
  776. }
  777. // IsValidThemeFile
  778. //
  779. // Answers the question.
  780. BOOL IsValidThemeFile(IN LPCWSTR pszTest)
  781. {
  782. WCHAR szValue[MAX_PATH];
  783. BOOL fIsValid = FALSE;
  784. if (GetPrivateProfileString(SZ_INISECTION_MASTERSELECTOR, SZ_INIKEY_THEMEMAGICTAG, SZ_EMPTY, szValue, ARRAYSIZE(szValue), pszTest))
  785. {
  786. fIsValid = !StrCmp(szValue, SZ_INIKEY_THEMEMAGICVALUE);
  787. }
  788. return fIsValid;
  789. }
  790. HRESULT SnapCreateTemplate(LPCWSTR pszPath, ITheme ** ppTheme)
  791. {
  792. HRESULT hr = E_INVALIDARG;
  793. if (ppTheme)
  794. {
  795. *ppTheme = NULL;
  796. if (pszPath)
  797. {
  798. // Start with a template ("Windows Classic.theme").
  799. TCHAR szTemplate[MAX_PATH];
  800. DeleteFile(pszPath);
  801. StrCpyN(szTemplate, pszPath, ARRAYSIZE(szTemplate));
  802. PathRemoveFileSpec(szTemplate);
  803. SHCreateDirectoryEx(NULL, szTemplate, NULL);
  804. hr = SHGetResourcePath(TRUE, szTemplate, ARRAYSIZE(szTemplate));
  805. if (SUCCEEDED(hr))
  806. {
  807. PathAppend(szTemplate, TEXT("Themes\\Windows Classic.theme"));
  808. if (CopyFile(szTemplate, pszPath, FALSE))
  809. {
  810. hr = CThemeFile_CreateInstance(pszPath, ppTheme);
  811. }
  812. else
  813. {
  814. hr = HRESULT_FROM_WIN32(GetLastError());
  815. }
  816. }
  817. }
  818. }
  819. return hr;
  820. }
  821. /*****************************************************************************\
  822. DESCRIPTION:
  823. This function will grab the live settings (from pPropertyBag) and put
  824. theme into a file specified by pszPath. A pointer to the Theme will be
  825. returned in ppTheme. If the settings cannot be obtained, they will come
  826. from "Windows Classic.theme". This includes the Display Name, so the call
  827. will almost always want to specify that if this function returns successfully.
  828. PARAMETERS:
  829. pPropertyBag: This is were the settings will be read from.
  830. pszPath: This is the file will be saved to. It will be replaced if it exists.
  831. ppTheme: This will be created and returned if successful.
  832. \*****************************************************************************/
  833. HRESULT SnapShotLiveSettingsToTheme(IPropertyBag * pPropertyBag, LPCWSTR pszPath, ITheme ** ppTheme)
  834. {
  835. HRESULT hr = SnapCreateTemplate(pszPath, ppTheme);
  836. if (SUCCEEDED(hr))
  837. {
  838. TCHAR szPath[MAX_PATH];
  839. ITheme * pTheme = *ppTheme;
  840. // 1. Save the Background
  841. // We may fail to get the background path if the policy turns it off.
  842. if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_BACKGROUNDSRC_PATH, szPath, ARRAYSIZE(szPath))))
  843. {
  844. CComBSTR bstrPath(szPath);
  845. hr = pTheme->put_Background(bstrPath);
  846. if (SUCCEEDED(hr))
  847. {
  848. DWORD dwBackgroundTile;
  849. if (SUCCEEDED(SHPropertyBag_ReadDWORD(pPropertyBag, SZ_PBPROP_BACKGROUND_TILE, &dwBackgroundTile)))
  850. {
  851. enumBkgdTile nTile = BKDGT_STRECH;
  852. switch (dwBackgroundTile)
  853. {
  854. case WPSTYLE_CENTER:
  855. nTile = BKDGT_CENTER;
  856. break;
  857. case WPSTYLE_TILE:
  858. nTile = BKDGT_TILE;
  859. break;
  860. };
  861. hr = pTheme->put_BackgroundTile(nTile);
  862. }
  863. }
  864. }
  865. // 2. Save Screen Saver
  866. if (SUCCEEDED(hr))
  867. {
  868. // This will fail with policies enabled. In that case, we just use the default value.
  869. if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_SCREENSAVER_PATH, szPath, ARRAYSIZE(szPath))))
  870. {
  871. CComBSTR bstrPath(szPath);
  872. hr = pTheme->put_ScreenSaver(bstrPath);
  873. }
  874. }
  875. // 3. Save Visual Style
  876. if (SUCCEEDED(hr))
  877. {
  878. // It's okay to have no visual style selected.
  879. if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_VISUALSTYLE_PATH, szPath, ARRAYSIZE(szPath))) && szPath[0])
  880. {
  881. CComBSTR bstrPath(szPath);
  882. hr = pTheme->put_VisualStyle(bstrPath);
  883. if (SUCCEEDED(hr) &&
  884. SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_VISUALSTYLE_COLOR, szPath, ARRAYSIZE(szPath))))
  885. {
  886. bstrPath = szPath;
  887. hr = pTheme->put_VisualStyleColor(bstrPath);
  888. if (SUCCEEDED(hr) &&
  889. SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, SZ_PBPROP_VISUALSTYLE_SIZE, szPath, ARRAYSIZE(szPath))))
  890. {
  891. bstrPath = szPath;
  892. hr = pTheme->put_VisualStyleSize(bstrPath);
  893. }
  894. }
  895. }
  896. }
  897. if (SUCCEEDED(hr))
  898. {
  899. // 4. Save System Metrics
  900. IPropertyBag * pPropertyBagFile;
  901. hr = pTheme->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBagFile));
  902. if (SUCCEEDED(hr))
  903. {
  904. VARIANT var = {0};
  905. // This call will return SYSTEMMETRICS relative to the currently live DPI.
  906. hr = pPropertyBag->Read(SZ_PBPROP_SYSTEM_METRICS, &var, NULL);
  907. if (SUCCEEDED(hr) && (VT_BYREF == var.vt) && var.byref)
  908. {
  909. SYSTEMMETRICSALL * pCurrent = (SYSTEMMETRICSALL *) var.byref;
  910. IUnknown_SetSite(pPropertyBagFile, pPropertyBag); // Set the site so they can get settings.
  911. hr = SHPropertyBag_WriteByRef(pPropertyBagFile, SZ_PBPROP_SYSTEM_METRICS, (void *)pCurrent);
  912. IUnknown_SetSite(pPropertyBagFile, NULL); // Break any back pointers.
  913. }
  914. pPropertyBagFile->Release();
  915. }
  916. }
  917. // 5. Save Sounds
  918. int nIndex;
  919. for (nIndex = 0; nIndex < ARRAYSIZE(s_ThemeSoundsValues); nIndex++)
  920. {
  921. if (FAILED(HrRegGetPath(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL, szPath, ARRAYSIZE(szPath))))
  922. {
  923. szPath[0] = 0;
  924. }
  925. pTheme->SetSound((BSTR)s_ThemeSoundsValues[nIndex].pszRegKey, szPath);
  926. }
  927. // 6. Save Icons
  928. for (nIndex = 0; (nIndex < ARRAYSIZE(s_Icons)); nIndex++)
  929. {
  930. // This can fail if the background policy is enabled.
  931. if (SUCCEEDED(SHPropertyBag_ReadStr(pPropertyBag, s_Icons[nIndex], szPath, ARRAYSIZE(szPath))))
  932. {
  933. pTheme->SetIcon((BSTR)s_Icons[nIndex], szPath);
  934. }
  935. }
  936. // 7. Save Cursors
  937. for (nIndex = 0; nIndex < ARRAYSIZE(s_pszCursorArray); nIndex++)
  938. {
  939. if (FAILED(HrRegGetPath(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex], szPath, ARRAYSIZE(szPath))))
  940. {
  941. szPath[0] = 0;
  942. }
  943. pTheme->SetCursor((BSTR)s_pszCursorArray[nIndex], szPath);
  944. }
  945. if (SUCCEEDED(HrRegGetPath(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, NULL, szPath, ARRAYSIZE(szPath))))
  946. {
  947. pTheme->SetCursor(SZ_INIKEY_CURSORSCHEME, szPath);
  948. }
  949. if (FAILED(hr))
  950. {
  951. // Partially written files are very bad.
  952. DeleteFile(pszPath);
  953. ATOMICRELEASE(*ppTheme);
  954. }
  955. }
  956. return hr;
  957. }