Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

799 lines
24 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: ThemeServer.cpp
  3. //
  4. // Copyright (c) 2000, Microsoft Corporation
  5. //
  6. // Functions that implement server functionality. Functions in this file
  7. // cannot execute per instance win32k functions that are done on the client's
  8. // behalf. That work must be done on the client side.
  9. //
  10. // History: 2000-11-11 vtan created
  11. // --------------------------------------------------------------------------
  12. #include "stdafx.h"
  13. #include "ThemeServer.h"
  14. #include <shfolder.h>
  15. #include "Loader.h"
  16. #include "Signing.h"
  17. #include "ThemeSection.h"
  18. #include "TmUtils.h"
  19. #include "sethook.h"
  20. #include "log.h"
  21. #include "services.h"
  22. #include <UxThemeServer.h>
  23. #define TBOOL(x) ((BOOL)(x))
  24. #define TW32(x) ((DWORD)(x))
  25. #define THR(x) ((HRESULT)(x))
  26. #define TSTATUS(x) ((NTSTATUS)(x))
  27. #define goto !!DO NOT USE GOTO!! - DO NOT REMOVE THIS ON PAIN OF DEATH
  28. // --------------------------------------------------------------------------
  29. // CThemeServer::CThemeServer
  30. //
  31. // Arguments: hProcessRegisterHook = Process used to install hooks.
  32. // dwServerChangeNumber = Server change number.
  33. // pfnRegister = Address of install hook function.
  34. // pfnUnregister = Address of remove hook function.
  35. // pfnClearStockObjects = Address of function to remove stock objects from section
  36. //
  37. // Returns: <none>
  38. //
  39. // Purpose: Constructor for CThemeServer. Initializes member variables
  40. // with information relevant for the session. Keeps a handle to
  41. // the process that called this to use to inject threads in to
  42. // handle hook installation and removal.
  43. //
  44. // History: 2000-11-11 vtan created
  45. // --------------------------------------------------------------------------
  46. CThemeServer::CThemeServer (HANDLE hProcessRegisterHook, DWORD dwServerChangeNumber, void *pfnRegister, void *pfnUnregister, void *pfnClearStockObjects, DWORD dwStackSizeReserve, DWORD dwStackSizeCommit) :
  47. _hProcessRegisterHook(NULL),
  48. _dwServerChangeNumber(dwServerChangeNumber),
  49. _pfnRegister(pfnRegister),
  50. _pfnUnregister(pfnUnregister),
  51. _pfnClearStockObjects(pfnClearStockObjects),
  52. _dwStackSizeReserve(dwStackSizeReserve),
  53. _dwStackSizeCommit(dwStackSizeCommit),
  54. _dwSessionID(NtCurrentPeb()->SessionId),
  55. _fHostHooksSet(false),
  56. _hSectionGlobalTheme(NULL),
  57. _dwClientChangeNumber(0)
  58. {
  59. ULONG ulReturnLength;
  60. PROCESS_SESSION_INFORMATION processSessionInformation;
  61. InitializeCriticalSection(&_lock);
  62. TBOOL(DuplicateHandle(GetCurrentProcess(),
  63. hProcessRegisterHook,
  64. GetCurrentProcess(),
  65. &_hProcessRegisterHook,
  66. 0,
  67. FALSE,
  68. DUPLICATE_SAME_ACCESS));
  69. if (NT_SUCCESS(NtQueryInformationProcess(hProcessRegisterHook,
  70. ProcessSessionInformation,
  71. &processSessionInformation,
  72. sizeof(processSessionInformation),
  73. &ulReturnLength)))
  74. {
  75. _dwSessionID = processSessionInformation.SessionId;
  76. }
  77. }
  78. // --------------------------------------------------------------------------
  79. // CThemeServer::~CThemeServer
  80. //
  81. // Arguments: <none>
  82. //
  83. // Returns: <none>
  84. //
  85. // Purpose: Destructor for CThemeServer. Releases resources used.
  86. //
  87. // History: 2000-11-11 vtan created
  88. // --------------------------------------------------------------------------
  89. CThemeServer::~CThemeServer (void)
  90. {
  91. //---- important: turn off hooks so everyone gets unthemed ----
  92. if (!GetSystemMetrics(SM_SHUTTINGDOWN)) // Don't do this on shutdown to keep winlogon themed
  93. {
  94. ThemeHooksOff();
  95. }
  96. //---- mark global theme invalid & release it ----
  97. if (_hSectionGlobalTheme != NULL)
  98. {
  99. SetGlobalTheme(NULL);
  100. }
  101. if (_hProcessRegisterHook != NULL)
  102. {
  103. TBOOL(CloseHandle(_hProcessRegisterHook));
  104. _hProcessRegisterHook = NULL;
  105. }
  106. DeleteCriticalSection(&_lock);
  107. }
  108. // --------------------------------------------------------------------------
  109. // CThemeServer::ThemeHooksOn
  110. //
  111. // Arguments: <none>
  112. //
  113. // Returns: HRESULT
  114. //
  115. // Purpose: Install theme hooks via the session controlling process.
  116. //
  117. // History: 2000-11-11 vtan created
  118. // --------------------------------------------------------------------------
  119. HRESULT CThemeServer::ThemeHooksOn (void)
  120. {
  121. HRESULT hr;
  122. LockAcquire();
  123. if (!_fHostHooksSet)
  124. {
  125. if (ClassicGetSystemMetrics(SM_CLEANBOOT) == 0)
  126. {
  127. if (_hProcessRegisterHook != NULL)
  128. {
  129. hr = InjectClientSessionThread(_hProcessRegisterHook, FunctionRegisterUserApiHook, NULL);
  130. _fHostHooksSet = SUCCEEDED(hr);
  131. }
  132. else
  133. {
  134. hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
  135. }
  136. }
  137. else
  138. {
  139. hr = MakeError32(ERROR_BAD_ENVIRONMENT); // themes not allowed in safe mode
  140. }
  141. }
  142. else
  143. {
  144. hr = S_OK;
  145. }
  146. LockRelease();
  147. return(hr);
  148. }
  149. // --------------------------------------------------------------------------
  150. // CThemeServer::ThemeHooksOff
  151. //
  152. // Arguments: <none>
  153. //
  154. // Returns: HRESULT
  155. //
  156. // Purpose: Remove theme hooks via the session controlling process.
  157. //
  158. // History: 2000-11-11 vtan created
  159. // --------------------------------------------------------------------------
  160. HRESULT CThemeServer::ThemeHooksOff (void)
  161. {
  162. LockAcquire();
  163. if (_fHostHooksSet)
  164. {
  165. _fHostHooksSet = false;
  166. if (_hProcessRegisterHook != NULL)
  167. {
  168. THR(InjectClientSessionThread(_hProcessRegisterHook, FunctionUnregisterUserApiHook, NULL));
  169. }
  170. }
  171. LockRelease();
  172. return(S_OK);
  173. }
  174. // --------------------------------------------------------------------------
  175. // CThemeServer::AreThemeHooksActive
  176. //
  177. // Arguments: <none>
  178. //
  179. // Returns: bool
  180. //
  181. // Purpose: Returns whether theme hooks have been successfully installed.
  182. //
  183. // History: 2000-11-11 vtan created
  184. // --------------------------------------------------------------------------
  185. bool CThemeServer::AreThemeHooksActive (void)
  186. {
  187. bool fResult;
  188. LockAcquire();
  189. fResult = _fHostHooksSet;
  190. LockRelease();
  191. return(fResult);
  192. }
  193. // --------------------------------------------------------------------------
  194. // CThemeServer::GetCurrentChangeNumber
  195. //
  196. // Arguments: <none>
  197. //
  198. // Returns: int
  199. //
  200. // Purpose: Returns the current change number.
  201. //
  202. // History: 2000-11-11 vtan created
  203. // --------------------------------------------------------------------------
  204. int CThemeServer::GetCurrentChangeNumber (void)
  205. {
  206. int iCurrentChangeNumber;
  207. LockAcquire();
  208. iCurrentChangeNumber = static_cast<int>((_dwServerChangeNumber << 16) | _dwClientChangeNumber);
  209. LockRelease();
  210. Log(LOG_TMCHANGE, L"GetCurrentChangeNumber: server: %d, client: %d, change: 0x%x",
  211. _dwServerChangeNumber, _dwClientChangeNumber, iCurrentChangeNumber);
  212. return(iCurrentChangeNumber);
  213. }
  214. // --------------------------------------------------------------------------
  215. // CThemeServer::GetNewChangeNumber
  216. //
  217. // Arguments: <none>
  218. //
  219. // Returns: int
  220. //
  221. // Purpose: Returns a new change number.
  222. //
  223. // History: 2000-11-11 vtan created
  224. // --------------------------------------------------------------------------
  225. int CThemeServer::GetNewChangeNumber (void)
  226. {
  227. int iCurrentChangeNumber;
  228. LockAcquire();
  229. _dwClientChangeNumber = static_cast<WORD>(_dwClientChangeNumber + 1);
  230. iCurrentChangeNumber = static_cast<int>((_dwServerChangeNumber << 16) | _dwClientChangeNumber);
  231. Log(LOG_TMLOAD, L"GetNewChangeNumber: server: %d, client: %d, change: 0x%x",
  232. _dwServerChangeNumber, _dwClientChangeNumber, iCurrentChangeNumber);
  233. LockRelease();
  234. return(iCurrentChangeNumber);
  235. }
  236. // --------------------------------------------------------------------------
  237. // CThemeServer::SetGlobalTheme
  238. //
  239. // Arguments: hSection = Handle to section of new theme.
  240. //
  241. // Returns: HRESULT
  242. //
  243. // Purpose: Invalidates the old section and closes the handle to it.
  244. // Validates the new section and if valid sets it as the global
  245. // theme.
  246. //
  247. // History: 2000-11-11 vtan created
  248. // --------------------------------------------------------------------------
  249. HRESULT CThemeServer::SetGlobalTheme (HANDLE hSection)
  250. {
  251. HRESULT hr;
  252. LockAcquire();
  253. if (_hSectionGlobalTheme != NULL)
  254. {
  255. void *pV;
  256. HANDLE hSemaphore = NULL;
  257. // Before closing the section invalidate it.
  258. pV = MapViewOfFile(_hSectionGlobalTheme,
  259. FILE_MAP_WRITE,
  260. 0,
  261. 0,
  262. 0);
  263. if (pV != NULL)
  264. {
  265. // Create a semaphore so that nobody will try to clean it before us, which would result in a double free
  266. // (as soon as we clear SECTION_GLOBAL, various CUxThemeFile destructors can call ClearStockObjects)
  267. WCHAR szName[64];
  268. wsprintf(szName, L"Global\\ClearStockGlobal%d-%d", reinterpret_cast<THEMEHDR*>(pV)->iLoadId, _dwSessionID);
  269. hSemaphore = CreateSemaphore(NULL, 0, 1, szName);
  270. Log(LOG_TMLOAD, L"SetGlobalTheme clearing section %d, semaphore=%s, hSemaphore=%X, gle=%d", reinterpret_cast<THEMEHDR*>(pV)->iLoadId, szName, hSemaphore, GetLastError());
  271. reinterpret_cast<THEMEHDR*>(pV)->dwFlags &= ~(SECTION_READY | SECTION_GLOBAL);
  272. }
  273. #ifdef DEBUG
  274. if (LogOptionOn(LO_TMLOAD))
  275. {
  276. // Unexpected failure
  277. ASSERT(pV != NULL);
  278. }
  279. #endif
  280. HANDLE hSectionForInjection = NULL;
  281. //---- create a handle for CLIENT process to use to clear stock bitmaps ----
  282. if (DuplicateHandle(GetCurrentProcess(),
  283. _hSectionGlobalTheme,
  284. _hProcessRegisterHook,
  285. &hSectionForInjection,
  286. FILE_MAP_READ,
  287. FALSE,
  288. 0) != FALSE)
  289. {
  290. // This will close the handle
  291. THR(InjectClientSessionThread(_hProcessRegisterHook, FunctionClearStockObjects, hSectionForInjection));
  292. }
  293. if (pV != NULL)
  294. {
  295. reinterpret_cast<THEMEHDR*>(pV)->dwFlags &= ~SECTION_HASSTOCKOBJECTS;
  296. if (hSemaphore != NULL)
  297. {
  298. CloseHandle(hSemaphore);
  299. }
  300. TBOOL(UnmapViewOfFile(pV));
  301. }
  302. TBOOL(CloseHandle(_hSectionGlobalTheme));
  303. _hSectionGlobalTheme = NULL;
  304. }
  305. if (hSection != NULL)
  306. {
  307. CThemeSection themeSection;
  308. hr = themeSection.Open(hSection);
  309. if (SUCCEEDED(hr))
  310. {
  311. hr = themeSection.ValidateData(true);
  312. }
  313. if (SUCCEEDED(hr))
  314. {
  315. if (DuplicateHandle(GetCurrentProcess(),
  316. hSection,
  317. GetCurrentProcess(),
  318. &_hSectionGlobalTheme,
  319. FILE_MAP_ALL_ACCESS,
  320. FALSE,
  321. 0) != FALSE)
  322. {
  323. hr = S_OK;
  324. }
  325. else
  326. {
  327. hr = MakeErrorLast();
  328. }
  329. }
  330. }
  331. else
  332. {
  333. hr = S_OK;
  334. }
  335. if (SUCCEEDED(hr))
  336. {
  337. //---- bump the change number at the same time so everything is in sync. ----
  338. int iChangeNum = GetNewChangeNumber();
  339. if (_hSectionGlobalTheme)
  340. {
  341. //---- put changenum into theme hdr to help client keep things straight ----
  342. VOID *pv = MapViewOfFile(_hSectionGlobalTheme,
  343. FILE_MAP_WRITE,
  344. 0,
  345. 0,
  346. 0);
  347. if (pv != NULL)
  348. {
  349. reinterpret_cast<THEMEHDR*>(pv)->dwFlags |= SECTION_GLOBAL;
  350. reinterpret_cast<THEMEHDR*>(pv)->iLoadId = iChangeNum;
  351. Log(LOG_TMLOAD, L"SetGlobalTheme: new section is %d", reinterpret_cast<THEMEHDR*>(pv)->iLoadId);
  352. TBOOL(UnmapViewOfFile(pv));
  353. }
  354. }
  355. }
  356. LockRelease();
  357. return(hr);
  358. }
  359. // --------------------------------------------------------------------------
  360. // CThemeServer::GetGlobalTheme
  361. //
  362. // Arguments: phSection = Handle to the section received.
  363. //
  364. // Returns: HRESULT
  365. //
  366. // Purpose: Duplicates the section back to the caller.
  367. //
  368. // History: 2000-11-11 vtan created
  369. // --------------------------------------------------------------------------
  370. HRESULT CThemeServer::GetGlobalTheme (HANDLE *phSection)
  371. {
  372. HRESULT hr;
  373. LockAcquire();
  374. *phSection = NULL;
  375. if (_hSectionGlobalTheme != NULL)
  376. {
  377. if (DuplicateHandle(GetCurrentProcess(),
  378. _hSectionGlobalTheme,
  379. GetCurrentProcess(),
  380. phSection,
  381. 0,
  382. FALSE,
  383. DUPLICATE_SAME_ACCESS) != FALSE)
  384. {
  385. hr = S_OK;
  386. }
  387. else
  388. {
  389. hr = MakeErrorLast();
  390. }
  391. }
  392. else
  393. {
  394. hr = S_OK;
  395. }
  396. LockRelease();
  397. return(hr);
  398. }
  399. // --------------------------------------------------------------------------
  400. // CThemeServer::LoadTheme
  401. //
  402. // Arguments: hSection = Section created by the client.
  403. // phSection = Section created by the server returned.
  404. // pszName = Theme name.
  405. // pszColor = Theme size.
  406. // pszSize = Theme color.
  407. //
  408. // Returns: HRESULT
  409. //
  410. // Purpose: Creates a new section in the server context based on the
  411. // section from the client. The contents are transferred across.
  412. // The section contents are also strictly verified.
  413. //
  414. // History: 2000-11-11 vtan created
  415. // --------------------------------------------------------------------------
  416. HRESULT CThemeServer::LoadTheme (HANDLE hSection, HANDLE *phSection, LPCWSTR pszName, LPCWSTR pszColor, LPCWSTR pszSize)
  417. {
  418. HRESULT hr;
  419. hr = CheckThemeSignature(pszName); // Check this is signed
  420. if (SUCCEEDED(hr))
  421. {
  422. CThemeSection themeSectionIn;
  423. if (SUCCEEDED(themeSectionIn.Open(hSection)))
  424. {
  425. if (ThemeMatch(themeSectionIn, pszName, pszColor, pszSize, 0) != FALSE)
  426. {
  427. hr = themeSectionIn.ValidateData(true);
  428. if (SUCCEEDED(hr))
  429. {
  430. CThemeSection themeSectionOut;
  431. // Note: we come here impersonating the user, we need for ThemeMatch.
  432. // However the theme section must be created in the system context, so that only
  433. // the system context has write access to it. We revert to self here based on the
  434. // knowledge that nothing after this call needs to be done in the user context.
  435. RevertToSelf();
  436. hr = themeSectionOut.CreateFromSection(hSection);
  437. if (SUCCEEDED(hr))
  438. {
  439. *phSection = themeSectionOut.Get(); // We now own the handle
  440. }
  441. }
  442. }
  443. else
  444. {
  445. hr = E_ACCESSDENIED;
  446. }
  447. }
  448. }
  449. return(hr);
  450. }
  451. // --------------------------------------------------------------------------
  452. // CThemeServer::IsSystemProcessContext
  453. //
  454. // Arguments: <none>
  455. //
  456. // Returns: bool
  457. //
  458. // Purpose: Is the current process executing in the SYSTEM context?
  459. //
  460. // History: 2000-11-11 vtan created
  461. // --------------------------------------------------------------------------
  462. bool CThemeServer::IsSystemProcessContext (void)
  463. {
  464. bool fResult;
  465. HANDLE hToken;
  466. fResult = false;
  467. if (OpenProcessToken(GetCurrentProcess(),
  468. TOKEN_QUERY,
  469. &hToken) != FALSE)
  470. {
  471. static const LUID sLUIDSystem = SYSTEM_LUID;
  472. ULONG ulReturnLength;
  473. TOKEN_STATISTICS tokenStatistics;
  474. fResult = ((GetTokenInformation(hToken,
  475. TokenStatistics,
  476. &tokenStatistics,
  477. sizeof(tokenStatistics),
  478. &ulReturnLength) != FALSE) &&
  479. RtlEqualLuid(&tokenStatistics.AuthenticationId, &sLUIDSystem));
  480. TBOOL(CloseHandle(hToken));
  481. }
  482. return(fResult);
  483. }
  484. // --------------------------------------------------------------------------
  485. // CThemeServer::ThemeHooksInstall
  486. //
  487. // Arguments: <none>
  488. //
  489. // Returns: DWORD
  490. //
  491. // Purpose: Thread entry point for injected thread running in session
  492. // creating process' context to call user32!RegisterUserApiHook.
  493. //
  494. // History: 2000-11-11 vtan created
  495. // --------------------------------------------------------------------------
  496. DWORD CThemeServer::ThemeHooksInstall (void)
  497. {
  498. DWORD dwErrorCode;
  499. if (IsSystemProcessContext())
  500. {
  501. INITUSERAPIHOOK pfnInitUserApiHook = ThemeInitApiHook;
  502. if (RegisterUserApiHook(g_hInst, pfnInitUserApiHook) != FALSE)
  503. {
  504. dwErrorCode = ERROR_SUCCESS;
  505. }
  506. else
  507. {
  508. dwErrorCode = GetLastError();
  509. }
  510. }
  511. else
  512. {
  513. dwErrorCode = ERROR_ACCESS_DENIED;
  514. }
  515. return(dwErrorCode);
  516. }
  517. // --------------------------------------------------------------------------
  518. // CThemeServer::ThemeHooksRemove
  519. //
  520. // Arguments: <none>
  521. //
  522. // Returns: DWORD
  523. //
  524. // Purpose: Thread entry point for injected thread running in session
  525. // creating process' context to call
  526. // user32!UnregisterUserApiHook.
  527. //
  528. // History: 2000-11-11 vtan created
  529. // --------------------------------------------------------------------------
  530. DWORD CThemeServer::ThemeHooksRemove (void)
  531. {
  532. DWORD dwErrorCode;
  533. if (IsSystemProcessContext())
  534. {
  535. if (UnregisterUserApiHook() != FALSE)
  536. {
  537. dwErrorCode = ERROR_SUCCESS;
  538. Log(LOG_TMLOAD, L"UnregisterUserApiHook() called");
  539. }
  540. else
  541. {
  542. dwErrorCode = GetLastError();
  543. }
  544. }
  545. else
  546. {
  547. dwErrorCode = ERROR_ACCESS_DENIED;
  548. }
  549. return(dwErrorCode);
  550. }
  551. // --------------------------------------------------------------------------
  552. // CThemeServer::ClearStockObjects
  553. //
  554. // Arguments: HANDLE hSection
  555. //
  556. // Returns: DWORD
  557. //
  558. // Purpose: Thread entry point for injected thread running in session
  559. // creating process' context to clear stock objects in theme
  560. // section.
  561. //
  562. //
  563. // History: 2001-05-01 rfernand created
  564. // --------------------------------------------------------------------------
  565. DWORD CThemeServer::ClearStockObjects (HANDLE hSection)
  566. {
  567. DWORD dwErrorCode = ERROR_SUCCESS;
  568. if (IsSystemProcessContext())
  569. {
  570. if (hSection)
  571. {
  572. //---- Clearing the stock bitmaps in the section ----
  573. //---- is OK here since we are running in the context ----
  574. //---- of the current USER session ----
  575. HRESULT hr = ClearTheme(hSection, TRUE);
  576. if (FAILED(hr))
  577. {
  578. Log(LOG_ALWAYS, L"ClearTheme() failed, hr=0x%x", hr);
  579. hr = S_OK; // not a fatal error
  580. }
  581. }
  582. }
  583. else
  584. {
  585. dwErrorCode = ERROR_ACCESS_DENIED;
  586. }
  587. return(dwErrorCode);
  588. }
  589. // --------------------------------------------------------------------------
  590. // CThemeServer::LockAcquire
  591. //
  592. // Arguments: <none>
  593. //
  594. // Returns: <none>
  595. //
  596. // Purpose: Acquires the object critical section.
  597. //
  598. // History: 2000-11-11 vtan created
  599. // --------------------------------------------------------------------------
  600. void CThemeServer::LockAcquire (void)
  601. {
  602. EnterCriticalSection(&_lock);
  603. }
  604. // --------------------------------------------------------------------------
  605. // CThemeServer::LockRelease
  606. //
  607. // Arguments: <none>
  608. //
  609. // Returns: <none>
  610. //
  611. // Purpose: Releases the object critical section.
  612. //
  613. // History: 2000-11-11 vtan created
  614. // --------------------------------------------------------------------------
  615. void CThemeServer::LockRelease (void)
  616. {
  617. LeaveCriticalSection(&_lock);
  618. }
  619. // --------------------------------------------------------------------------
  620. // CThemeServer::InjectClientSessionThread
  621. //
  622. // Arguments: hProcess = Handle to process to inject thread in.
  623. // iIndexFunction = Function to call on injected thread.
  624. // pvParam = Ptr to param for entry function
  625. //
  626. // Returns: HRESULT
  627. //
  628. // Purpose: Create a user mode thread in the remote process (possibly
  629. // across sessions) and execute the entry point specified at
  630. // object construction which is valid in the remote process
  631. // context. Wait for the thread to finish. It will signal its
  632. // success of failure in the exit code.
  633. //
  634. // History: 2000-11-11 vtan created
  635. // 2001-05-18 vtan generic function index
  636. // --------------------------------------------------------------------------
  637. HRESULT CThemeServer::InjectClientSessionThread (HANDLE hProcess, int iIndexFunction, void *pvParam)
  638. {
  639. HRESULT hr;
  640. PUSER_THREAD_START_ROUTINE pfnThreadStart;
  641. switch (iIndexFunction)
  642. {
  643. case FunctionRegisterUserApiHook:
  644. pfnThreadStart = reinterpret_cast<PUSER_THREAD_START_ROUTINE>(_pfnRegister);
  645. break;
  646. case FunctionUnregisterUserApiHook:
  647. pfnThreadStart = reinterpret_cast<PUSER_THREAD_START_ROUTINE>(_pfnUnregister);
  648. break;
  649. case FunctionClearStockObjects:
  650. pfnThreadStart = reinterpret_cast<PUSER_THREAD_START_ROUTINE>(_pfnClearStockObjects);
  651. break;
  652. default:
  653. pfnThreadStart = NULL;
  654. break;
  655. }
  656. if (pfnThreadStart != NULL)
  657. {
  658. NTSTATUS status;
  659. HANDLE hThread;
  660. status = RtlCreateUserThread(hProcess,
  661. NULL,
  662. FALSE,
  663. 0,
  664. _dwStackSizeReserve,
  665. _dwStackSizeCommit,
  666. pfnThreadStart,
  667. pvParam,
  668. &hThread,
  669. NULL);
  670. if (NT_SUCCESS(status))
  671. {
  672. DWORD dwWaitResult, dwExitCode;
  673. dwWaitResult = WaitForSingleObject(hThread, INFINITE);
  674. if (GetExitCodeThread(hThread, &dwExitCode) != FALSE)
  675. {
  676. hr = HRESULT_FROM_WIN32(dwExitCode);
  677. }
  678. else
  679. {
  680. hr = E_FAIL;
  681. }
  682. TBOOL(CloseHandle(hThread));
  683. }
  684. else
  685. {
  686. hr = HRESULT_FROM_NT(status);
  687. }
  688. }
  689. else
  690. {
  691. hr = E_FAIL;
  692. }
  693. return(hr);
  694. }