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.

2433 lines
88 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: Services.cpp
  3. //
  4. // Copyright (c) 2000, Microsoft Corporation
  5. //
  6. // APIs to communicate with the theme service running in the winlogon
  7. // process context.
  8. //
  9. // History: 2000-08-10 vtan created
  10. // 2000-10-11 vtan rewrite for LPC
  11. // --------------------------------------------------------------------------
  12. #include "stdafx.h"
  13. #include "Services.h"
  14. #include <uxthemep.h>
  15. #include "errors.h"
  16. #include "info.h"
  17. #include "MessageBroadcast.h"
  18. #include "stringtable.h"
  19. #include "themefile.h"
  20. #include "ThemeSection.h"
  21. #include "ThemeServer.h"
  22. #include "tmreg.h"
  23. #include "tmutils.h"
  24. #include <regstr.h> // REGSTR_PATH_POLICIES
  25. #define TBOOL(x) ((BOOL)(x))
  26. #define TW32(x) ((DWORD)(x))
  27. #define THR(x) ((HRESULT)(x))
  28. #define TSTATUS(x) ((NTSTATUS)(x))
  29. #undef ASSERTMSG
  30. #define ASSERTMSG(x, y)
  31. #define goto !!DO NOT USE GOTO!! - DO NOT REMOVE THIS ON PAIN OF DEATH
  32. // --------------------------------------------------------------------------
  33. // CThemeServices::s_hAPIPort
  34. //
  35. // Purpose: Static member variables for CThemeServices.
  36. //
  37. // NOTE: The critical section provides a lock for s_hAPIPort.
  38. // It's not acquired consistently because most of the API calls
  39. // would block trying to acquire the lock while another API call
  40. // is holding the lock across a request. The handle could be
  41. // copied to a local variable but this would defeat the purpose
  42. // of the lock. So the lock isn't used. It's possible for the
  43. // handle to become invalid. If so the request will just fail.
  44. //
  45. // History: 2000-11-09 vtan created
  46. // --------------------------------------------------------------------------
  47. CRITICAL_SECTION CThemeServices::s_lock = {0};
  48. HANDLE CThemeServices::s_hAPIPort = INVALID_HANDLE_VALUE;
  49. // --------------------------------------------------------------------------
  50. // CThemeServices::StaticInitialize
  51. //
  52. // Arguments: <none>
  53. //
  54. // Returns: <none>
  55. //
  56. // Purpose: Initialize static member variables.
  57. //
  58. // History: 2000-10-11 vtan created
  59. // 2000-11-09 vtan make static
  60. // --------------------------------------------------------------------------
  61. void CThemeServices::StaticInitialize (void)
  62. {
  63. if( !InitializeCriticalSectionAndSpinCount(&s_lock, 0) )
  64. {
  65. ASSERT(0 == s_lock.DebugInfo);
  66. }
  67. }
  68. // --------------------------------------------------------------------------
  69. // CThemeServices::~CThemeServices
  70. //
  71. // Arguments: <none>
  72. //
  73. // Returns: <none>
  74. //
  75. // Purpose: Release static resources used by CThemeServices.
  76. //
  77. // History: 2000-10-11 vtan created
  78. // 2000-11-09 vtan make static
  79. // --------------------------------------------------------------------------
  80. void CThemeServices::StaticTerminate (void)
  81. {
  82. ReleaseConnection();
  83. SAFE_DELETECRITICALSECTION(&s_lock);
  84. }
  85. // --------------------------------------------------------------------------
  86. // CThemeServices::ThemeHooksOn
  87. //
  88. // Arguments: <none>
  89. //
  90. // Returns: HRESULT
  91. //
  92. // Purpose: Ask the server what the hook DLL HMODULE and
  93. // pfnInitUserApiHook is and call user32!RegisterUserApiHook on
  94. // the client side. This is done because it's specific to the
  95. // session on which the client runs.
  96. //
  97. // History: 2000-11-09 vtan created
  98. // --------------------------------------------------------------------------
  99. HRESULT CThemeServices::ThemeHooksOn (HWND hwndTarget)
  100. {
  101. HRESULT hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  102. if (ConnectedToService())
  103. {
  104. NTSTATUS status;
  105. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  106. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  107. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  108. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_THEMEHOOKSON;
  109. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  110. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  111. status = NtRequestWaitReplyPort(s_hAPIPort,
  112. &portMessageIn.portMessage,
  113. &portMessageOut.portMessage);
  114. CheckForDisconnectedPort(status);
  115. if (NT_SUCCESS(status))
  116. {
  117. status = portMessageOut.apiThemes.apiGeneric.status;
  118. if (NT_SUCCESS(status))
  119. {
  120. hr = portMessageOut.apiThemes.apiSpecific.apiThemeHooksOn.out.hr;
  121. }
  122. }
  123. if (!NT_SUCCESS(status))
  124. {
  125. hr = HRESULT_FROM_NT(status);
  126. }
  127. //---- send the WM_UAHINIT msg to engage hooking now ----
  128. if (SUCCEEDED(hr))
  129. {
  130. if (hwndTarget)
  131. {
  132. (LRESULT)SendMessage(hwndTarget, WM_UAHINIT, 0, 0);
  133. }
  134. else
  135. {
  136. CMessageBroadcast messageBroadcast;
  137. messageBroadcast.PostAllThreadsMsg(WM_UAHINIT, 0, 0);
  138. //Log(LOG_TMCHANGEMSG, L"Just sent WM_UAHINIT, hwndTarget=0x%x", hwndTarget);
  139. }
  140. }
  141. Log(LOG_TMCHANGE, L"ThemeHooksOn called, hr=0x%x", hr);
  142. }
  143. return hr;
  144. }
  145. // --------------------------------------------------------------------------
  146. // CThemeServices::ThemeHooksOff
  147. //
  148. // Arguments: <none>
  149. //
  150. // Returns: HRESULT
  151. //
  152. // Purpose: Tell the server that this session is unregistering hooks.
  153. // Call user32!UnregisterUserApiHook either way.
  154. //
  155. // History: 2000-11-09 vtan created
  156. // --------------------------------------------------------------------------
  157. HRESULT CThemeServices::ThemeHooksOff (void)
  158. {
  159. HRESULT hr;
  160. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  161. if (ConnectedToService())
  162. {
  163. NTSTATUS status;
  164. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  165. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  166. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  167. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_THEMEHOOKSOFF;
  168. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  169. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  170. status = NtRequestWaitReplyPort(s_hAPIPort,
  171. &portMessageIn.portMessage,
  172. &portMessageOut.portMessage);
  173. CheckForDisconnectedPort(status);
  174. if (NT_SUCCESS(status))
  175. {
  176. status = portMessageOut.apiThemes.apiGeneric.status;
  177. if (NT_SUCCESS(status))
  178. {
  179. hr = portMessageOut.apiThemes.apiSpecific.apiThemeHooksOff.out.hr;
  180. }
  181. if (SUCCEEDED(hr))
  182. {
  183. //---- real unhooking happens on next window message in each process ----
  184. //---- so post a dummy msg to everyone to make it happen asap ----
  185. PostMessage(HWND_BROADCAST, WM_THEMECHANGED, WPARAM(-1), 0);
  186. Log(LOG_TMLOAD, L"Message to kick all window threads in session posted");
  187. }
  188. }
  189. if (!NT_SUCCESS(status))
  190. {
  191. hr = HRESULT_FROM_NT(status);
  192. }
  193. }
  194. return(hr);
  195. }
  196. // --------------------------------------------------------------------------
  197. // CThemeServices::GetStatusFlags
  198. //
  199. // Arguments: pdwFlags = Status flags returned from the theme services.
  200. //
  201. // Returns: HRESULT
  202. //
  203. // Purpose: Gets status flags from the theme services.
  204. //
  205. // History: 2000-08-10 vtan created
  206. // 2000-10-11 vtan rewrite for LPC
  207. // --------------------------------------------------------------------------
  208. HRESULT CThemeServices::GetStatusFlags (DWORD *pdwFlags)
  209. {
  210. HRESULT hr;
  211. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  212. if (ConnectedToService())
  213. {
  214. NTSTATUS status;
  215. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  216. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  217. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  218. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_GETSTATUSFLAGS;
  219. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  220. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  221. status = NtRequestWaitReplyPort(s_hAPIPort,
  222. &portMessageIn.portMessage,
  223. &portMessageOut.portMessage);
  224. CheckForDisconnectedPort(status);
  225. if (NT_SUCCESS(status))
  226. {
  227. status = portMessageOut.apiThemes.apiGeneric.status;
  228. if (NT_SUCCESS(status))
  229. {
  230. *pdwFlags = portMessageOut.apiThemes.apiSpecific.apiGetStatusFlags.out.dwFlags;
  231. hr = S_OK;
  232. }
  233. }
  234. if (!NT_SUCCESS(status))
  235. {
  236. hr = HRESULT_FROM_NT(status);
  237. }
  238. }
  239. return(hr);
  240. }
  241. // --------------------------------------------------------------------------
  242. // CThemeServices::GetCurrentChangeNumber
  243. //
  244. // Arguments: piValue = Current change number returned to caller.
  245. //
  246. // Returns: HRESULT
  247. //
  248. // Purpose: Gets the current change number of the theme services.
  249. //
  250. // History: 2000-08-10 vtan created
  251. // 2000-10-11 vtan rewrite for LPC
  252. // --------------------------------------------------------------------------
  253. HRESULT CThemeServices::GetCurrentChangeNumber (int *piValue)
  254. {
  255. HRESULT hr;
  256. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  257. if (ConnectedToService())
  258. {
  259. NTSTATUS status;
  260. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  261. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  262. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  263. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_GETCURRENTCHANGENUMBER;
  264. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  265. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  266. status = NtRequestWaitReplyPort(s_hAPIPort,
  267. &portMessageIn.portMessage,
  268. &portMessageOut.portMessage);
  269. CheckForDisconnectedPort(status);
  270. if (NT_SUCCESS(status))
  271. {
  272. status = portMessageOut.apiThemes.apiGeneric.status;
  273. if (NT_SUCCESS(status))
  274. {
  275. *piValue = portMessageOut.apiThemes.apiSpecific.apiGetCurrentChangeNumber.out.iChangeNumber;
  276. hr = S_OK;
  277. }
  278. Log(LOG_TMLOAD, L"*** GetCurrentChangeNumber: num=%d, hr=0x%x", *piValue, hr);
  279. }
  280. if (!NT_SUCCESS(status))
  281. {
  282. hr = HRESULT_FROM_NT(status);
  283. }
  284. }
  285. return(hr);
  286. }
  287. // --------------------------------------------------------------------------
  288. // CThemeServices::SetGlobalTheme
  289. //
  290. // Arguments: hSection = Section to set as the global theme.
  291. //
  292. // Returns: HRESULT
  293. //
  294. // Purpose: Sets the current global theme section handle.
  295. //
  296. // History: 2000-08-10 vtan created
  297. // 2000-10-11 vtan rewrite for LPC
  298. // --------------------------------------------------------------------------
  299. HRESULT CThemeServices::SetGlobalTheme (HANDLE hSection)
  300. {
  301. HRESULT hr;
  302. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  303. if (ConnectedToService())
  304. {
  305. NTSTATUS status;
  306. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  307. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  308. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  309. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_SETGLOBALTHEME;
  310. portMessageIn.apiThemes.apiSpecific.apiSetGlobalTheme.in.hSection = hSection;
  311. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  312. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  313. status = NtRequestWaitReplyPort(s_hAPIPort,
  314. &portMessageIn.portMessage,
  315. &portMessageOut.portMessage);
  316. CheckForDisconnectedPort(status);
  317. if (NT_SUCCESS(status))
  318. {
  319. status = portMessageOut.apiThemes.apiGeneric.status;
  320. if (NT_SUCCESS(status))
  321. {
  322. hr = portMessageOut.apiThemes.apiSpecific.apiSetGlobalTheme.out.hr;
  323. }
  324. }
  325. if (!NT_SUCCESS(status))
  326. {
  327. hr = HRESULT_FROM_NT(status);
  328. }
  329. }
  330. return(hr);
  331. }
  332. // --------------------------------------------------------------------------
  333. // CThemeServices::GetGlobalTheme
  334. //
  335. // Arguments: phSection = Section object returned from theme services.
  336. //
  337. // Returns: HRESULT
  338. //
  339. // Purpose: Gets the current global theme section handle.
  340. //
  341. // History: 2000-08-10 vtan created
  342. // 2000-10-11 vtan rewrite for LPC
  343. // --------------------------------------------------------------------------
  344. HRESULT CThemeServices::GetGlobalTheme (HANDLE *phSection)
  345. {
  346. HRESULT hr;
  347. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  348. if (ConnectedToService())
  349. {
  350. NTSTATUS status;
  351. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  352. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  353. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  354. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_GETGLOBALTHEME;
  355. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  356. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  357. status = NtRequestWaitReplyPort(s_hAPIPort,
  358. &portMessageIn.portMessage,
  359. &portMessageOut.portMessage);
  360. CheckForDisconnectedPort(status);
  361. if (NT_SUCCESS(status))
  362. {
  363. status = portMessageOut.apiThemes.apiGeneric.status;
  364. if (NT_SUCCESS(status))
  365. {
  366. hr = portMessageOut.apiThemes.apiSpecific.apiGetGlobalTheme.out.hr;
  367. if (SUCCEEDED(hr))
  368. {
  369. *phSection = portMessageOut.apiThemes.apiSpecific.apiGetGlobalTheme.out.hSection;
  370. }
  371. }
  372. }
  373. if (!NT_SUCCESS(status))
  374. {
  375. hr = HRESULT_FROM_NT(status);
  376. }
  377. }
  378. return(hr);
  379. }
  380. // --------------------------------------------------------------------------
  381. // CThemeServices::CheckThemeSignature
  382. //
  383. // Arguments: pszThemeName = File path of theme to check.
  384. //
  385. // Returns: HRESULT
  386. //
  387. // Purpose: Checks the given theme's signature.
  388. //
  389. // History: 2000-08-10 vtan created
  390. // 2000-10-11 vtan rewrite for LPC
  391. // --------------------------------------------------------------------------
  392. HRESULT CThemeServices::CheckThemeSignature (const WCHAR *pszThemeName)
  393. {
  394. HRESULT hr;
  395. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  396. if (ConnectedToService())
  397. {
  398. NTSTATUS status;
  399. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  400. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  401. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  402. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_CHECKTHEMESIGNATURE;
  403. portMessageIn.apiThemes.apiSpecific.apiCheckThemeSignature.in.pszName = pszThemeName;
  404. portMessageIn.apiThemes.apiSpecific.apiCheckThemeSignature.in.cchName = lstrlen(pszThemeName) + sizeof('\0');
  405. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  406. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  407. status = NtRequestWaitReplyPort(s_hAPIPort,
  408. &portMessageIn.portMessage,
  409. &portMessageOut.portMessage);
  410. CheckForDisconnectedPort(status);
  411. if (NT_SUCCESS(status))
  412. {
  413. status = portMessageOut.apiThemes.apiGeneric.status;
  414. if (NT_SUCCESS(status))
  415. {
  416. hr = portMessageOut.apiThemes.apiSpecific.apiCheckThemeSignature.out.hr;
  417. }
  418. }
  419. if (!NT_SUCCESS(status))
  420. {
  421. hr = HRESULT_FROM_NT(status);
  422. }
  423. }
  424. return(hr);
  425. }
  426. // --------------------------------------------------------------------------
  427. // CThemeServices::LoadTheme
  428. //
  429. // Arguments: phSection = Section object to theme returned.
  430. // pszThemeName = Theme file to load.
  431. // pszColorParam = Color.
  432. // pszSizeParam = Size.
  433. // fGlobal = FALSE for a preview.
  434. //
  435. // Returns: HRESULT
  436. //
  437. // Purpose: Loads the given theme and creates a section object for it.
  438. //
  439. // History: 2000-08-10 vtan created
  440. // 2000-10-11 vtan rewrite for LPC
  441. // --------------------------------------------------------------------------
  442. HRESULT CThemeServices::LoadTheme (HANDLE *phSection,
  443. const WCHAR *pszThemeName, const WCHAR *pszColor, const WCHAR *pszSize, BOOL fGlobal)
  444. {
  445. HRESULT hr;
  446. *phSection = NULL; // Result if failure
  447. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  448. if (ConnectedToService())
  449. {
  450. HANDLE hSection;
  451. CThemeLoader *pLoader;
  452. WCHAR szColor[MAX_PATH];
  453. WCHAR szSize[MAX_PATH];
  454. // Because the loader makes GDI calls that directly affect the
  455. // client instance of win32k the theme must be loaded on the
  456. // client side. Once the theme is loaded it is handed to the
  457. // server (which creates a new section) and copies the data to
  458. // it. The server then controls the theme data and the client
  459. // discards the temporary theme.
  460. hSection = NULL;
  461. pLoader = new CThemeLoader;
  462. if (pLoader != NULL)
  463. {
  464. HINSTANCE hInst = NULL;
  465. // Keep the DLL loaded to avoid loading it 3 times below
  466. hr = LoadThemeLibrary(pszThemeName, &hInst);
  467. if (SUCCEEDED(hr) && (pszColor == NULL || *pszColor == L'\0'))
  468. {
  469. hr = GetThemeDefaults(pszThemeName, szColor, ARRAYSIZE(szColor), NULL, 0);
  470. pszColor = szColor;
  471. }
  472. if (SUCCEEDED(hr) && (pszSize == NULL || *pszSize == L'\0'))
  473. {
  474. hr = GetThemeDefaults(pszThemeName, NULL, 0, szSize, ARRAYSIZE(szSize));
  475. pszSize = szSize;
  476. }
  477. if (SUCCEEDED(hr))
  478. {
  479. hr = pLoader->LoadTheme(pszThemeName, pszColor, pszSize, &hSection, fGlobal);
  480. }
  481. delete pLoader;
  482. if (hInst)
  483. {
  484. FreeLibrary(hInst);
  485. }
  486. }
  487. else
  488. {
  489. hr = MakeError32(E_OUTOFMEMORY);
  490. }
  491. if (SUCCEEDED(hr) && (hSection != NULL))
  492. {
  493. NTSTATUS status;
  494. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  495. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  496. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  497. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_LOADTHEME;
  498. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.pszName = pszThemeName;
  499. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.cchName = lstrlen(pszThemeName) + sizeof('\0');
  500. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.pszColor = pszColor;
  501. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.cchColor = lstrlen(pszColor) + sizeof('\0');
  502. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.pszSize = pszSize;
  503. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.cchSize = lstrlen(pszSize) + sizeof('\0');
  504. portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.hSection = hSection;
  505. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  506. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  507. status = NtRequestWaitReplyPort(s_hAPIPort,
  508. &portMessageIn.portMessage,
  509. &portMessageOut.portMessage);
  510. CheckForDisconnectedPort(status);
  511. if (NT_SUCCESS(status))
  512. {
  513. status = portMessageOut.apiThemes.apiGeneric.status;
  514. if (NT_SUCCESS(status))
  515. {
  516. hr = portMessageOut.apiThemes.apiSpecific.apiLoadTheme.out.hr;
  517. if (SUCCEEDED(hr))
  518. {
  519. *phSection = portMessageOut.apiThemes.apiSpecific.apiLoadTheme.out.hSection;
  520. }
  521. else
  522. {
  523. }
  524. }
  525. }
  526. if (!NT_SUCCESS(status))
  527. {
  528. hr = HRESULT_FROM_NT(status);
  529. }
  530. }
  531. // Clear our temporary section
  532. if (hSection != NULL)
  533. {
  534. // If we didn't transfer the stock objects handles to a new section, clear them always
  535. if (*phSection == NULL)
  536. {
  537. THR(ClearStockObjects(hSection));
  538. }
  539. TBOOL(CloseHandle(hSection));
  540. }
  541. }
  542. return(hr);
  543. }
  544. // --------------------------------------------------------------------------
  545. HRESULT CThemeServices::ProcessLoadGlobalTheme(
  546. const WCHAR *pszThemeName,
  547. const WCHAR *pszColor,
  548. const WCHAR *pszSize,
  549. OUT HANDLE *phSection )
  550. {
  551. HRESULT hr;
  552. *phSection = 0;
  553. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  554. if (ConnectedToService())
  555. {
  556. WCHAR szColor[MAX_PATH];
  557. WCHAR szSize[MAX_PATH];
  558. HINSTANCE hInst = NULL;
  559. hr = S_OK;
  560. // In this version of theme load, we're going to request the server initiate
  561. // the load from disk. Because the loader makes GDI calls that directly affect the
  562. // client instance of win32k the theme must be loaded on the client's window station.
  563. // To accomplish this, the theme server may decide to launch a new process or inject
  564. // a thread into some existing process on this client's window station. The client
  565. // doesn't know or care which method is used.
  566. // Addl note: On case of failure, any GDI stock objects created will be cleaned up
  567. // elsewhere.
  568. // fetch default color variant name if needed
  569. if (SUCCEEDED(hr) && !(pszColor && *pszColor))
  570. {
  571. // Map in the .msstyles dll to avoid multiple loads.
  572. hr = LoadThemeLibrary(pszThemeName, &hInst);
  573. if( SUCCEEDED(hr) )
  574. {
  575. hr = GetThemeDefaults(pszThemeName, szColor, ARRAYSIZE(szColor), NULL, 0);
  576. pszColor = szColor;
  577. }
  578. }
  579. // fetch default size variant name if needed
  580. if (SUCCEEDED(hr) && !(pszSize && *pszSize))
  581. {
  582. hr = GetThemeDefaults(pszThemeName, NULL, 0, szSize, ARRAYSIZE(szSize));
  583. pszSize = szSize;
  584. }
  585. // drop our msstyles reference, if any
  586. if (hInst)
  587. {
  588. FreeLibrary(hInst);
  589. }
  590. // ready to do the LPC request
  591. if (SUCCEEDED(hr))
  592. {
  593. NTSTATUS status;
  594. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  595. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  596. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  597. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_PROCESSLOADTHEME;
  598. portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.pszName = pszThemeName;
  599. portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.cchName = lstrlen(pszThemeName) + sizeof('\0');
  600. portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.pszColor = pszColor;
  601. portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.cchColor = lstrlen(pszColor) + sizeof('\0');
  602. portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.pszSize = pszSize;
  603. portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.cchSize = lstrlen(pszSize) + sizeof('\0');
  604. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  605. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  606. status = NtRequestWaitReplyPort(s_hAPIPort,
  607. &portMessageIn.portMessage,
  608. &portMessageOut.portMessage);
  609. CheckForDisconnectedPort(status);
  610. if (NT_SUCCESS(status))
  611. {
  612. status = portMessageOut.apiThemes.apiGeneric.status;
  613. if (NT_SUCCESS(status))
  614. {
  615. hr = portMessageOut.apiThemes.apiSpecific.apiProcessLoadTheme.out.hr;
  616. if (SUCCEEDED(hr))
  617. {
  618. *phSection = portMessageOut.apiThemes.apiSpecific.apiProcessLoadTheme.out.hSection;
  619. }
  620. }
  621. else
  622. {
  623. hr = HRESULT_FROM_NT(status);
  624. }
  625. }
  626. else
  627. {
  628. hr = HRESULT_FROM_NT(status);
  629. }
  630. }
  631. }
  632. return(hr);
  633. }
  634. // --------------------------------------------------------------------------
  635. HRESULT CThemeServices::CheckColorDepth(CUxThemeFile *pThemeFile)
  636. {
  637. HRESULT hr = S_OK;
  638. THEMEMETRICS *pMetrics = GetThemeMetricsPtr(pThemeFile);
  639. DWORD dwDepthRequired = pMetrics->iInts[TMT_MINCOLORDEPTH - TMT_FIRSTINT];
  640. if (MinimumDisplayColorDepth() < dwDepthRequired)
  641. {
  642. hr = MakeError32(ERROR_BAD_ENVIRONMENT);
  643. }
  644. return hr;
  645. }
  646. // --------------------------------------------------------------------------
  647. HRESULT CThemeServices::UpdateThemeRegistry(BOOL fThemeActive,
  648. LPCWSTR pszThemeFileName, LPCWSTR pszColorParam, LPCWSTR pszSizeParam, BOOL fJustSetActive,
  649. BOOL fJustApplied)
  650. {
  651. if (fThemeActive)
  652. {
  653. if (fJustSetActive)
  654. {
  655. //---- see if a theme was previously active ----
  656. WCHAR szThemeName[MAX_PATH];
  657. THR(GetCurrentUserThemeString(THEMEPROP_DLLNAME, L"", szThemeName, ARRAYSIZE(szThemeName)));
  658. if (szThemeName[0] != L'\0')
  659. {
  660. THR(SetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, 1));
  661. }
  662. }
  663. else
  664. {
  665. WCHAR szFullName[MAX_PATH];
  666. if (GetFullPathName(pszThemeFileName, ARRAYSIZE(szFullName), szFullName, NULL) == 0)
  667. {
  668. SafeStringCchCopyW(szFullName, ARRAYSIZE(szFullName), pszThemeFileName);
  669. }
  670. THR(SetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, 1));
  671. if (fJustApplied)
  672. {
  673. THR(SetCurrentUserThemeInt(THEMEPROP_LOADEDBEFORE, 1));
  674. THR(SetCurrentUserThemeInt(THEMEPROP_LANGID, (int) GetUserDefaultUILanguage()));
  675. // Theme identification
  676. THR(SetCurrentUserThemeStringExpand(THEMEPROP_DLLNAME, szFullName));
  677. THR(SetCurrentUserThemeString(THEMEPROP_COLORNAME, pszColorParam));
  678. THR(SetCurrentUserThemeString(THEMEPROP_SIZENAME, pszSizeParam));
  679. }
  680. else // for forcing theme to be loaded from InitUserTheme()
  681. {
  682. WCHAR szThemeName[MAX_PATH];
  683. THR(GetCurrentUserThemeString(THEMEPROP_DLLNAME, L"", szThemeName, ARRAYSIZE(szThemeName)));
  684. if (lstrcmpiW(szThemeName, szFullName) != 0)
  685. {
  686. THR(SetCurrentUserThemeString(THEMEPROP_DLLNAME, szFullName));
  687. TW32(DeleteCurrentUserThemeValue(THEMEPROP_LOADEDBEFORE));
  688. TW32(DeleteCurrentUserThemeValue(THEMEPROP_LANGID));
  689. TW32(DeleteCurrentUserThemeValue(THEMEPROP_COLORNAME));
  690. TW32(DeleteCurrentUserThemeValue(THEMEPROP_SIZENAME));
  691. } else
  692. {
  693. return S_FALSE; // S_FALSE means we did nothing really
  694. }
  695. }
  696. }
  697. }
  698. else
  699. {
  700. THR(SetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, 0));
  701. if (! fJustSetActive) // wipe out all theme info
  702. {
  703. THR(DeleteCurrentUserThemeValue(THEMEPROP_DLLNAME));
  704. THR(DeleteCurrentUserThemeValue(THEMEPROP_COLORNAME));
  705. THR(DeleteCurrentUserThemeValue(THEMEPROP_SIZENAME));
  706. THR(DeleteCurrentUserThemeValue(THEMEPROP_LOADEDBEFORE));
  707. THR(DeleteCurrentUserThemeValue(THEMEPROP_LANGID));
  708. }
  709. }
  710. return S_OK;
  711. }
  712. // --------------------------------------------------------------------------
  713. void CThemeServices::SendThemeChangedMsg(BOOL fNewTheme, HWND hwndTarget, DWORD dwFlags,
  714. int iLoadId)
  715. {
  716. WPARAM wParam;
  717. LPARAM lParamBits, lParamMixed;
  718. BOOL fExcluding = ((dwFlags & AT_EXCLUDE) != 0);
  719. BOOL fCustom = ((dwFlags & AT_PROCESS) != 0);
  720. //---- change number was set in ApplyTheme() for both global and preview cases ----
  721. int iChangeNum;
  722. if( SUCCEEDED(GetCurrentChangeNumber(&iChangeNum)) )
  723. {
  724. wParam = iChangeNum;
  725. lParamBits = 0;
  726. if (fNewTheme)
  727. {
  728. lParamBits |= WTC_THEMEACTIVE;
  729. }
  730. if (fCustom)
  731. {
  732. lParamBits |= WTC_CUSTOMTHEME;
  733. }
  734. if ((hwndTarget) && (! fExcluding))
  735. {
  736. SendMessage(hwndTarget, WM_THEMECHANGED, wParam, lParamBits);
  737. }
  738. else
  739. {
  740. lParamMixed = (iLoadId << 4) | (lParamBits & 0xf);
  741. CMessageBroadcast messageBroadcast;
  742. // POST the WM_THEMECHANGED_TRIGGER msg to all targeted windows
  743. messageBroadcast.PostAllThreadsMsg(WM_THEMECHANGED_TRIGGER, wParam, lParamMixed);
  744. Log(LOG_TMCHANGEMSG, L"Just Broadcasted WM_THEMECHANGED_TRIGGER: iLoadId=%d", iLoadId);
  745. }
  746. }
  747. }
  748. // --------------------------------------------------------------------------
  749. int CThemeServices::GetLoadId(HANDLE hSectionOld)
  750. {
  751. int iLoadId = 0;
  752. //---- extract LoadId from old section ----
  753. if (hSectionOld)
  754. {
  755. CThemeSection pThemeSectionFile;
  756. if (SUCCEEDED(pThemeSectionFile.Open(hSectionOld)))
  757. {
  758. CUxThemeFile *pThemeFile = pThemeSectionFile;
  759. if (pThemeFile)
  760. {
  761. THEMEHDR *hdr = (THEMEHDR *)(pThemeFile->_pbThemeData);
  762. if (hdr)
  763. {
  764. iLoadId = hdr->iLoadId;
  765. }
  766. }
  767. }
  768. }
  769. return iLoadId;
  770. }
  771. // --------------------------------------------------------------------------
  772. // CThemeServices::ApplyTheme
  773. //
  774. // Arguments: pThemeFile = Object wrapping the theme section to apply.
  775. // dwFlags = Flags.
  776. // hwndTarget = HWND.
  777. //
  778. // Returns: HRESULT
  779. //
  780. // Purpose: Applies the given theme. Do some metric and color depth
  781. // validation, clear the stock bitmaps of the old theme, set
  782. // the given theme as the current theme and broadcast this fact.
  783. //
  784. // History: 2000-08-10 vtan created
  785. // 2000-10-11 vtan rewrite for LPC
  786. // --------------------------------------------------------------------------
  787. // In the design notes below, note that SEND and POST differences are
  788. // significant.
  789. //
  790. // Also, when the "WM_THEMECHANGED_TRIGGER" msg is sent,
  791. // the uxtheme hooking code in each process will:
  792. //
  793. // 1. enumerate all windows for process (using desktop enumeration and
  794. // the per-process "foreign window list") to:
  795. //
  796. // a. process WM_THEMECHANGED for nonclient area
  797. // b. SEND a WM_THEMECHANGED msg to regular window
  798. //
  799. // 2. call FreeRenderObjects() for old theme, if any
  800. // --------------------------------------------------------------------------
  801. // To ensure correct window notification of theme changes and correct removal
  802. // of old theme file RefCounts, the following CRITICAL STEPS must be taken
  803. // in the 4 basic theme transition sequences:
  804. //
  805. // turning ON preview theme:
  806. // a. turn ON global UAE hooks
  807. // b. SEND WM_UAHINIT msg to hwndTarget
  808. // c. SEND WM_THEMECHANGED to hwndTarget
  809. //
  810. // turning ON global theme:
  811. // a. turn ON global UAE hooks
  812. // b. POST WM_UAHINIT msg to all accessible windows
  813. // c. POST WM_THEMECHANGED_TRIGGER to all accessible window threads
  814. //
  815. // turning OFF preview theme:
  816. // c. SEND WM_THEMECHANGED to hwndTarget
  817. //
  818. // turning OFF global theme:
  819. // a. turn OFF global UAE hooks
  820. // b. step "a" will cause WM_THEMECHANGED_TRIGGER-type processing
  821. // to occur from OnHooksDisabled() in each process
  822. // --------------------------------------------------------------------------
  823. HRESULT CThemeServices::ApplyTheme (CUxThemeFile *pThemeFile, DWORD dwFlags, HWND hwndTarget)
  824. {
  825. HRESULT hr;
  826. bool fNewTheme, fGlobal;
  827. int iLoadId;
  828. WCHAR szThemeFileName[MAX_PATH];
  829. WCHAR szColorParam[MAX_PATH];
  830. WCHAR szSizeParam[MAX_PATH];
  831. HANDLE hSection = NULL;
  832. if (pThemeFile != NULL)
  833. {
  834. hSection = pThemeFile->Handle();
  835. }
  836. fGlobal = (((dwFlags & AT_EXCLUDE) != 0) ||
  837. ((hwndTarget == NULL) && ((dwFlags & AT_PROCESS) == 0)));
  838. fNewTheme = (hSection != NULL);
  839. iLoadId = 0;
  840. Log(LOG_TMHANDLE, L"ApplyTheme: hSection=0x%x, dwFlags=0x%x, hwndTarget=0x%x",
  841. hSection, dwFlags, hwndTarget);
  842. if (fNewTheme)
  843. {
  844. if (pThemeFile->HasStockObjects() && !fGlobal) // Don't do this
  845. {
  846. hr = E_INVALIDARG;
  847. }
  848. else
  849. {
  850. //---- get some basic info used thruout this function ----
  851. hr = GetThemeNameId(pThemeFile,
  852. szThemeFileName, ARRAYSIZE(szThemeFileName),
  853. szColorParam, ARRAYSIZE(szColorParam),
  854. szSizeParam, ARRAYSIZE(szSizeParam),
  855. NULL, NULL);
  856. if (SUCCEEDED(hr))
  857. {
  858. //---- ensure color depth of monitor(s) are enough for theme ----
  859. if (GetSystemMetrics(SM_REMOTESESSION)) // only check for terminal server sessions
  860. {
  861. hr = CheckColorDepth(pThemeFile);
  862. }
  863. if (SUCCEEDED(hr))
  864. {
  865. //---- ensure hooks are on ----
  866. hr = ThemeHooksOn(hwndTarget);
  867. }
  868. }
  869. }
  870. }
  871. else
  872. {
  873. hr = S_OK;
  874. }
  875. if (SUCCEEDED(hr) && fGlobal)
  876. {
  877. HANDLE hSectionOld;
  878. //---- get a handle to the old global theme (for stock cleanup) ----
  879. hr = GetGlobalTheme(&hSectionOld);
  880. if (SUCCEEDED(hr))
  881. {
  882. //---- extract Load ID before theme becomes invalid (dwFlags & SECTION_READY=0) ----
  883. if (hSectionOld != NULL)
  884. {
  885. iLoadId = GetLoadId(hSectionOld);
  886. }
  887. //---- tell server to switch global themes ----
  888. hr = SetGlobalTheme(hSection);
  889. if (SUCCEEDED(hr))
  890. {
  891. //---- update needed registry settings ----
  892. if ((dwFlags & AT_NOREGUPDATE) == 0) // if caller allows update
  893. {
  894. hr = UpdateThemeRegistry(fNewTheme, szThemeFileName, szColorParam, szSizeParam,
  895. FALSE, TRUE);
  896. if (FAILED(hr))
  897. {
  898. Log(LOG_ALWAYS, L"UpdateThemeRegistry call failed, hr=0x%x", hr);
  899. hr = S_OK; // not a fatal error
  900. }
  901. }
  902. //---- set system metrics, if requested ----
  903. if ((dwFlags & AT_LOAD_SYSMETRICS) != 0)
  904. {
  905. BOOL fSync = ((dwFlags & AT_SYNC_LOADMETRICS) != 0);
  906. if (fNewTheme)
  907. {
  908. SetSystemMetrics(GetThemeMetricsPtr(pThemeFile), fSync);
  909. }
  910. else // just load classic metrics
  911. {
  912. LOADTHEMEMETRICS tm;
  913. hr = InitThemeMetrics(&tm);
  914. if (SUCCEEDED(hr))
  915. {
  916. SetSystemMetrics(&tm, fSync);
  917. }
  918. }
  919. }
  920. }
  921. if (hSectionOld != NULL)
  922. {
  923. TBOOL(CloseHandle(hSectionOld));
  924. }
  925. }
  926. }
  927. //---- if we turned off global theme, turn hooks off now ----
  928. if (SUCCEEDED(hr))
  929. {
  930. if (!fNewTheme && fGlobal)
  931. {
  932. hr = ThemeHooksOff();
  933. }
  934. else
  935. {
  936. //---- send the correct WM_THEMECHANGED_XXX msg to window(s) ----
  937. SendThemeChangedMsg(fNewTheme, hwndTarget, dwFlags, iLoadId);
  938. }
  939. }
  940. // If the service is down but we're trying to turn Themes off, clean up the registry
  941. else if (hr == MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT) && !fNewTheme && fGlobal && (dwFlags & AT_NOREGUPDATE) == 0)
  942. {
  943. // Ignore failure here
  944. UpdateThemeRegistry(fNewTheme, NULL, NULL, NULL, FALSE, TRUE);
  945. }
  946. return(hr);
  947. }
  948. // --------------------------------------------------------------------------
  949. // CThemeServices::AdjustTheme
  950. //
  951. // Arguments: BOOL fEnable - if TRUE, enable CU theme; if FALSE, disable it
  952. //
  953. // Returns: HRESULT
  954. //
  955. // Purpose: for 3rd party skinning apps to cooperate better with theme mgr
  956. //
  957. // History: 2001-03-12 rfernand created
  958. // --------------------------------------------------------------------------
  959. HRESULT CThemeServices::AdjustTheme(BOOL fEnable)
  960. {
  961. HRESULT hr = UpdateThemeRegistry(fEnable, NULL, NULL, NULL, TRUE, FALSE);
  962. if (SUCCEEDED(hr))
  963. {
  964. hr = InitUserTheme(FALSE);
  965. }
  966. return hr;
  967. }
  968. // --------------------------------------------------------------------------
  969. // CThemeServices::ApplyDefaultMetrics
  970. //
  971. // Arguments: <none>
  972. //
  973. // Returns: <none>
  974. //
  975. // Purpose: Make sure the user metrics gets reset to Windows Standard
  976. //
  977. // History: 2001-03-30 lmouton created
  978. // --------------------------------------------------------------------------
  979. void CThemeServices::ApplyDefaultMetrics(void)
  980. {
  981. HKEY hKeyThemes;
  982. CCurrentUser hKeyCurrentUser(KEY_READ | KEY_WRITE);
  983. Log(LOG_TMLOAD, L"Applying default metrics");
  984. if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyCurrentUser,
  985. THEMES_REGKEY L"\\" SZ_DEFAULTVS_OFF,
  986. 0,
  987. KEY_QUERY_VALUE,
  988. &hKeyThemes)))
  989. {
  990. WCHAR szVisualStyle[MAX_PATH] = {L'\0'};
  991. WCHAR szColor[MAX_PATH] = {L'\0'};
  992. WCHAR szSize[MAX_PATH] = {L'\0'};
  993. BOOL fGotOne;
  994. // Note: These will fail for the first user logon, themeui sets these keys and needs to call InstallVS itself
  995. fGotOne = SUCCEEDED(RegistryStrRead(hKeyThemes, SZ_INSTALLVISUALSTYLE, szVisualStyle, ARRAYSIZE(szVisualStyle)));
  996. fGotOne = SUCCEEDED(RegistryStrRead(hKeyThemes, SZ_INSTALLVISUALSTYLECOLOR, szColor, ARRAYSIZE(szColor)))
  997. || fGotOne;
  998. fGotOne = SUCCEEDED(RegistryStrRead(hKeyThemes, SZ_INSTALLVISUALSTYLESIZE, szSize, ARRAYSIZE(szSize)))
  999. || fGotOne;
  1000. if (fGotOne)
  1001. {
  1002. // At least one key is present in the registry, it may be enough
  1003. WCHAR szSysDir[MAX_PATH];
  1004. if (0 < GetSystemDirectory(szSysDir, ARRAYSIZE(szSysDir)))
  1005. {
  1006. WCHAR *pszCmdLine = new WCHAR[MAX_PATH * 5];
  1007. if (pszCmdLine)
  1008. {
  1009. StringCchPrintfW(pszCmdLine, MAX_PATH * 5, L"%s\\regsvr32.exe /s /n /i:\"" SZ_INSTALL_VS L"%s','%s','%s'\" %s\\themeui.dll", szSysDir, szVisualStyle, szColor, szSize, szSysDir);
  1010. // Set a reg key to have themeui install the proper settings instead of defaults
  1011. // We can't do this now because the user could not be completely logged on
  1012. HKEY hKeyRun;
  1013. if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyCurrentUser, REGSTR_PATH_RUNONCE, 0, KEY_SET_VALUE, &hKeyRun)))
  1014. {
  1015. THR(RegistryStrWrite(hKeyRun, szColor, pszCmdLine));
  1016. TW32(RegCloseKey(hKeyRun));
  1017. }
  1018. delete [] pszCmdLine;
  1019. }
  1020. }
  1021. }
  1022. TW32(RegCloseKey(hKeyThemes));
  1023. }
  1024. }
  1025. // --------------------------------------------------------------------------
  1026. // CThemeServices::InitUserTheme
  1027. //
  1028. // Arguments: BOOL fPolicyCheckOnly
  1029. // TRUE means
  1030. // "only do something if the policy is different from the current loaded theme"
  1031. //
  1032. // Returns: HRESULT
  1033. //
  1034. // Purpose: Special entry point for winlogon/msgina to control themes
  1035. // for user logon/logoff.
  1036. //
  1037. // History: 2000-11-11 vtan created
  1038. // --------------------------------------------------------------------------
  1039. HRESULT CThemeServices::InitUserTheme (BOOL fPolicyCheckOnly)
  1040. {
  1041. BOOL fActive = FALSE;
  1042. BOOL fOldActive = FALSE;
  1043. BOOL fPolicyActive = FALSE;
  1044. //---- should theme be active for this user? ----
  1045. if (! IsRemoteThemeDisabled())
  1046. {
  1047. THR(GetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, FALSE, &fActive));
  1048. fOldActive = fActive;
  1049. fPolicyActive = ThemeEnforcedByPolicy(fActive != FALSE);
  1050. if (fPolicyActive)
  1051. {
  1052. // Refresh fActive because the policy changed it
  1053. THR(GetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, FALSE, &fActive));
  1054. }
  1055. if ((fActive) && (ThemeSettingsModified()))
  1056. {
  1057. fActive = FALSE;
  1058. }
  1059. }
  1060. #ifdef DEBUG
  1061. if (LogOptionOn(LO_TMLOAD))
  1062. {
  1063. WCHAR szUserName[MAX_PATH];
  1064. DWORD dwSize = ARRAYSIZE(szUserName);
  1065. GetUserName(szUserName, &dwSize);
  1066. Log(LOG_TMLOAD, L"InitUserTheme: User=%s, ThemeActive=%d, SM_REMOTESESSION=%d, fPolicyActive=%d, fPolicyCheckOnly=%d",
  1067. szUserName, fActive, GetSystemMetrics(SM_REMOTESESSION), fPolicyActive, fPolicyCheckOnly);
  1068. }
  1069. #endif
  1070. BOOL fEarlyExit = FALSE;
  1071. if (fPolicyCheckOnly)
  1072. {
  1073. // Bail out early if nothing has changed since last time, which is most of the time
  1074. if (!fPolicyActive)
  1075. {
  1076. Log(LOG_TMLOAD, L"InitUserTheme: Nothing to do after Policy check");
  1077. fEarlyExit = TRUE;
  1078. } else
  1079. {
  1080. Log(LOG_TMLOAD, L"InitUserTheme: Reloading after Policy check");
  1081. }
  1082. }
  1083. if (!fEarlyExit)
  1084. {
  1085. if (fActive)
  1086. {
  1087. //---- load this user's theme ----
  1088. HRESULT hr = LoadCurrentTheme();
  1089. if (FAILED(hr))
  1090. {
  1091. fActive = FALSE;
  1092. }
  1093. }
  1094. if (! fActive) // turn off themes
  1095. {
  1096. // if fPolicyActive, force refresh system metrics from temporary defaults
  1097. THR(ApplyTheme(NULL, AT_NOREGUPDATE | (fPolicyActive ? AT_LOAD_SYSMETRICS | AT_SYNC_LOADMETRICS: 0), false));
  1098. // Apply the proper default metrics
  1099. if (fPolicyActive)
  1100. {
  1101. ApplyDefaultMetrics();
  1102. }
  1103. }
  1104. }
  1105. return S_OK; // never fail this guy
  1106. }
  1107. // --------------------------------------------------------------------------
  1108. // CThemeServices::InitUserRegistry
  1109. //
  1110. // Arguments: <none>
  1111. //
  1112. // Returns: HRESULT
  1113. //
  1114. // Purpose: Propogate settings from HKLM to HKCU. This should only be
  1115. // invoked for ".Default" hives. Assert to ensure this.
  1116. //
  1117. // History: 2000-11-11 vtan created (ported from themeldr.cpp)
  1118. // --------------------------------------------------------------------------
  1119. HRESULT CThemeServices::InitUserRegistry (void)
  1120. {
  1121. HRESULT hr;
  1122. DWORD dwErrorCode;
  1123. HKEY hklm;
  1124. CCurrentUser hkeyCurrentUser(KEY_READ | KEY_WRITE);
  1125. #ifdef DEBUG
  1126. ASSERT(CThemeServer::IsSystemProcessContext());
  1127. #endif /* DEBUG */
  1128. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1129. THEMEMGR_REGKEY,
  1130. 0,
  1131. KEY_QUERY_VALUE | KEY_SET_VALUE,
  1132. &hklm))
  1133. {
  1134. HKEY hkcu;
  1135. if (ERROR_SUCCESS == RegCreateKeyEx(hkeyCurrentUser,
  1136. THEMEMGR_REGKEY,
  1137. 0,
  1138. NULL,
  1139. 0,
  1140. KEY_QUERY_VALUE | KEY_SET_VALUE,
  1141. NULL,
  1142. &hkcu,
  1143. NULL))
  1144. {
  1145. int iLMVersion;
  1146. hr = RegistryIntRead(hklm, THEMEPROP_LMVERSION, &iLMVersion);
  1147. if (SUCCEEDED(hr))
  1148. {
  1149. int iCUVersion;
  1150. if (FAILED(RegistryIntRead(hkcu, THEMEPROP_LMVERSION, &iCUVersion)))
  1151. {
  1152. iCUVersion = 0;
  1153. }
  1154. if (iLMVersion != iCUVersion)
  1155. {
  1156. BOOL fOverride;
  1157. WCHAR szValueData[MAX_PATH];
  1158. hr = RegistryIntWrite(hkcu, THEMEPROP_LMVERSION, iLMVersion);
  1159. if (FAILED(hr) || FAILED(RegistryIntRead(hklm, THEMEPROP_LMOVERRIDE, &fOverride)))
  1160. {
  1161. fOverride = FALSE;
  1162. }
  1163. if ((fOverride != FALSE) ||
  1164. FAILED(RegistryStrRead(hkcu, THEMEPROP_DLLNAME, szValueData, ARRAYSIZE(szValueData))) ||
  1165. (lstrlenW(szValueData) == 0))
  1166. {
  1167. DWORD dwIndex;
  1168. dwIndex = 0;
  1169. do
  1170. {
  1171. DWORD dwType, dwValueNameSize, dwValueDataSize;
  1172. WCHAR szValueName[MAX_PATH];
  1173. dwValueNameSize = ARRAYSIZE(szValueName);
  1174. dwValueDataSize = sizeof(szValueData);
  1175. dwErrorCode = RegEnumValue(hklm,
  1176. dwIndex++,
  1177. szValueName,
  1178. &dwValueNameSize,
  1179. NULL,
  1180. &dwType,
  1181. reinterpret_cast<LPBYTE>(szValueData),
  1182. &dwValueDataSize);
  1183. if ((ERROR_SUCCESS == dwErrorCode) &&
  1184. ((REG_SZ == dwType) || (REG_EXPAND_SZ == dwType)) &&
  1185. (AsciiStrCmpI(szValueName, THEMEPROP_LMOVERRIDE) != 0))
  1186. {
  1187. if (AsciiStrCmpI(szValueName, THEMEPROP_DLLNAME) == 0)
  1188. {
  1189. hr = RegistryStrWriteExpand(hkcu, szValueName, szValueData);
  1190. }
  1191. else
  1192. {
  1193. hr = RegistryStrWrite(hkcu, szValueName, szValueData);
  1194. }
  1195. }
  1196. } while ((dwErrorCode == ERROR_SUCCESS) && SUCCEEDED(hr));
  1197. // Since we wrote a new DLL name, erase the old names
  1198. (DWORD)RegDeleteValue(hkcu, THEMEPROP_COLORNAME);
  1199. (DWORD)RegDeleteValue(hkcu, THEMEPROP_SIZENAME);
  1200. }
  1201. }
  1202. }
  1203. else
  1204. {
  1205. hr = S_OK;
  1206. }
  1207. BOOL fLoadedBefore = 1;
  1208. if (SUCCEEDED(RegistryIntRead(hklm, THEMEPROP_LOADEDBEFORE, &fLoadedBefore)) && fLoadedBefore == 0)
  1209. {
  1210. // HKLM\..\LoadedBefore was reset to 0 during Setup, propagate it to HKCU (.DEFAULT), so that the
  1211. // metrics get refreshed for the Winlogon dialogs.
  1212. RegistryIntWrite(hkcu, THEMEPROP_LOADEDBEFORE, 0);
  1213. // Mark it done
  1214. RegistryIntWrite(hklm, THEMEPROP_LOADEDBEFORE, 1);
  1215. }
  1216. TW32(RegCloseKey(hkcu));
  1217. }
  1218. else
  1219. {
  1220. dwErrorCode = GetLastError();
  1221. hr = HRESULT_FROM_WIN32(dwErrorCode);
  1222. }
  1223. TW32(RegCloseKey(hklm));
  1224. }
  1225. else
  1226. {
  1227. // It's possible for this key to be absent. Ignore the error.
  1228. hr = S_OK;
  1229. }
  1230. return(hr);
  1231. }
  1232. // --------------------------------------------------------------------------
  1233. // CThemeServices::ReestablishServerConnection
  1234. //
  1235. // Arguments: <none>
  1236. //
  1237. // Returns: HRESULT
  1238. //
  1239. // Purpose: Forces an attempt to reconnect to the theme server. Used when
  1240. // the port was disconnected but a refresh is desired because the
  1241. // server came back up.
  1242. //
  1243. // History: 2000-11-17 vtan created
  1244. // --------------------------------------------------------------------------
  1245. HRESULT CThemeServices::ReestablishServerConnection (void)
  1246. {
  1247. HRESULT hr;
  1248. NTSTATUS status;
  1249. //---- do we have a good looking handle that as gone bad? ----
  1250. if ((s_hAPIPort != NULL) && (s_hAPIPort != INVALID_HANDLE_VALUE))
  1251. {
  1252. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  1253. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  1254. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  1255. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_PING;
  1256. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  1257. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  1258. status = NtRequestWaitReplyPort(s_hAPIPort,
  1259. &portMessageIn.portMessage,
  1260. &portMessageOut.portMessage);
  1261. if (NT_SUCCESS(status))
  1262. {
  1263. status = portMessageOut.apiThemes.apiGeneric.status;
  1264. }
  1265. }
  1266. else
  1267. {
  1268. status = STATUS_PORT_DISCONNECTED;
  1269. }
  1270. if (NT_SUCCESS(status))
  1271. {
  1272. hr = S_OK;
  1273. }
  1274. else
  1275. {
  1276. //---- our handle has gone bad; reset for another try on next service call ----
  1277. LockAcquire();
  1278. if ((s_hAPIPort != NULL) && (s_hAPIPort != INVALID_HANDLE_VALUE))
  1279. {
  1280. TBOOL(CloseHandle(s_hAPIPort));
  1281. }
  1282. s_hAPIPort = INVALID_HANDLE_VALUE;
  1283. LockRelease();
  1284. hr = S_FALSE;
  1285. }
  1286. return(hr);
  1287. }
  1288. // --------------------------------------------------------------------------
  1289. // CThemeServices::LockAcquire
  1290. //
  1291. // Arguments: <none>
  1292. //
  1293. // Returns: <none>
  1294. //
  1295. // Purpose: Acquire the critical section.
  1296. //
  1297. // History: 2000-12-01 vtan created
  1298. // --------------------------------------------------------------------------
  1299. void CThemeServices::LockAcquire (void)
  1300. {
  1301. SAFE_ENTERCRITICALSECTION(&s_lock);
  1302. }
  1303. // --------------------------------------------------------------------------
  1304. // CThemeServices::LockRelease
  1305. //
  1306. // Arguments: <none>
  1307. //
  1308. // Returns: <none>
  1309. //
  1310. // Purpose: Release the critical section.
  1311. //
  1312. // History: 2000-12-01 vtan created
  1313. // --------------------------------------------------------------------------
  1314. void CThemeServices::LockRelease (void)
  1315. {
  1316. SAFE_LEAVECRITICALSECTION(&s_lock);
  1317. }
  1318. // --------------------------------------------------------------------------
  1319. // CThemeServices::ConnectedToService
  1320. //
  1321. // Arguments: <none>
  1322. //
  1323. // Returns: bool
  1324. //
  1325. // Purpose: Demand connect to service. Only do this once. This function
  1326. // has knowledge of where the port exists within the NT object
  1327. // namespace.
  1328. //
  1329. // History: 2000-10-27 vtan created
  1330. // --------------------------------------------------------------------------
  1331. bool CThemeServices::ConnectedToService (void)
  1332. {
  1333. if (s_hAPIPort == INVALID_HANDLE_VALUE)
  1334. {
  1335. ULONG ulConnectionInfoLength;
  1336. UNICODE_STRING portName;
  1337. SECURITY_QUALITY_OF_SERVICE sqos;
  1338. WCHAR szConnectionInfo[32];
  1339. RtlInitUnicodeString(&portName, THEMES_PORT_NAME);
  1340. sqos.Length = sizeof(sqos);
  1341. sqos.ImpersonationLevel = SecurityImpersonation;
  1342. sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  1343. sqos.EffectiveOnly = TRUE;
  1344. StringCchCopyW(szConnectionInfo, ARRAYSIZE(szConnectionInfo), THEMES_CONNECTION_REQUEST);
  1345. ulConnectionInfoLength = sizeof(szConnectionInfo);
  1346. LockAcquire();
  1347. if (!NT_SUCCESS(NtConnectPort(&s_hAPIPort,
  1348. &portName,
  1349. &sqos,
  1350. NULL,
  1351. NULL,
  1352. NULL,
  1353. szConnectionInfo,
  1354. &ulConnectionInfoLength)))
  1355. {
  1356. s_hAPIPort = NULL;
  1357. }
  1358. LockRelease();
  1359. }
  1360. return(s_hAPIPort != NULL);
  1361. }
  1362. // --------------------------------------------------------------------------
  1363. // CThemeServices::ReleaseConnection
  1364. //
  1365. // Arguments: <none>
  1366. //
  1367. // Returns: <none>
  1368. //
  1369. // Purpose: Releases the API port connection.
  1370. //
  1371. // History: 2000-11-17 vtan created
  1372. // --------------------------------------------------------------------------
  1373. void CThemeServices::ReleaseConnection (void)
  1374. {
  1375. if ((s_hAPIPort != INVALID_HANDLE_VALUE) && (s_hAPIPort != NULL))
  1376. {
  1377. LockAcquire();
  1378. TBOOL(CloseHandle(s_hAPIPort));
  1379. s_hAPIPort = INVALID_HANDLE_VALUE;
  1380. LockRelease();
  1381. }
  1382. }
  1383. // --------------------------------------------------------------------------
  1384. // CThemeServices::CheckForDisconnectedPort
  1385. //
  1386. // Arguments: status = NTSTATUS of last API request.
  1387. //
  1388. // Returns: <none>
  1389. //
  1390. // Purpose: Checks for STATUS_PORT_DISCONNECTED. If found then it
  1391. // releases the port object and NULL out the handle.
  1392. //
  1393. // History: 2000-11-17 vtan created
  1394. // --------------------------------------------------------------------------
  1395. void CThemeServices::CheckForDisconnectedPort (NTSTATUS status)
  1396. {
  1397. if (STATUS_PORT_DISCONNECTED == status)
  1398. {
  1399. ReleaseConnection();
  1400. }
  1401. #ifdef DEBUG
  1402. else if( !NT_SUCCESS(status) )
  1403. {
  1404. Log(LOG_ALWAYS, L"ThemeServices::CheckForDisconnectedPort failure status code: %08lX\n", status);
  1405. }
  1406. #endif DEBUG
  1407. }
  1408. // --------------------------------------------------------------------------
  1409. // CThemeServices::CurrentThemeMatch
  1410. //
  1411. // Arguments: pszThemeName = Name of theme.
  1412. // pszColor = Color.
  1413. // pszSize = Size.
  1414. // fLoadMetricsOnMatch = Load metrics.
  1415. //
  1416. // Returns: HRESULT
  1417. //
  1418. // Purpose: Is the current theme the same as the theme specified? This
  1419. // can be used to save reloading a theme when it's the same.
  1420. //
  1421. // History: 2000-11-11 vtan created (ported from themeldr.cpp)
  1422. // --------------------------------------------------------------------------
  1423. bool CThemeServices::CurrentThemeMatch (LPCWSTR pszThemeName, LPCWSTR pszColor, LPCWSTR pszSize, LANGID wLangID, bool fLoadMetricsOnMatch)
  1424. {
  1425. bool fMatch;
  1426. HANDLE hSection;
  1427. fMatch = false;
  1428. if (SUCCEEDED(GetGlobalTheme(&hSection)) && (hSection != NULL))
  1429. {
  1430. CThemeSection pThemeSectionFile;
  1431. if (SUCCEEDED(pThemeSectionFile.Open(hSection)))
  1432. {
  1433. fMatch = (ThemeMatch(pThemeSectionFile, pszThemeName, pszColor, pszSize, wLangID) != FALSE);
  1434. if (fMatch)
  1435. {
  1436. //---- ensure color depth of monitor(s) are enough for theme ----
  1437. if (GetSystemMetrics(SM_REMOTESESSION)) // only check for terminal server sessions
  1438. {
  1439. if (FAILED(CheckColorDepth(pThemeSectionFile)))
  1440. {
  1441. fMatch = FALSE;
  1442. }
  1443. }
  1444. }
  1445. if (fMatch && fLoadMetricsOnMatch)
  1446. {
  1447. SetSystemMetrics(GetThemeMetricsPtr(pThemeSectionFile), FALSE);
  1448. }
  1449. }
  1450. TBOOL(CloseHandle(hSection));
  1451. }
  1452. return(fMatch);
  1453. }
  1454. // --------------------------------------------------------------------------
  1455. // CThemeServices::LoadCurrentTheme
  1456. //
  1457. // Arguments: <none>
  1458. //
  1459. // Returns: HRESULT
  1460. //
  1461. // Purpose: Loads the current theme as set in the registry for the
  1462. // impersonated user.
  1463. //
  1464. // History: 2000-11-11 vtan created (ported from themeldr.cpp)
  1465. // --------------------------------------------------------------------------
  1466. HRESULT CThemeServices::LoadCurrentTheme (void)
  1467. {
  1468. HRESULT hr = S_OK;
  1469. WCHAR szThemeName[MAX_PATH];
  1470. WCHAR szColorName[MAX_PATH];
  1471. WCHAR szSizeName[MAX_PATH];
  1472. THR(GetCurrentUserThemeString(THEMEPROP_DLLNAME, L"", szThemeName, ARRAYSIZE(szThemeName)));
  1473. if (szThemeName[0] != L'\0')
  1474. {
  1475. int iLoadedBefore;
  1476. HANDLE hSection;
  1477. int nLangID;
  1478. THR(GetCurrentUserThemeString(THEMEPROP_COLORNAME, L"", szColorName, ARRAYSIZE(szColorName)));
  1479. THR(GetCurrentUserThemeString(THEMEPROP_SIZENAME, L"", szSizeName, ARRAYSIZE(szSizeName)));
  1480. THR(GetCurrentUserThemeInt(THEMEPROP_LOADEDBEFORE, 0, &iLoadedBefore));
  1481. THR(GetCurrentUserThemeInt(THEMEPROP_LANGID, -1, &nLangID));
  1482. // Does new user's theme match the current theme?
  1483. if (nLangID != -1 && CurrentThemeMatch(szThemeName, szColorName, szSizeName, (LANGID) nLangID, (iLoadedBefore == 0)))
  1484. {
  1485. DWORD dwFlags;
  1486. // Everything is done except this registry value.
  1487. if (iLoadedBefore == 0)
  1488. {
  1489. THR(SetCurrentUserThemeInt(THEMEPROP_LOADEDBEFORE, 1));
  1490. }
  1491. hr = GetStatusFlags(&dwFlags);
  1492. if (SUCCEEDED(hr))
  1493. {
  1494. if ((dwFlags & QTS_RUNNING) == 0)
  1495. {
  1496. hr = GetGlobalTheme(&hSection);
  1497. if (SUCCEEDED(hr))
  1498. {
  1499. CUxThemeFile file; // Will clean up on destruction
  1500. if (SUCCEEDED(file.OpenFromHandle(hSection, FILE_MAP_READ, TRUE)))
  1501. {
  1502. hr = ApplyTheme(&file, 0, false);
  1503. }
  1504. }
  1505. }
  1506. }
  1507. }
  1508. else
  1509. {
  1510. hr = LoadTheme(&hSection, szThemeName, szColorName, szSizeName, TRUE);
  1511. if (SUCCEEDED(hr))
  1512. {
  1513. DWORD dwFlags;
  1514. dwFlags = 0;
  1515. // Has this theme been loaded before?
  1516. // or has the user changed his/her language?
  1517. if (iLoadedBefore == 0 || ((nLangID != -1) && ((LANGID) nLangID != GetUserDefaultUILanguage())))
  1518. {
  1519. dwFlags |= AT_LOAD_SYSMETRICS;
  1520. }
  1521. CUxThemeFile file; // Will clean up on destruction
  1522. if (SUCCEEDED(file.OpenFromHandle(hSection, FILE_MAP_READ, TRUE)))
  1523. {
  1524. hr = ApplyTheme(&file, dwFlags, false);
  1525. }
  1526. }
  1527. }
  1528. }
  1529. else
  1530. {
  1531. hr = MakeError32(ERROR_NOT_FOUND);
  1532. }
  1533. return(hr);
  1534. }
  1535. // --------------------------------------------------------------------------
  1536. // CThemeServices::SectionProcessType
  1537. //
  1538. // Arguments: hSection = Section to walk and clear stock objects in.
  1539. //
  1540. // Returns: HRESULT
  1541. //
  1542. // Purpose: Walks the section (read-only) and finds HBITMAPs that are stock
  1543. // listed in the section and deletes these objects.
  1544. // This is work that needs to be done on the client.
  1545. //
  1546. // History: 2000-11-17 lmouton created
  1547. // vtan rewritten from themeldr.cpp
  1548. // --------------------------------------------------------------------------
  1549. int CThemeServices::SectionProcessType (const BYTE *pbThemeData, MIXEDPTRS& u)
  1550. {
  1551. UNPACKED_ENTRYHDR header;
  1552. FillAndSkipHdr(u, &header);
  1553. switch (header.ePrimVal)
  1554. {
  1555. case TMT_PARTJUMPTABLE:
  1556. case TMT_STATEJUMPTABLE:
  1557. break;
  1558. case TMT_DIBDATA:
  1559. TMBITMAPHEADER *pThemeBitmapHeader;
  1560. pThemeBitmapHeader = reinterpret_cast<TMBITMAPHEADER*>(u.pb);
  1561. ASSERT(pThemeBitmapHeader->dwSize == TMBITMAPSIZE);
  1562. // Clean up stock bitmaps
  1563. if (pThemeBitmapHeader->hBitmap != NULL)
  1564. {
  1565. HBITMAP hBitmap;
  1566. hBitmap = pThemeBitmapHeader->hBitmap;
  1567. hBitmap = ClearBitmapAttributes(hBitmap, SBA_STOCK);
  1568. #ifdef DEBUG
  1569. if (hBitmap == NULL)
  1570. {
  1571. Log(LOG_TMBITMAP, L"UxTheme: ClearBitmapAttributes failed for %8X in SetGlobalTheme", hBitmap);
  1572. }
  1573. else if (!DeleteObject(hBitmap))
  1574. {
  1575. Log(LOG_TMBITMAP, L"Failed to delete bitmap:%8X", hBitmap);
  1576. }
  1577. #else
  1578. if (hBitmap != NULL)
  1579. {
  1580. DeleteObject(hBitmap);
  1581. }
  1582. #endif
  1583. }
  1584. // Fall thru to the default case that increments the mixed pointer.
  1585. default:
  1586. u.pb += header.dwDataLen;
  1587. break;
  1588. }
  1589. return(header.ePrimVal);
  1590. }
  1591. // --------------------------------------------------------------------------
  1592. // CThemeServices::SectionWalkData
  1593. //
  1594. // Arguments: pV = Address of section data to walk.
  1595. // iIndex = Index into section.
  1596. //
  1597. // Returns: <none>
  1598. //
  1599. // Purpose:
  1600. //
  1601. // History: 2000-11-17 lmouton created
  1602. // vtan rewritten from themeldr.cpp
  1603. // --------------------------------------------------------------------------
  1604. void CThemeServices::SectionWalkData (const BYTE *pbThemeData, int iIndexIn)
  1605. {
  1606. bool fDone;
  1607. MIXEDPTRS u;
  1608. fDone = false;
  1609. u.pb = const_cast<BYTE*>(pbThemeData + iIndexIn);
  1610. while (!fDone)
  1611. {
  1612. //---- special post-handling ----
  1613. switch (SectionProcessType(pbThemeData, u))
  1614. {
  1615. int i, iLimit, iIndex;
  1616. case TMT_PARTJUMPTABLE:
  1617. u.pi++;
  1618. iLimit = *u.pb++;
  1619. for (i = 0; i < iLimit; ++i)
  1620. {
  1621. iIndex = *u.pi++;
  1622. if (iIndex > -1)
  1623. {
  1624. SectionWalkData(pbThemeData, iIndex);
  1625. }
  1626. }
  1627. fDone = true;
  1628. break;
  1629. case TMT_STATEJUMPTABLE:
  1630. iLimit = *u.pb++;
  1631. for (i = 0; i < iLimit; ++i)
  1632. {
  1633. iIndex = *u.pi++;
  1634. if (iIndex > -1)
  1635. {
  1636. SectionWalkData(pbThemeData, iIndex);
  1637. }
  1638. }
  1639. fDone = true;
  1640. break;
  1641. case TMT_JUMPTOPARENT:
  1642. fDone = true;
  1643. break;
  1644. default:
  1645. break;
  1646. }
  1647. }
  1648. }
  1649. // --------------------------------------------------------------------------
  1650. // CThemeServices::ClearStockObjects
  1651. //
  1652. // Arguments: hSection = Section to walk and clear bitmaps in.
  1653. //
  1654. // Returns: HRESULT
  1655. //
  1656. // Purpose: Walks the section (read-only) and finds HBITMAPs and corresponding
  1657. // HBRUSHes that are stock listed in the section and deletes these objects.
  1658. // This is work that needs to be done on the client.
  1659. //
  1660. // History: 2000-11-17 lmouton created
  1661. // vtan rewritten from themeldr.cpp
  1662. // 2001-05-15 lmouton Added semaphore support for cleaning from ~CUxThemeFile
  1663. // --------------------------------------------------------------------------
  1664. HRESULT CThemeServices::ClearStockObjects (HANDLE hSection, BOOL fForce)
  1665. {
  1666. HRESULT hr;
  1667. BYTE* pbThemeData;
  1668. bool bWriteable = true;
  1669. HANDLE hSectionWrite = NULL;
  1670. // If the section is global, we can't write to it since only the server can.
  1671. // So let's try to get write access, else we'll call the server
  1672. pbThemeData = static_cast<BYTE*>(MapViewOfFile(hSection,
  1673. FILE_MAP_WRITE,
  1674. 0,
  1675. 0,
  1676. 0));
  1677. if (pbThemeData == NULL)
  1678. {
  1679. // Let's try to reopen a write handle for ourselves
  1680. if (DuplicateHandle(GetCurrentProcess(),
  1681. hSection,
  1682. GetCurrentProcess(),
  1683. &hSectionWrite,
  1684. FILE_MAP_WRITE,
  1685. FALSE,
  1686. 0) != FALSE)
  1687. {
  1688. pbThemeData = static_cast<BYTE*>(MapViewOfFile(hSectionWrite,
  1689. FILE_MAP_WRITE,
  1690. 0,
  1691. 0,
  1692. 0));
  1693. }
  1694. if (pbThemeData == NULL)
  1695. {
  1696. // We can't open it for write, let's try read-only
  1697. pbThemeData = static_cast<BYTE*>(MapViewOfFile(hSection,
  1698. FILE_MAP_READ,
  1699. 0,
  1700. 0,
  1701. 0));
  1702. bWriteable = false;
  1703. }
  1704. #ifdef DEBUG
  1705. else
  1706. {
  1707. Log(LOG_TMLOAD, L"Reopened section %d for write", reinterpret_cast<THEMEHDR*>(pbThemeData)->iLoadId);
  1708. }
  1709. #endif
  1710. }
  1711. #ifdef DEBUG
  1712. if (LogOptionOn(LO_TMLOAD))
  1713. {
  1714. // Unexpected failure
  1715. ASSERT(pbThemeData != NULL);
  1716. }
  1717. #endif
  1718. if (pbThemeData != NULL)
  1719. {
  1720. int i, iLimit;
  1721. THEMEHDR *pThemeHdr;
  1722. APPCLASSLIVE *pACL;
  1723. pThemeHdr = reinterpret_cast<THEMEHDR*>(pbThemeData);
  1724. hr = S_OK;
  1725. Log(LOG_TMLOAD, L"ClearStockObjects for section %X, bWriteable=%d, dwFlags=%d, iLoadId=%d, fForce=%d",
  1726. hSection, bWriteable, pThemeHdr->dwFlags, pThemeHdr->iLoadId, fForce);
  1727. volatile THEMEHDR* pTmpHdr = pThemeHdr;
  1728. // If this is a local section with stock objects, it's this process's responsibility to
  1729. // clean them up.
  1730. if ((pTmpHdr->dwFlags & SECTION_HASSTOCKOBJECTS) && !(pTmpHdr->dwFlags & SECTION_GLOBAL))
  1731. {
  1732. // Make sure we don't contend with any other cleanup threads
  1733. WCHAR szName[64];
  1734. if (pThemeHdr->iLoadId != 0)
  1735. {
  1736. // Each section has a unique iLoadId but not across sessions
  1737. // It has to be global because the Theme service can create it
  1738. StringCchPrintfW(szName, ARRAYSIZE(szName),
  1739. L"Global\\ClearStockGlobal%d-%d", pThemeHdr->iLoadId, NtCurrentPeb()->SessionId);
  1740. }
  1741. else
  1742. {
  1743. // The session is local to the process
  1744. StringCchPrintfW(szName, ARRAYSIZE(szName),
  1745. L"ClearStockLocal%d-%d", GetCurrentProcessId(), NtCurrentPeb()->SessionId);
  1746. }
  1747. HANDLE hSemaphore = CreateSemaphore(NULL, 0, 1, szName);
  1748. DWORD dwError = GetLastError();
  1749. Log(LOG_TMLOAD, L"Opening semaphore %s, hSemaphore=%X, gle=%d", szName, hSemaphore, dwError);
  1750. // If CreateSemaphore fails for another reason, ignore the failure, we have to clean and we only
  1751. // risk a GDI assert on CHK builds.
  1752. // We'll get access denied if the semaphore was created in the service on SetGlobalTheme, but
  1753. // in this case fForce is true for winlogon, false for the other callers.
  1754. bool bAlreadyExists = (dwError == ERROR_ALREADY_EXISTS || dwError == ERROR_ACCESS_DENIED);
  1755. #ifdef DEBUG
  1756. if (LogOptionOn(LO_TMLOAD))
  1757. {
  1758. // Unexpected failure
  1759. ASSERT(dwError == 0 || bAlreadyExists);
  1760. }
  1761. #endif
  1762. // Recheck again that this is a local section with stock objects. We've seen in stress
  1763. // runs where in the middle of creating the semaphore (above), another thread
  1764. // raced ahead and removed the SECTION_GLOBAL flag and cleared the stock bitmaps, but hadn't
  1765. // quite gotten around to removing the SECTION_HASSTOCKOBJECTS bit before this thread
  1766. // woke up and executed, resulting in hundreds of GDI assertions that the bitmap isn't stock, etc.
  1767. if ((!bAlreadyExists || fForce)
  1768. && ((pTmpHdr->dwFlags & SECTION_HASSTOCKOBJECTS) && !(pTmpHdr->dwFlags & SECTION_GLOBAL)))
  1769. {
  1770. // If nobody else is already doing it
  1771. Log(LOG_TMLOAD, L"ClearStockObjects: Clearing data, semaphore = %s", szName);
  1772. #ifdef DEBUG
  1773. bool bDisconnected = false;
  1774. #endif
  1775. pACL = reinterpret_cast<APPCLASSLIVE*>(pbThemeData + pThemeHdr->iSectionIndexOffset);
  1776. iLimit = pThemeHdr->iSectionIndexLength / sizeof(APPCLASSLIVE);
  1777. for (i = 0; i < iLimit; ++pACL, ++i)
  1778. {
  1779. SectionWalkData(pbThemeData, pACL->iIndex);
  1780. }
  1781. if (bWriteable)
  1782. {
  1783. pThemeHdr->dwFlags &= ~SECTION_HASSTOCKOBJECTS; // To avoid doing it twice
  1784. }
  1785. else
  1786. {
  1787. // Can't write to it, let's call MarkSection in the service to do it
  1788. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  1789. if (ConnectedToService())
  1790. {
  1791. NTSTATUS status;
  1792. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  1793. ZeroMemory(&portMessageIn, sizeof(portMessageIn));
  1794. ZeroMemory(&portMessageOut, sizeof(portMessageOut));
  1795. portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_MARKSECTION;
  1796. portMessageIn.apiThemes.apiSpecific.apiMarkSection.in.hSection = hSection;
  1797. portMessageIn.apiThemes.apiSpecific.apiMarkSection.in.dwAdd = 0;
  1798. portMessageIn.apiThemes.apiSpecific.apiMarkSection.in.dwRemove = SECTION_HASSTOCKOBJECTS;
  1799. portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  1800. portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  1801. status = NtRequestWaitReplyPort(s_hAPIPort,
  1802. &portMessageIn.portMessage,
  1803. &portMessageOut.portMessage);
  1804. CheckForDisconnectedPort(status);
  1805. #ifdef DEBUG
  1806. if (STATUS_PORT_DISCONNECTED == status)
  1807. {
  1808. bDisconnected = true; // This failure must not trigger the assert
  1809. }
  1810. #endif
  1811. if (NT_SUCCESS(status))
  1812. {
  1813. status = portMessageOut.apiThemes.apiGeneric.status;
  1814. if (NT_SUCCESS(status))
  1815. {
  1816. hr = S_OK;
  1817. }
  1818. }
  1819. if (!NT_SUCCESS(status))
  1820. {
  1821. hr = HRESULT_FROM_NT(status);
  1822. }
  1823. }
  1824. }
  1825. #ifdef DEBUG
  1826. // When the service goes down, we may fail ApplyTheme (so iLoadId is still 0),
  1827. // and we fail MarkSection too, ignore this error.
  1828. if (LogOptionOn(LO_TMLOAD) && !bDisconnected && pThemeHdr->iLoadId != 0)
  1829. {
  1830. ASSERT(!(pThemeHdr->dwFlags & SECTION_HASSTOCKOBJECTS));
  1831. }
  1832. #endif
  1833. }
  1834. else
  1835. {
  1836. Log(LOG_TMLOAD, L"ClearStockObjects: Doing nothing, semaphore %s, dwFlags=%d, gle=%d", szName, pThemeHdr->dwFlags, dwError);
  1837. }
  1838. if (hSemaphore)
  1839. {
  1840. Log(LOG_TMLOAD, L"ClearStockObjects: Closing semaphore %X", hSemaphore);
  1841. CloseHandle(hSemaphore);
  1842. }
  1843. }
  1844. TBOOL(UnmapViewOfFile(pbThemeData));
  1845. }
  1846. else
  1847. {
  1848. DWORD dwErrorCode;
  1849. dwErrorCode = GetLastError();
  1850. hr = HRESULT_FROM_WIN32(dwErrorCode);
  1851. }
  1852. if (hSectionWrite != NULL)
  1853. {
  1854. CloseHandle(hSectionWrite);
  1855. }
  1856. return(hr);
  1857. }
  1858. // --------------------------------------------------------------------------
  1859. // CThemeServices::ThemeSettingsModified
  1860. //
  1861. // Returns: BOOL
  1862. //
  1863. // Purpose: Detects that appearance settings have been changed on a
  1864. // W2K machine by a roaming user.
  1865. //
  1866. // History: 2000-11-28 lmouton created
  1867. // --------------------------------------------------------------------------
  1868. bool CThemeServices::ThemeSettingsModified (void)
  1869. {
  1870. WCHAR szCurrent[MAX_PATH];
  1871. WCHAR szNewCurrent[MAX_PATH];
  1872. // If NewCurrent exists and is different from Current, Current
  1873. // has been tampered with on a roaming W2K machine
  1874. THR(GetCurrentUserString(CONTROLPANEL_APPEARANCE_REGKEY, THEMEPROP_CURRSCHEME, L" ", szCurrent, ARRAYSIZE(szCurrent)));
  1875. THR(GetCurrentUserString(CONTROLPANEL_APPEARANCE_REGKEY, THEMEPROP_NEWCURRSCHEME, L" ", szNewCurrent, ARRAYSIZE(szNewCurrent)));
  1876. return((lstrcmpW(szNewCurrent, L" ") != 0) && (lstrcmpW(szCurrent, szNewCurrent) != 0));
  1877. }
  1878. // --------------------------------------------------------------------------
  1879. // CThemeServices::ThemeEnforcedByPolicy
  1880. //
  1881. // Arguments: BOOL TRUE if a .msstyles file is currently active for the user
  1882. //
  1883. // Returns: BOOL TRUE if the policy changed something
  1884. //
  1885. // Purpose: Loads the .msstyles file specified in the SetVisualStyle policy.
  1886. //
  1887. // History: 2000-11-28 lmouton created
  1888. // --------------------------------------------------------------------------
  1889. bool CThemeServices::ThemeEnforcedByPolicy (bool fActive)
  1890. {
  1891. bool fPolicyPresent;
  1892. HKEY hKeyPol = NULL;
  1893. CCurrentUser hKeyCurrentUser(KEY_READ | KEY_WRITE);
  1894. fPolicyPresent = false;
  1895. // See if a policy overrides the theme name
  1896. if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyCurrentUser,
  1897. REGSTR_PATH_POLICIES L"\\" SZ_THEME_POLICY_KEY,
  1898. 0,
  1899. KEY_QUERY_VALUE,
  1900. &hKeyPol)))
  1901. {
  1902. WCHAR szNewThemeName[MAX_PATH + 1];
  1903. StringCchCopyW(szNewThemeName, ARRAYSIZE(szNewThemeName), L" ");
  1904. if (SUCCEEDED(RegistryStrRead(hKeyPol,
  1905. SZ_POLICY_SETVISUALSTYLE,
  1906. szNewThemeName,
  1907. ARRAYSIZE(szNewThemeName))))
  1908. {
  1909. if (szNewThemeName[0] == L'\0') // Disable themes
  1910. {
  1911. if (fActive)
  1912. {
  1913. THR(UpdateThemeRegistry(FALSE, NULL, NULL, NULL, FALSE, FALSE));
  1914. fPolicyPresent = true;
  1915. }
  1916. }
  1917. else
  1918. {
  1919. if (FileExists(szNewThemeName))
  1920. {
  1921. HRESULT hr = UpdateThemeRegistry(TRUE, szNewThemeName, NULL, NULL, FALSE, FALSE);
  1922. THR(hr);
  1923. if (!fActive || hr == S_OK)
  1924. {
  1925. // If we had no theme before or a different one, say we changed something
  1926. fPolicyPresent = true;
  1927. }
  1928. }
  1929. }
  1930. }
  1931. TW32(RegCloseKey(hKeyPol));
  1932. }
  1933. return(fPolicyPresent);
  1934. }
  1935. // --------------------------------------------------------------------------
  1936. // ::CThemeServices::SendProcessAssignSection
  1937. //
  1938. // Arguments: hrApply = Error HRESULT to forward to themeservice
  1939. // hSection = read-write theme section handle, valid in current address space.
  1940. // dwHash = hash value for theme section
  1941. // pPortMsgIn = port message object for parameters sent to service
  1942. // pPortMsgOut = port message object for parameters received in response
  1943. //
  1944. //
  1945. // Returns: NTSTATUS
  1946. //
  1947. // Purpose: Worker function to send API_THEMES_PROCESSASSIGNSECTION to request
  1948. //
  1949. // History: 2002-02-26 scotthan created
  1950. // --------------------------------------------------------------------------
  1951. NTSTATUS CThemeServices::SendProcessAssignSection(
  1952. HRESULT hrAssign,
  1953. HANDLE hSection,
  1954. DWORD dwHash,
  1955. OUT THEMESAPI_PORT_MESSAGE* pPortMsgIn,
  1956. OUT THEMESAPI_PORT_MESSAGE* pPortMsgOut )
  1957. {
  1958. NTSTATUS status = STATUS_PORT_DISCONNECTED;
  1959. if (ConnectedToService())
  1960. {
  1961. ZeroMemory(pPortMsgIn, sizeof(*pPortMsgIn));
  1962. ZeroMemory(pPortMsgOut, sizeof(*pPortMsgOut));
  1963. pPortMsgIn->apiThemes.apiGeneric.ulAPINumber = API_THEMES_PROCESSASSIGNSECTION;
  1964. pPortMsgIn->apiThemes.apiSpecific.apiProcessAssignSection.in.hrLoad = hrAssign;
  1965. pPortMsgIn->apiThemes.apiSpecific.apiProcessAssignSection.in.dwHash = dwHash;
  1966. pPortMsgIn->apiThemes.apiSpecific.apiProcessAssignSection.in.hSection = hSection;
  1967. pPortMsgIn->portMessage.u1.s1.DataLength = sizeof(API_THEMES);
  1968. pPortMsgIn->portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
  1969. status = NtRequestWaitReplyPort(s_hAPIPort,&pPortMsgIn->portMessage,
  1970. &pPortMsgOut->portMessage);
  1971. CheckForDisconnectedPort(status);
  1972. }
  1973. return status;
  1974. }
  1975. // --------------------------------------------------------------------------
  1976. // ::ProcessLoadTheme_RunDLLW
  1977. //
  1978. // (Deliberately undocumented entrypoint.)
  1979. //
  1980. // History: 2002-02-26 scotthan created
  1981. // --------------------------------------------------------------------------
  1982. STDAPI_(void) ProcessLoadTheme_RunDLLW(
  1983. HWND hwndDefer, HINSTANCE hInst, LPWSTR lpwszDeferMsg, int cTimeout)
  1984. {
  1985. __try
  1986. {
  1987. WCHAR szModule[MAX_PATH];
  1988. // validate that we're a rundll32 process launched from %windir%\system32
  1989. if( GetModuleFileName(NULL, szModule, ARRAYSIZE(szModule)) )
  1990. {
  1991. WCHAR szRunDll[MAX_PATH];
  1992. if( GetSystemDirectory(szRunDll, ARRAYSIZE(szRunDll)) &&
  1993. SUCCEEDED(StringCchCatW(szRunDll, ARRAYSIZE(szRunDll), L"\\rundll32.exe")) )
  1994. {
  1995. if( 0 == AsciiStrCmpI(szModule, szRunDll) != 0 )
  1996. {
  1997. CThemeServices::ProcessLoaderEntry(lpwszDeferMsg);
  1998. }
  1999. }
  2000. }
  2001. }
  2002. __except(EXCEPTION_EXECUTE_HANDLER)
  2003. {
  2004. Log(LOG_ALWAYS, L"Exception in theme loader process.");
  2005. }
  2006. }
  2007. // --------------------------------------------------------------------------
  2008. // ::CThemeServices::ProcessLoaderEntry
  2009. //
  2010. // Arguments: lpwszCmdLine = cmdline
  2011. //
  2012. //
  2013. // Returns: HRESULT, but no one is listening.
  2014. //
  2015. // Purpose: This is the secure loader process's RunDLL worker function
  2016. // (needed to be a static member to access private static
  2017. // class data.).
  2018. //
  2019. // History: 2002-02-26 scotthan created
  2020. // --------------------------------------------------------------------------
  2021. HRESULT CThemeServices::ProcessLoaderEntry(LPWSTR lpwszCmdLine)
  2022. {
  2023. size_t cchCmdLine;
  2024. HRESULT hr = StringCchLengthW(lpwszCmdLine,
  2025. (MAX_PATH*3) + 2 /* three strings delimited by 2 spaces */,
  2026. &cchCmdLine);
  2027. ASSERT(SUCCEEDED(hr));
  2028. if( SUCCEEDED(hr) )
  2029. {
  2030. // make a local copy of the command line, which we'll modify in parsing.
  2031. LPWSTR pszCmdLine = new WCHAR[cchCmdLine + 1];
  2032. if( pszCmdLine )
  2033. {
  2034. LPWSTR rgArgs[3] = {0}; // array of pointers to args in pszCmdLine
  2035. int iArg = 0; // tracks current index into rgArgs.
  2036. enum {iThemeFileArg, iColorVariantArg, iSizeVariantArg}; // rgArg array indices
  2037. StringCchCopyW(pszCmdLine, cchCmdLine + 1, lpwszCmdLine);
  2038. LPWSTR psz; // working cmdline char pointer
  2039. LPWSTR pszArg; // address of current arg token
  2040. // skip spaces
  2041. for( psz = pszCmdLine; L' ' == *psz; psz++);
  2042. // init first arg token
  2043. pszArg = psz;
  2044. // capture arg tokens (delimited by '?') and null terminate each.
  2045. while(*psz != 0)
  2046. {
  2047. if( L'?' == *psz )
  2048. {
  2049. // null-terminate and assign to arg list
  2050. *psz = 0; // null terminate
  2051. if( iArg < ARRAYSIZE(rgArgs) )
  2052. {
  2053. rgArgs[iArg] = pszArg;
  2054. pszArg = ++psz; // advance past the delimiter and update arg token.
  2055. }
  2056. iArg++; // advance current arg index
  2057. }
  2058. else
  2059. {
  2060. psz++; // advance cmdline char ptr.
  2061. }
  2062. }
  2063. // assign the last arg.
  2064. if( iArg < ARRAYSIZE(rgArgs) )
  2065. {
  2066. rgArgs[iArg++] = pszArg;
  2067. }
  2068. // check arg count. Correct count should be == ARRAYSIZE(rgArgs)
  2069. if( ARRAYSIZE(rgArgs) == iArg )
  2070. {
  2071. hr = S_OK;
  2072. HANDLE hSection = NULL;
  2073. DWORD dwHash = 0;
  2074. // create the memory section
  2075. CThemeLoader *pLoader = new CThemeLoader;
  2076. if (pLoader != NULL)
  2077. {
  2078. hr = pLoader->LoadTheme(rgArgs[iThemeFileArg], rgArgs[iColorVariantArg],
  2079. rgArgs[iSizeVariantArg], &hSection, TRUE);
  2080. delete pLoader;
  2081. }
  2082. else
  2083. {
  2084. hr = E_OUTOFMEMORY;
  2085. }
  2086. // compute a hash to further secure the data
  2087. if( SUCCEEDED(hr) )
  2088. {
  2089. ASSERT(hSection);
  2090. #if 0
  2091. hr = HashThemeSection(hSection, Get
  2092. GetCurrentProcessId() /* as hash parameter, which server possesses and can use for data validate */,
  2093. &dwHash );
  2094. #endif
  2095. }
  2096. // unconditionally notify service of what happened so the result can be propagated to the
  2097. // LPC client of API_THEMES_PROCESSLOADTHEME:
  2098. NTSTATUS status;
  2099. THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
  2100. status = SendProcessAssignSection(hr, hSection, dwHash, &portMessageIn, &portMessageOut);
  2101. // if we were able to create the section and hash it...
  2102. if( SUCCEEDED(hr) )
  2103. {
  2104. // check LPC result
  2105. hr = HRESULT_FROM_NT(status);
  2106. if (NT_SUCCESS(status))
  2107. {
  2108. // check service's high-level result
  2109. status = portMessageOut.apiThemes.apiGeneric.status;
  2110. hr = HRESULT_FROM_NT(status);
  2111. if (NT_SUCCESS(status))
  2112. {
  2113. // check service's specialized result*/
  2114. hr = portMessageOut.apiThemes.apiSpecific.apiProcessAssignSection.out.hr;
  2115. }
  2116. }
  2117. }
  2118. if( hSection != NULL )
  2119. {
  2120. // if we failed anywhere, we need to destroy stock objects
  2121. if( FAILED(hr) )
  2122. {
  2123. ClearStockObjects( hSection, TRUE );
  2124. }
  2125. // And whether we succeeded or failed, we close the section handle;
  2126. // we're done with this local read-write copy.
  2127. CloseHandle(hSection);
  2128. }
  2129. }
  2130. else
  2131. {
  2132. hr = E_INVALIDARG;
  2133. }
  2134. delete [] pszCmdLine;
  2135. }
  2136. else
  2137. {
  2138. hr = E_OUTOFMEMORY;
  2139. }
  2140. }
  2141. return hr;
  2142. }