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.

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